{-# OPTIONS_GHC -fno-warn-orphans #-}
{-|
Module      : Game.GoreAndAsh.Actor.State
Description : State of actor core module
Copyright   : (c) Anton Gushcha, 2015-2016
License     : BSD3
Maintainer  : ncrashed@gmail.com
Stability   : experimental
Portability : POSIX

Internal state of actor core module.
-}
module Game.GoreAndAsh.Actor.State(
    ActorState(..)
  , emptyActorState
  , moveSendedMessages
  ) where

import Control.DeepSeq 
import Data.Dynamic
import GHC.Generics (Generic)
import qualified Data.HashMap.Strict as H 
import qualified Data.Sequence as S 

import Game.GoreAndAsh.Actor.TypeRep

-- | Inner state of actor module.
--
-- [@s@] - State of next module, the states are chained via nesting.
data ActorState s = ActorState {
  -- | Stores messages for actor with specified id
  --
  -- Message has type of Dynamic as message manager doesn't know anything about message types.
  -- We don't need to serialization protocol due passing via memory. Type safety is forced 
  -- with Messagable type class with type family (see Actor.Message module). Id space is separate for each actor type
  --
  -- There are two sequences of messages, one for recieved messages from previous frame, and
  -- the second one for messages recieved at current frame. At the end of each frame first
  -- sequence is purged and filled with contents of first and the second one is replaced with
  -- empty sequence.
  actorBoxes :: !(H.HashMap (HashableTypeRep, Int) (S.Seq Dynamic, S.Seq Dynamic))
  -- | Next empty id of actor, id space is separate for each actor type
, actorNextId :: !(H.HashMap HashableTypeRep Int)
  -- | Search table for actor names
, actorNameMap :: !(H.HashMap String HashableTypeRep)
  -- | Next state in state chain of modules
, actorNextState :: !s
} deriving (Generic)

instance NFData Dynamic where 
  rnf = (`seq` ())

instance NFData s => NFData (ActorState s)

-- | Create empty actor state
emptyActorState :: s -> ActorState s 
emptyActorState s = ActorState {
    actorBoxes = H.empty
  , actorNextId = H.empty
  , actorNameMap = H.empty
  , actorNextState = s
  }

-- | Perform rotation between sended messages and recieved ones
moveSendedMessages :: ActorState s -> ActorState s 
moveSendedMessages s = s {
    actorBoxes = fmap (\(_, b) -> (b, S.empty)) . actorBoxes $! s
  }