-- | Startup up the frontend together with the server, which starts up clients.
module Game.LambdaHack.Client.UI.StartupFrontendClient
  ( srtFrontend
  ) where

import Control.Concurrent.Async
import qualified Control.Concurrent.STM as STM
import Control.Exception.Assert.Sugar

import Game.LambdaHack.Client.State
import Game.LambdaHack.Client.UI.Config
import Game.LambdaHack.Client.UI.Content.KeyKind
import Game.LambdaHack.Client.UI.Frontend
import Game.LambdaHack.Client.UI.KeyBindings
import Game.LambdaHack.Client.UI.MonadClientUI
import Game.LambdaHack.Common.ClientOptions
import Game.LambdaHack.Common.Faction
import qualified Game.LambdaHack.Common.Kind as Kind
import Game.LambdaHack.Common.Msg
import Game.LambdaHack.Common.State

-- | Wire together game content, the main loops of game clients,
-- the main game loop assigned to this frontend (possibly containing
-- the server loop, if the whole game runs in one process),
-- UI config and the definitions of game commands.
srtFrontend :: (DebugModeCli -> SessionUI -> State -> StateClient
                -> chanServerUI
                -> IO ())    -- ^ UI main loop
            -> (DebugModeCli -> SessionUI -> State -> StateClient
                -> chanServerAI
                -> IO ())    -- ^ AI main loop
            -> KeyKind       -- ^ key and command content
            -> Kind.COps     -- ^ game content
            -> DebugModeCli  -- ^ client debug parameters
            -> ((FactionId -> chanServerUI -> IO ())
               -> (FactionId -> chanServerAI -> IO ())
               -> IO ())     -- ^ frontend main loop
            -> IO ()
srtFrontend executorUI executorAI
            copsClient cops sdebugCli exeServer = do
  -- UI config reloaded at each client start.
  sconfig <- mkConfig cops
  let !sbinding = stdBinding copsClient sconfig  -- evaluate to check for errors
      sdebugMode = applyConfigToDebug sconfig sdebugCli cops
  defaultHist <- defaultHistory $ configHistoryMax sconfig
  let cli = defStateClient defaultHist emptyReport
      s = updateCOps (const cops) emptyState
      exeClientAI fid =
        let noSession = assert `failure` "AI client needs no UI session"
                               `twith` fid
        in executorAI sdebugMode noSession s (cli fid True)
      exeClientUI sescMVar loopFrontend fid chanServerUI = do
        responseF <- STM.newTQueueIO
        requestF <- STM.newTQueueIO
        let schanF = ChanFrontend{..}
        a <- async $ loopFrontend schanF
        link a
        executorUI sdebugMode SessionUI{..} s (cli fid False) chanServerUI
        STM.atomically $ STM.writeTQueue requestF FrontFinish
        wait a
  -- TODO: let each client start his own raw frontend (e.g., gtk, though
  -- that leads to disaster); then don't give server as the argument
  -- to startupF, but the Client.hs (when it ends, gtk ends); server is
  -- then forked separately and client doesn't need to know about
  -- starting servers.
  startupF sdebugMode $ \sescMVar loopFrontend ->
    exeServer (exeClientUI sescMVar loopFrontend) exeClientAI