{-# LANGUAGE OverloadedLabels #-}

module Octane.Utility.Generator
  ( generateStream
  ) where

import Data.Function ((&))

import qualified Data.Binary.Bits as BinaryBit
import qualified Data.Binary.Bits.Put as BinaryBit
import qualified Data.Binary.Put as Binary
import qualified Octane.Type.Boolean as Boolean
import qualified Octane.Type.CacheItem as CacheItem
import qualified Octane.Type.ClassItem as ClassItem
import qualified Octane.Type.Frame as Frame
import qualified Octane.Type.Initialization as Initialization
import qualified Octane.Type.List as List
import qualified Octane.Type.Replication as Replication
import qualified Octane.Type.State as State
import qualified Octane.Type.Stream as Stream
import qualified Octane.Type.Text as Text

-- | Generates a network stream.
generateStream
  :: [Frame.Frame]
  -> List.List Text.Text
  -> List.List Text.Text
  -> List.List ClassItem.ClassItem
  -> List.List CacheItem.CacheItem
  -> Stream.Stream
generateStream frames _objects _names _classes _cache = do
  let bitPut = putFrames frames
  let bytePut = BinaryBit.runBitPut bitPut
  let bytes = Binary.runPut bytePut
  Stream.Stream bytes

putFrames :: [Frame.Frame] -> BinaryBit.BitPut ()
putFrames frames = do
  case frames of
    [] -> pure ()
    frame:rest -> do
      putFrame frame
      putFrames rest

putFrame :: Frame.Frame -> BinaryBit.BitPut ()
putFrame frame = do
  frame & #time & BinaryBit.putBits 32
  frame & #delta & BinaryBit.putBits 32
  frame & #replications & putReplications

putReplications :: [Replication.Replication] -> BinaryBit.BitPut ()
putReplications replications = do
  case replications of
    [] -> do
      False & Boolean.Boolean & BinaryBit.putBits 1
    replication:rest -> do
      True & Boolean.Boolean & BinaryBit.putBits 1
      putReplication replication
      putReplications rest

putReplication :: Replication.Replication -> BinaryBit.BitPut ()
putReplication replication = do
  replication & #actorId & BinaryBit.putBits 0
  case #state replication of
    State.SOpening -> putNewReplication replication
    State.SExisting -> putExistingReplication replication
    State.SClosing -> putClosedReplication replication

putNewReplication :: Replication.Replication -> BinaryBit.BitPut ()
putNewReplication replication = do
  True & Boolean.Boolean & BinaryBit.putBits 1 -- open
  True & Boolean.Boolean & BinaryBit.putBits 1 -- new
  False & Boolean.Boolean & BinaryBit.putBits 1 -- unknown
  pure () -- TODO: convert object name into ID and put it
  case #initialization replication of
    Nothing -> pure ()
    Just x -> Initialization.putInitialization x

putExistingReplication :: Replication.Replication -> BinaryBit.BitPut ()
putExistingReplication _replication = do
  True & Boolean.Boolean & BinaryBit.putBits 1 -- open
  False & Boolean.Boolean & BinaryBit.putBits 1 -- existing
  pure () -- TODO: put props

putClosedReplication :: Replication.Replication -> BinaryBit.BitPut ()
putClosedReplication _replication = do
  False & Boolean.Boolean & BinaryBit.putBits 1 -- closed