{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE DuplicateRecordFields  #-}
{-# LANGUAGE FlexibleInstances  #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

module Play.Engine.Sprite where

import qualified SDL

import Data.Word (Word8)
import Play.Engine.Utils
import Play.Engine.Types
import Control.Lens
import GHC.Generics
import Data.Typeable

import SDL.Vect (V2(..))
import qualified SDL.Vect as Vect (Point(..))
import qualified Data.Map as M
import qualified Data.Text as T

data Sprite
  = Sprite
  { _actionmap :: M.Map T.Text Int
  , _action :: Int
  , _texture :: SDL.Texture
  , _size :: !Size
  , _initSpeed :: !Int
  , _speed :: !Int
  , _pos :: !Int
  , _maxPos :: !Int
  }
  deriving (Eq, Generic, Typeable)

makeFieldsNoPrefix ''Sprite


data MakeArgs
  = MakeArgs
  { mkActionmap :: [T.Text]
  , mkAction :: !T.Text
  , mkSpeed :: !Int
  , mkTexture :: !SDL.Texture
  , mkSize :: !Size
  , mkMaxPos :: !Int
  }

simpleArgs :: Size -> SDL.Texture -> MakeArgs
simpleArgs sz t = MakeArgs
  { mkActionmap = ["normal"]
  , mkAction = "normal"
  , mkSpeed = 0
  , mkTexture = t
  , mkSize = sz
  , mkMaxPos = 1
  }

make :: MakeArgs -> Maybe Sprite
make MakeArgs{..} = do
  let actionMap = M.fromList $ zip mkActionmap [0..]
  act <- M.lookup mkAction actionMap
  pure $ Sprite
    { _actionmap = actionMap
    , _action = act
    , _texture = mkTexture
    , _initSpeed = mkSpeed
    , _speed = mkSpeed
    , _size = mkSize
    , _pos = 0
    , _maxPos = mkMaxPos
    }


update :: Maybe T.Text -> Bool -> Sprite -> Sprite
update !act !restart !sprite =
  sprite
    & over speed (\s -> if s < 0 || restart then sprite ^. initSpeed else s - 1)
    & over action (\a -> maybe a id $ flip M.lookup (sprite ^. actionmap) =<< act)
    & over pos (\p -> if restart then 0 else if sprite ^. speed == 0 then (p + 1) `mod` (sprite ^. maxPos) else p)

render :: SDL.Renderer -> Camera -> IPoint -> Size -> Word8 -> Sprite -> IO ()
render renderer cam position sz transp sprite = do
  let
    rect = toRect (cam $ position) sz
    ssz =  (sprite ^. size . x, sprite ^. size . y)
    clip = SDL.Rectangle
      (Vect.P $ V2 (fst ssz * sprite ^. pos) (snd ssz * sprite ^. action))
      (uncurry V2 ssz)
  SDL.textureBlendMode (sprite ^. texture) SDL.$= SDL.BlendAlphaBlend
  SDL.textureAlphaMod  (sprite ^. texture) SDL.$= transp
  SDL.copy renderer (sprite ^. texture) (Just $ fmap fromIntegral clip) (Just rect)
  SDL.textureAlphaMod  (sprite ^. texture) SDL.$= 255