{-# OPTIONS_HADDOCK hide #-}

{-# LANGUAGE NoImplicitPrelude #-}

module Imj.Graphics.Animation.Design.Types
          ( Animation(..)
          , UpdateSpec(..)
          , AnimatedPoints(..)
          , AnimatedPoint(..)
          , CanInteract(..)
          , InteractionResult(..)
          ) where

import           Imj.Prelude

import           GHC.Show(showString)

import           Imj.Geo.Discrete
import           Imj.Iteration
import           Imj.Timing


data Animation = Animation {
    _animationPoints :: !AnimatedPoints
    -- ^ The current points.
  , _animationUpdate :: !((Coords Pos -> InteractionResult)
                      -> Frame
                      -> AnimatedPoints
                      -> AnimatedPoints)
    -- ^ The function updating 'AnimatedPoints'.
  , _animationInteraction :: !(Coords Pos -> InteractionResult)
    -- ^ The environment interaction function.
  , _animationNextUpdateSpec :: !UpdateSpec
    -- ^ The time and iteration of the next update
  , _animationChar :: !(Maybe Char)
    -- ^ The char used to draw animated points when the 'AnimatedPoints'
    -- don't specify one. If 'Nothing', the animation function /must/ specify one
    -- when creating new 'AnimatedPoint's.
}

instance Show Animation where
  showsPrec _ (Animation a _ _ b c) =
    showString $ "Animation{" ++ show (a,b,c) ++ "}"


data UpdateSpec = UpdateSpec {
    _updateSpecTime :: !KeyTime
    -- ^ The time at which the update should happen.
  , _updateSpecIteration :: !Iteration
    -- ^ The iteration that will be used in the update.
} deriving(Show)


data AnimatedPoints = AnimatedPoints {
    _animatedPointsBranches :: !(Maybe [Either AnimatedPoints AnimatedPoint])
    -- ^ The children. A child can be 'Right' 'AnimatedPoint' or 'Left' 'AnimatedPoints'.
    --
    --  When a 'Right' 'AnimatedPoint' mutates, it is converted to an empty 'Left' 'AnimatedPoints'
  , _animatedPointsCenter :: !(Coords Pos)
    -- ^ The center, aka the coordinates of the 'AnimatedPoint', w.r.t the animation reference frame,
    -- that gave birth to this 'AnimatedPoints'.
  , _animatedPointsFrame :: !Frame
    -- ^ The frame at which this 'AnimatedPoints' was created, relatively to the parent, if any.
} deriving (Show)

data AnimatedPoint = AnimatedPoint {
    _animatedPointCanInteract :: !CanInteract
    -- ^ Can the point interact with the environment?
  , _animatedPointCoords :: {-# UNPACK #-} !(Coords Pos)
    -- ^ Its location, w.r.t the animation reference frame.
  , _animatedPointRenderedWith :: !(Maybe Char)
    -- ^ The char used to render it. If 'Nothing', 'Animation' /must/ specify a 'Char'.
} deriving (Show)

data CanInteract = DontInteract
                 -- ^ The 'AnimatedPoint' can't interact with the environment.
                 --
                 -- To ensure that the 'Animation' is finite in time,
                 -- animation functions returning 'AnimatedPoint's that
                 -- 'DontInteract' should return an empty list of 'AnimatedPoint's
                 -- for each 'Frame' after a given 'Frame'.
                 | Interact
                 -- ^ The 'AnimatedPoint' can be mutated after an interaction
                 -- with the environment.
                 --
                 -- For the animation to be finite in time, 'AnimatedPoint's that
                 -- 'Interact' /must/ eventually be mutated by the environment.
                 --
                 -- Hence, assuming the environment has a finite size,
                 -- animation functions returning 'AnimatedPoint's that
                 -- 'Interact' can guarantee animation finitude by computing their
                 -- coordinates using functions diverging in the 'Frame' argument.
                 deriving (Show, Eq)

data InteractionResult = Mutation
                       -- ^ The 'AnimatedPoint' can mutate.
                       | Stable
                       -- ^ The 'AnimatedPoint' doesn't mutate.
                       deriving(Eq,Show)