{-|
Module: BattlePlace.WebApi
Description: Web API definitions.
License: MIT
-}

{-# LANGUAGE DataKinds, DeriveGeneric, GeneralizedNewtypeDeriving, TypeOperators #-}

module BattlePlace.WebApi
        ( WebApi
        , ClientAuthRequest(..)
        , ClientAuthResponse(..)
        , MatchRequest(..)
        , MatchResponse(..)
        , MatchStatusResponse(..)
        , MatchCancelResponse(..)
        , MatchTeam(..)
        , MatchPlayer(..)
        , MatchServer(..)
        , SessionResultRequest(..)
        , ServerMatchRequest(..)
        , ServerMatchResponse(..)
        , ServerSessionResultRequest(..)
        , ServerSession(..)
        ) where

import qualified Data.Aeson as J
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import GHC.Generics(Generic)
import Servant.API

import BattlePlace.Token.Types
import BattlePlace.Util
import BattlePlace.WebApi.Auth
import BattlePlace.WebApi.Types

type WebApi = "v1a" :>
        (    "client" :>
                (    "auth" :> ReqBody '[JSON] ClientAuthRequest :> Post '[JSON] ClientAuthResponse
                :<|> "match" :>
                        (    AuthProtect ClientToken :> ReqBody '[JSON] MatchRequest :> Post '[JSON] MatchResponse
                        :<|> AuthProtect ClientToken :> Capture "matchToken" (InternalToken MatchToken) :> Get '[JSON] MatchStatusResponse
                        :<|> AuthProtect ClientToken :> Capture "matchToken" (InternalToken MatchToken) :> Delete '[JSON] MatchCancelResponse
                        )
                :<|> "session" :> Capture "sessionToken" (InternalToken SessionToken) :>
                        (    "result" :> AuthProtect ClientToken :> ReqBody '[JSON] SessionResultRequest :> Post '[JSON] ()
                        )
                :<|> "info" :>
                        (    "stats" :> AuthProtect ClientToken :> Get '[JSON] UserStats
                        )
                )
        :<|> "server" :>
                (    "match" :> ReqBody '[JSON] ServerMatchRequest :> Post '[JSON] ServerMatchResponse
                :<|> "session" :> Capture "serverSessionToken" (InternalToken ServerSessionToken) :>
                        (    "result" :> ReqBody '[JSON] ServerSessionResultRequest :> Post '[JSON] ()
                        )
                )
        )

data ClientAuthRequest = ClientAuthRequest
        { clientAuthRequest_projectId :: {-# UNPACK #-} !ProjectId
        , clientAuthRequest_auth :: !Auth
        } deriving Generic
instance J.FromJSON ClientAuthRequest where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ClientAuthRequest where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data ClientAuthResponse
        = ClientAuthResponse_authenticated
                { clientAuthResponse_clientToken :: !(InternalToken ClientToken)
                , clientAuthResponse_name :: !T.Text
                , clientAuthResponse_pictureUrl :: !T.Text
                }
        | ClientAuthResponse_notAuthenticated
                { clientAuthResponse_error :: !T.Text
                }
        deriving Generic
instance J.FromJSON ClientAuthResponse where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ClientAuthResponse where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data MatchRequest = MatchRequest
        { matchRequest_teamSizes :: !(VU.Vector MatchTeamSize)
        , matchRequest_maxMatchTime :: {-# UNPACK #-} !Int
        , matchRequest_matchTag :: !(Maybe MatchTag)
        , matchRequest_serverTag :: !(Maybe ServerTag)
        , matchRequest_info :: !(Maybe MatchPlayerInfo)
        } deriving Generic
instance J.FromJSON MatchRequest where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchRequest where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data MatchResponse = MatchResponse
        { matchResponse_matchToken :: !(InternalToken MatchToken)
        } deriving Generic
instance J.FromJSON MatchResponse where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchResponse where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data MatchStatusResponse
        = MatchStatusResponse_notFound
        | MatchStatusResponse_inProgress
        | MatchStatusResponse_matched
                { matchStatusResponse_sessionId :: !SessionId
                , matchStatusResponse_sessionToken :: !(InternalToken SessionToken)
                , matchStatusResponse_teams :: !(V.Vector MatchTeam)
                , matchStatusResponse_teamIndex :: {-# UNPACK #-} !Int
                , matchStatusResponse_mateIndex :: {-# UNPACK #-} !Int
                , matchStatusResponse_server :: !(Maybe MatchServer)
                }
        | MatchStatusResponse_failed
                { matchStatusResponse_reason :: !MatchFailureReason
                }
        -- | Match status was cleaned.
        -- Normallly this status should not be visible to clients, it's here just in case.
        | MatchStatusResponse_cleaned
        deriving Generic
instance J.FromJSON MatchStatusResponse where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchStatusResponse where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

newtype MatchTeam = MatchTeam (V.Vector MatchPlayer) deriving (J.FromJSON, J.ToJSON)

data MatchPlayer = MatchPlayer
        { matchPlayer_info :: !MatchPlayerInfo
        , matchPlayer_ourTicket :: !(Maybe Ticket)
        , matchPlayer_theirTicket :: !(Maybe Ticket)
        } deriving Generic
instance J.FromJSON MatchPlayer where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchPlayer where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data MatchServer = MatchServer
        { matchServer_info :: !MatchServerInfo
        , matchServer_ourTicket :: !Ticket
        , matchServer_theirTicket :: !Ticket
        } deriving Generic
instance J.FromJSON MatchServer where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchServer where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data MatchCancelResponse
        = MatchCancelResponse_notFound
        | MatchCancelResponse_cancelled
        deriving Generic
instance J.FromJSON MatchCancelResponse where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchCancelResponse where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data SessionResultRequest
        = SessionResultRequest_finished
                { sessionResultRequest_ranks :: !(V.Vector Int)
                }
        | SessionResultRequest_cancelled
        deriving Generic
instance J.FromJSON SessionResultRequest where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON SessionResultRequest where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data ServerMatchRequest = ServerMatchRequest
        { serverMatchRequest_projectId :: {-# UNPACK #-} !ProjectId
        , serverMatchRequest_projectServerToken :: !ProjectServerToken
        , serverMatchRequest_serverTag :: !(Maybe ServerTag)
        , serverMatchRequest_maxSessionsCount :: {-# UNPACK #-} !Int
        , serverMatchRequest_name :: !T.Text
        , serverMatchRequest_info :: !(Maybe MatchServerInfo)
        , serverMatchRequest_timeout :: !(Maybe Int)
        } deriving Generic
instance J.FromJSON ServerMatchRequest where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ServerMatchRequest where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data ServerMatchResponse = ServerMatchResponse
        { serverMatchResponse_sessions :: !(V.Vector ServerSession)
        } deriving Generic
instance J.FromJSON ServerMatchResponse where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ServerMatchResponse where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data ServerSessionResultRequest
        = ServerSessionResultRequest_finished
                { serverSessionResultRequest_ranks :: !(V.Vector Int)
                }
        | ServerSessionResultRequest_cancelled
        deriving Generic
instance J.FromJSON ServerSessionResultRequest where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ServerSessionResultRequest where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions

data ServerSession = ServerSession
        { serverSession_sessionId :: !SessionId
        , serverSession_serverSessionToken :: !(InternalToken ServerSessionToken)
        , serverSession_teams :: !(V.Vector MatchTeam)
        , serverSession_matchTag :: !MatchTag
        , serverSession_serverTag :: !ServerTag
        } deriving Generic
instance J.FromJSON ServerSession where
        parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ServerSession where
        toJSON = J.genericToJSON jsonOptions
        toEncoding = J.genericToEncoding jsonOptions