module TheatreDev.Terminal.StatefulActorSpec where

import TheatreDev.Prelude

data StatefulActorSpec message = forall state.
  StatefulActorSpec
  { enter :: Concurrently state,
    step :: state -> NonEmpty message -> Concurrently state,
    exit :: state -> Concurrently ()
  }

instance Semigroup (StatefulActorSpec message) where
  StatefulActorSpec leftEnter leftStep leftExit <> StatefulActorSpec rightEnter rightStep rightExit =
    StatefulActorSpec
      { enter =
          (,) <$> leftEnter <*> rightEnter,
        step = \(leftState, rightState) messages ->
          (,)
            <$> leftStep leftState messages
            <*> rightStep rightState messages,
        exit = \(leftState, rightState) ->
          leftExit leftState *> rightExit rightState
      }

instance Monoid (StatefulActorSpec message) where
  mempty =
    StatefulActorSpec
      { enter = pure (),
        step = const $ const $ pure (),
        exit = const $ pure ()
      }

individual :: IO state -> (state -> message -> IO state) -> (state -> IO ()) -> StatefulActorSpec message
individual enter step exit =
  StatefulActorSpec
    { enter = Concurrently enter,
      step = \state messages -> Concurrently (foldM step state messages),
      exit = \state -> Concurrently (exit state)
    }