module Sim
  ( runSim
  ) where

import           Import

import           RIO.ByteString
import           RIO.State

import qualified Data.Serialize                  as S

import           CellularFluid

-- | The main function of simulation
runSim :: RIO SimApp ()
runSim = do
  env <- ask
  logDebug "Checking grid sanity..."
  when (not . gridIsSane $ simGrid env) $ do
    logError "Grid insane"
    exitFailure
  logDebug "Done checking grid sanity. Grid is sane."
  logDebug "Start simulation."
  simLoop $ simSteps env
  logDebug "End simulation."
  logDebug "Waiting for output to finish..."
  hClose $ env ^. outHandleL
  logDebug "Done waiting for output to finish."

-- | Main simulation loop
simLoop :: Int -> RIO SimApp Grid
simLoop nmax = (simGrid <$> ask) >>= evalStateT (go 0)
  where
    go n'
      | n' >= nmax = get
      | otherwise = do
        when (n' `mod` 100 == 0) $
          lift $ logDebug . fromString $ "Iteration: " ++ show n'
        stepSim
        go (n' + 1)

-- | Steps grid once and outputs
stepSim :: HasSimInfo env => StateT Grid (RIO env) ()
stepSim = do
  env <- lift ask
  grid <- get
  let cfg = env ^. simCfgL
      h = env ^. outHandleL
  let grid' = stepGrid cfg grid
    -- Writes output
  hPut h (S.encode grid')
  put grid'
