
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module ServerMonad (
                    ServerMonad, evalServerMonad, mkServerState,
                    getUser,
                    getProtocolVersion,
                    getLastReadyTime, setLastReadyTime,
                    getUserInfo,
                    getDirectory, getNotifierVar,
                    -- XXX Don't really belong here:
                    Directory(..), MessagerRequest(..),
                    getConfig,
                    NVar,
                    CHVar, ConfigHandlerRequest(..),
                    TimeMasterVar,
                    baseDir,
                   ) where

import Builder.Config
import Builder.Handlelike
import Builder.Utils

import Control.Concurrent.MVar
import Control.Monad.State
import Data.Time.LocalTime

type NVar = MVar (User, BuildNum)

type CHVar = MVar ConfigHandlerRequest

type MessagerVar = MVar MessagerRequest

type TimeMasterVar = MVar (String, MVar LocalTime)

data ConfigHandlerRequest = ReloadConfig
                          | GiveMeConfig (MVar Config)

data MessagerRequest = Message Verbosity String
                     | Reopen

baseDir :: FilePath
baseDir = "data"

newtype ServerMonad a = ServerMonad (StateT ServerState IO a)
    deriving (Monad, MonadIO)

data ServerState = ServerState {
                       ss_handleOrSsl :: HandleOrSsl,
                       ss_user :: String,
                       ss_protocolVersion :: ProtocolVersion,
                       ss_directory :: Directory,
                       ss_last_ready_time :: TimeOfDay
                   }

mkServerState :: HandleOrSsl
              -> User
              -> ProtocolVersion
              -> Directory
              -> TimeOfDay
              -> ServerState
mkServerState h u pv directory lrt
    = ServerState {
          ss_handleOrSsl = h,
          ss_user = u,
          ss_protocolVersion = pv,
          ss_directory = directory,
          ss_last_ready_time = lrt
      }

evalServerMonad :: ServerMonad a -> ServerState -> IO a
evalServerMonad (ServerMonad m) cs = evalStateT m cs

getHandleOrSsl :: ServerMonad HandleOrSsl
getHandleOrSsl = do st <- ServerMonad get
                    return $ ss_handleOrSsl st

getUser :: ServerMonad String
getUser = do st <- ServerMonad get
             return $ ss_user st

getProtocolVersion :: ServerMonad ProtocolVersion
getProtocolVersion = do st <- ServerMonad get
                        return $ ss_protocolVersion st

getLastReadyTime :: ServerMonad TimeOfDay
getLastReadyTime = do st <- ServerMonad get
                      return $ ss_last_ready_time st

setLastReadyTime :: TimeOfDay -> ServerMonad ()
setLastReadyTime tod = do st <- ServerMonad get
                          ServerMonad $ put $ st { ss_last_ready_time = tod }

getUserInfo :: ServerMonad (Maybe UserInfo)
getUserInfo = do st <- ServerMonad get
                 config <- liftIO $ getConfig (ss_directory st)
                 return $ lookup (ss_user st) $ config_clients config

getDirectory :: ServerMonad Directory
getDirectory = do st <- ServerMonad get
                  return $ ss_directory st

getNotifierVar :: ServerMonad NVar
getNotifierVar = liftM dir_notifierVar $ getDirectory

instance HandlelikeM ServerMonad where
    hlPutStrLn str = do h <- getHandleOrSsl
                        liftIO $ hlPutStrLn' h str
    hlGetLine = do h <- getHandleOrSsl
                   liftIO $ hlGetLine' h
    hlGet n = do h <- getHandleOrSsl
                 liftIO $ hlGet' h n

data Directory = Directory {
                     dir_messagerVar :: MessagerVar,
                     dir_notifierVar :: NVar,
                     dir_configHandlerVar :: CHVar,
                     dir_timeMasterVar :: TimeMasterVar
                 }

getConfig :: Directory -> IO Config
getConfig directory
 = do mv <- newEmptyMVar
      putMVar (dir_configHandlerVar directory) (GiveMeConfig mv)
      takeMVar mv

