{-# OPTIONS_HADDOCK hide #-}

{-# LANGUAGE NoImplicitPrelude #-}

module Imj.Graphics.Animation.Design.Render
    ( renderAnim
    ) where


import           Imj.Prelude

import           Control.Monad.IO.Class(MonadIO)
import           Control.Monad.Reader.Class(MonadReader)

import           Data.Either(partitionEithers)

import           Imj.Geo.Discrete
import           Imj.Graphics.Animation.Design.Types
import           Imj.Graphics.Animation.Design.Color
import           Imj.Graphics.Render
import           Imj.Iteration

{- | If an 'AnimatedPoint' has no specific 'Char' to be rendered with,
it will be rendered with the 'Char' of the 'Animation'.

Hence, if neither 'AnimatedPoint' nor 'Animation' contain a 'Char', this function
errors.-}
{-# INLINABLE renderAnim #-}
renderAnim :: (Draw e, MonadReader e m, MonadIO m)
           => Animation
           -> Coords Pos
           -- ^ Reference coordinates.
           -> m ()
renderAnim (Animation points _ interaction (UpdateSpec _ (Iteration _ frameForNextUpdate)) mayChar) =
  render' frameForNextUpdate mayChar points interaction

{-# INLINABLE render' #-}
render' :: (Draw e, MonadReader e m, MonadIO m)
        => Frame
        -> Maybe Char
        -- ^ Default char to use when there is no char specified in the state
        -> AnimatedPoints
        -> (Coords Pos -> InteractionResult)
        -> Coords Pos
        -> m ()
render' _ _ (AnimatedPoints Nothing _ _) _ _   = return ()
render' _ _ (AnimatedPoints (Just []) _ _) _ _ = return ()
render'
 parentFrame mayCharAnim (AnimatedPoints (Just branches) _ childFrame) interaction r = do
  let (children, aliveCoordinates) = partitionEithers branches
      selectRenderedCoordinates =
        filter (\(AnimatedPoint canInteract coords _) ->
                    case canInteract of
                      -- An alive animated point may collide:
                      DontInteract -> interaction coords == Stable
                      -- We make the assumption that every alive point is guaranteed to be collision-free.
                      -- Note that when the environment will be dynamic, it will be wrong:
                      Interact -> True)
      relFrame = parentFrame - childFrame
      color = colorFromFrame relFrame
  mapM_ (\(AnimatedPoint _ c mayChar) -> do
            let char = fromMaybe (error "no char was specified") $ mayChar <|> mayCharAnim
            drawChar char (sumCoords c r) color)
        $ selectRenderedCoordinates aliveCoordinates
  mapM_ (\child -> render' relFrame mayCharAnim child interaction r) children