{-|
Module      : Game.GoreAndAsh.Sync.State
Description : Internal state of core module and message typeclass
Copyright   : (c) Anton Gushcha, 2015-2016
License     : BSD3
Maintainer  : ncrashed@gmail.com
Stability   : experimental
Portability : POSIX
-}
module Game.GoreAndAsh.Sync.State(
    SyncState(..)
  , SyncRole(..)
  , NetworkMessage(..)
  , SyncServiceMsg(..)
  , emptySyncState
  ) where

import Control.DeepSeq
import Data.Serialize 
import Data.Word 
import GHC.Generics
import qualified Data.HashMap.Strict as H 
import qualified Data.Sequence as S 

import Game.GoreAndAsh.Actor
import Game.GoreAndAsh.Actor.TypeRep
import Game.GoreAndAsh.Network 

-- | Defines behavior in synchronization for actor ids
data SyncRole = 
    SyncMaster -- ^ Registers types of actors
  | SyncSlave -- ^ Always ask for ids from other nodes
  deriving (Generic, Eq, Show, Enum)

instance NFData SyncRole 

-- | Extension for actor message, messages that are sent to remote host
class ActorMessage i => NetworkMessage i where
  -- | Corresponding message payload for @i@ identifier, usually ADT
  type NetworkMessageType i :: *

-- | Internal service message for synchronizing ids of actors
data SyncServiceMsg = 
  -- | Request id of actor with specified name
    SyncServiceRequestId !String
  -- | Response with actor id and name
  | SyncServiceResponseId !String !Word64
  -- | Response that given actor is not found
  | SyncServiceResponseNotRegistered !String
  deriving (Generic)

instance Serialize SyncServiceMsg

-- | Inner state of sync module
--
-- [@s@] - State of next module, the states are chained via nesting.
data SyncState s = SyncState {
  -- | Next module in chain
  syncNextState :: !s
  -- | Mapping from type representation to id
, syncIdMap :: !(H.HashMap HashableTypeRep Word64)
  -- | Reverse mapping from id to type representation
, syncIdMapRev :: !(H.HashMap Word64 HashableTypeRep)
  -- | Next empty id for registering, value zero is service value
, syncNextId :: !Word64
  -- | Messages that are waiting resolving of network id of actor
  -- Actor name and actor local id is stored with the message to sent
, syncScheduledMessages :: !(H.HashMap Peer (S.Seq (String, ChannelID, Word64 -> Message)))
  -- | Flag that enables detailed logging
, syncLogging :: !Bool
  -- | Current model of synchronization
, syncRole :: !SyncRole
} deriving (Generic)

instance NFData s => NFData (SyncState s)

-- | Make empty sync state
emptySyncState :: s -> SyncState s 
emptySyncState s = SyncState {
    syncNextState = s
  , syncIdMap = H.empty 
  , syncIdMapRev = H.empty
  , syncNextId = 1
  , syncScheduledMessages = H.empty
  , syncLogging = False
  , syncRole = SyncMaster
  }