{-# OPTIONS_HADDOCK hide #-}

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE LambdaCase #-}

module Imj.Game.Hamazed.World.Types
        ( World(..)
        , WallDistribution(..)
        , WorldShape(..)
        , BattleShip(..)
        , Number(..)
        , Boundaries(..)
        , mkWorldContainer
        , InTerminal(..)
        , environmentInteraction
        -- * Reexports
        , module Imj.Iteration
        , module Imj.Graphics.Text.Animation
        , module Imj.Physics.Discrete.Types
        , Terminal.Window
        , RectContainer(..)
        ) where

import           Imj.Prelude

import qualified System.Console.Terminal.Size as Terminal( Window(..))

import           Imj.Game.Hamazed.World.Space.Types
import           Imj.Game.Hamazed.World.Space
import           Imj.Geo.Discrete
import           Imj.Graphics.Animation.Design.Types
import           Imj.Graphics.Text.Animation
import           Imj.Graphics.UI.RectContainer
import           Imj.Iteration
import           Imj.Physics.Discrete.Types
import           Imj.Physics.Discrete
import           Imj.Timing


data WorldShape = Square
                -- ^ Width = Height
                | Rectangle2x1
                -- ^ Width = 2 * Height

-- | How should walls be created?
data WallDistribution = None
              -- ^ No 'Wall's.
              | Deterministic
              -- ^ A Rectangular 'Wall' in the middle of the level.
              | Random !RandomParameters
              -- ^ 'Wall's are created with an algorithm involving random numbers.

-- | Helper function to create a 'RectContainer' containing a 'World'.
mkWorldContainer :: World -> RectContainer
mkWorldContainer (World _ _ (Space _ sz _) _ (InTerminal _ upperLeft)) =
  RectContainer sz upperLeft

data World = World {
    _worldNumbers :: ![Number]
    -- ^ The remaining 'Number's (shot 'Number's are removed from the list)
  , _worldShip :: !BattleShip
    -- ^ The player's 'BattleShip'
  , _worldSpace :: !Space
    -- ^ The 'Space' in which 'BattleShip' and 'Number's evolve
  , _worldAnimations :: ![Animation]
    -- ^ Visual animations. They don't have an influence on the game, they are just here
    -- for aesthetics.
  , _worldEmbedded :: !InTerminal
    -- ^ To know where we should draw the 'World' from, w.r.t terminal frame.
}

data InTerminal = InTerminal {
    _inTerminalSize :: !(Maybe (Terminal.Window Int))
    -- ^ The size of the terminal window
  , _inTerminalUpperLeft :: !(Coords Pos)
    -- ^ The 'World' 's 'RectContainer' upper left coordinates,
    -- w.r.t terminal frame.
} deriving (Show)

data BattleShip = BattleShip {
    _shipPosSpeed :: !PosSpeed
  -- ^ Discrete position and speed.
  , _shipAmmo :: !Int
  -- ^ How many laser shots are left.
  , _shipSafeUntil :: !(Maybe SystemTime)
  -- ^ At the beginning of each level, the ship is immune to collisions with 'Number's
  -- for a given time. This field holds the time at which the immunity ends.
  , _shipCollisions :: ![Number]
  -- ^ Which 'Number's are currently colliding with the 'BattleShip'.
} deriving(Show)


data Number = Number {
    _numberPosSpeed :: !PosSpeed
  -- ^ Discrete position and speed.
  , _numberNum :: !Int
  -- ^ Which number it represents (1 to 16).
} deriving(Eq, Show)

-- | An interaction function taking into account a 'World' and 'Boundaries'
environmentInteraction :: World -> Boundaries -> Coords Pos -> InteractionResult
environmentInteraction (World _ _ space _ (InTerminal mayTermWindow upperLeft)) scope =
  let worldCorner = translate' 1 1 upperLeft
  in scopedLocation space mayTermWindow worldCorner scope >>> \case
    InsideWorld  -> Stable
    OutsideWorld -> Mutation