packages feed

diplomacy-server-0.1.0.0: Resources/Resolution.hs

{-|
Module      : Resources.Resolution
Description : 
Copyright   : (c) Alexander Vieth, 2015
Licence     : BSD3
Maintainer  : aovieth@gmail.com
Stability   : experimental
Portability : non-portable (GHC only)
-}

{-# LANGUAGE AutoDeriveTypeable #-}

module Resources.Resolution (

      resource

    ) where

import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Except
import Rest
import Rest.Resource as R
import Rest.Dictionary.Types
import Types.Server
import Types.GameState
import Types.GameId
import Types.Credentials
import Diplomacy.Game
import Diplomacy.Turn

resource :: Resource (ReaderT GameId Server) (ReaderT GameId Server) () Void Void
resource = mkResourceId
    { R.name = "resolution"
    , R.schema = singleton () $ unnamedSingle (const ())
    , R.update = Just get
    }
  where
    get :: Handler (ReaderT GameId Server)
    get = secureHandler $ mkHandler (addPar roundParam . mkPar turnParam . jsonO . jsonE . jsonI) handler

    turnParam :: Param (Maybe Turn)
    turnParam = Param ["turn"] $ \xs -> case xs of
        (Just x : _) -> case reads x :: [(Int, String)] of
            [(i, [])] -> case turnFromInt i of
                Just t -> Right (Just t)
                _ -> Left (ParseError "Could not parse turn")
            _ -> Left (ParseError "Could not parse turn")
        [Nothing] -> Right Nothing
        _ -> Left (ParseError "Could not parse turn")

    roundParam :: Param (Maybe Round)
    roundParam = Param ["round"] $ \xs -> case xs of
        (Just x : _) -> case reads x :: [(Int, String)] of
            [(i, [])] -> let min = minBound :: Round
                             max = maxBound :: Round
                         in  if i >= fromEnum min && i <= fromEnum max
                             then Right (Just (toEnum i))
                             else Left (ParseError "Could not parse round")
            _ -> Left (ParseError "Could not parse round")
        [Nothing] -> Right Nothing
        _ -> Left (ParseError "Could not parse round")


    handler :: Env h (Maybe Round, Maybe Turn) Credentials -> ExceptT (Reason Void) (ReaderT GameId Server) (Maybe GameResolution)
    handler env =
        let credentials = input env
            (maybeRound, maybeTurn) = param env
        in  lift ask >>= \gameId -> doGet credentials gameId maybeTurn maybeRound

    doGet
        :: Credentials
        -> GameId
        -> Maybe Turn
        -> Maybe Round
        -> ExceptT (Reason Void) (ReaderT GameId Server) (Maybe GameResolution)
    doGet credentials gameId maybeTurn maybeRound = withUserCredentialsForGame credentials gameId f
      where
        f gameStateView = return $ do
            metadata <- gameStateViewMetadata gameStateView
            let turn' = maybe (metadataTurn metadata) id maybeTurn
            -- Prevent giving the resolution of the latest (currently ongoing)
            -- round! This would allow any player to see the orders of other
            -- players.
            let (turn, round) = case maybeRound of
                    Just x -> (turn', x)
                    Nothing -> if metadataRound metadata /= RoundOne
                               then (turn', prevRound (metadataRound metadata))
                               else case prevTurn turn' of
                                   -- In this case we're at turn 0 round 0; we
                                   -- give the current turn and round and the
                                   -- upcoming guard will fail.
                                   Nothing -> (turn', metadataRound metadata)
                                   Just t -> (t, prevRound (metadataRound metadata))
            guard (turn <= metadataTurn metadata)
            guard (turn < metadataTurn metadata || round < metadataRound metadata)
            gameStateViewResolution turn round gameStateView