{-# LANGUAGE LambdaCase #-}
module GameEngine.Scene
  ( module GameEngine.Graphics.Frustum
  , module Data.Vect.Float.Util.Quaternion
  , Renderable(..)
  , Picture(..)
  , Camera(..)
  , Scene(..)
  , Resource(..)
  , asResource
  , module GameEngine.Scene
  ) where

import Data.Maybe
import Data.Vect
import System.FilePath
import Data.Vect.Float.Util.Quaternion
import Data.Vect.Float.Instances
import GameEngine.Graphics.Frustum
import Data.ByteString (ByteString)

type Scale = Float
type Position = Vec3
type Orientation = UnitQuaternion
type RGBA = Vec4
type SkinName = String
type ShaderName = String

newtype Tag = Tag ByteString
  deriving (Show)

data MD3Data
  = MD3Data
  { md3Position     :: Vec3
  , md3Orientation  :: UnitQuaternion
  , md3Scale        :: Float
  , md3RGBA         :: Vec4
  , md3ModelFile    :: FilePath
  , md3Frame        :: Maybe Int -- NOTE: animated models can not be shared due to mutable vertex buffers
  , md3SkinName     :: Maybe String
  , md3Attachments  :: [(Tag, MD3Data)]
  }
  deriving (Show)

defaultMD3Data :: MD3Data
defaultMD3Data = MD3Data
  { md3Position     = Vec3 0 0 0
  , md3Orientation  = unitU
  , md3Scale        = 1
  , md3RGBA         = Vec4 1 1 1 1
  , md3ModelFile    = error "md3ModelFile is mandatory!"
  , md3Frame        = Nothing
  , md3SkinName     = Nothing
  , md3Attachments  = []
  }

data Renderable
  = MD3             Position Orientation Scale RGBA FilePath
  | MD3Character    Position Orientation Scale RGBA FilePath SkinName
  | MD3New          MD3Data
  | BSPInlineModel  Position Orientation Scale RGBA FilePath Int
  | BSPMap          FilePath
  deriving Show

data Camera
  = Camera
  { cameraPosition      :: Position
  , cameraOrientation   :: Orientation
  , cameraProjection    :: Mat4
  , cameraFrustum       :: Frustum
  , cameraViewportSize  :: (Int,Int)
  -- add viewport position
  } deriving Show

data Scene
  = Scene
  { renderables :: [Renderable]
  , pictures    :: [Picture]
  , camera      :: Camera
  -- add shader time
  } deriving Show

data Resource
  = R_MD3             FilePath
  | R_MD3Character    FilePath SkinName
  | R_Shader          ShaderName
  | R_BSPMap          FilePath
  | R_Skin            FilePath
  | R_AnimationCfg    FilePath
  deriving (Eq, Show)

asResource :: Renderable -> [Resource]
asResource = \case
  MD3New d                          -> collectMD3 d
  MD3             _ _ _ _ name      -> [R_MD3 name]
  MD3Character    _ _ _ _ name skin -> [R_MD3Character name skin]
  BSPInlineModel  _ _ _ _ _ _       -> []
  BSPMap          name              -> [R_BSPMap name]
 where
  collectMD3 :: MD3Data -> [Resource]
  collectMD3 d = R_MD3 (md3ModelFile d) : maybeToList (R_Skin <$> md3SkinName d) ++ concatMap (collectMD3 . snd) (md3Attachments d)

data Picture
  = Picture
  { picturePosition :: Vec2
  , pictureSize     :: Vec2
  , pictureUV1      :: Vec2
  , pictureUV2      :: Vec2
  , pictureColor    :: RGBA
  , pictureShader   :: ShaderName
  } deriving Show
