content: module Server.CommandController
    ( CommandController,
      new,
      take,
      release,
      put,
    )
  where
  
  import Agda.Interaction.Base (IOTCM)
  import Control.Concurrent
  import Control.Concurrent.SizedChan
  import Control.Monad (forM_)
  import Prelude hiding (take)
  
  data CommandController
    = CommandController
        -- | Unbounded Command queue
        (SizedChan IOTCM)
        -- | MVar for the Command consumer
        (MVar IOTCM)
  
  new :: IO CommandController
  new = CommandController <$> newSizedChan <*> newEmptyMVar
  
  -- | Blocks if the front is empty
  take :: CommandController -> IO IOTCM
  take (CommandController _ front) = takeMVar front
  
  -- | Move the payload from the queue to the front
  -- Does not block if the front or the queue is empty
  release :: CommandController -> IO ()
  release (CommandController queue front) = do
    result <- tryReadSizedChan queue
    forM_ result (tryPutMVar front)
  
  -- | Does not block
  -- Move the payload to the front if the front is empty
  put :: CommandController -> IOTCM -> IO ()
  put (CommandController queue front) command = do
    isEmpty <- isEmptyMVar front
    if isEmpty
      then putMVar front command
      else writeSizedChan queue command