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

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

module BattlePlace.WebApi
	( WebApi
	, ClientAuthRequest(..)
	, ClientAuthResponse(..)
	, MatchRequest(..)
	, MatchResponse(..)
	, MatchStatusResponse(..)
	, MatchStatusReason(..)
	, MatchTeam(..)
	, MatchPlayer(..)
	, MatchServer(..)
	, MatchServerInfo(..)
	, 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.Types

type WebApi = "v1a" :>
	(    "client" :>
		(    "auth" :> ReqBody '[JSON] ClientAuthRequest :> Post '[JSON] ClientAuthResponse
		:<|> "match" :>
			(    ReqBody '[JSON] MatchRequest :> Post '[JSON] MatchResponse
			:<|> Capture "matchToken" (InternalToken MatchToken) :> Get '[JSON] MatchStatusResponse
			)
		:<|> "session" :> Capture "sessionToken" (InternalToken SessionToken) :>
			(    "result" :> ReqBody '[JSON] SessionResultRequest :> Post '[JSON] ()
			)
		)
	:<|> "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_clientToken :: !(InternalToken ClientToken)
	, matchRequest_teamSizes :: !(VU.Vector MatchTeamSize)
	, matchRequest_maxMatchTime :: {-# UNPACK #-} !Int
	, matchRequest_matchTag :: !(Maybe MatchTag)
	, 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 :: !MatchStatusReason
		}
	-- | 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

-- | Reason of match failure.
data MatchStatusReason
	-- | Failed to make a match in a specified time.
	= MatchStatusReason_timedOut
	-- | Match was made, but no server is available (and use of server is mandatory).
	| MatchStatusReason_noServer
	-- | Matching was explicitly cancelled by user.
	| MatchStatusReason_cancelled
	deriving Generic
instance J.FromJSON MatchStatusReason where
	parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON MatchStatusReason 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

newtype MatchServerInfo = MatchServerInfo T.Text deriving (J.FromJSON, J.ToJSON)

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_maxSessionsCount :: {-# UNPACK #-} !Int
	, serverMatchRequest_name :: !T.Text
	, serverMatchRequest_info :: !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)
	} deriving Generic
instance J.FromJSON ServerSession where
	parseJSON = J.genericParseJSON jsonOptions
instance J.ToJSON ServerSession where
	toJSON = J.genericToJSON jsonOptions
	toEncoding = J.genericToEncoding jsonOptions