module Rattletrap.Header where

import Rattletrap.Primitive
import Rattletrap.Property
import Rattletrap.PropertyValue

import qualified Data.Binary as Binary
import qualified Data.Map as Map

-- | Contains high-level metadata about a 'Rattletrap.Replay.Replay'.
data Header = Header
  { headerEngineVersion :: Word32
  -- ^ The "major" version number.
  , headerLicenseeVersion :: Word32
  -- ^ The "minor" version number.
  , headerPatchVersion :: Maybe Word32
  -- ^ The "patch" version number.
  , headerLabel :: Text
  -- ^ Always @TAGame.Replay_Soccar_TA@.
  , headerProperties :: Dictionary Property
  -- ^ These properties determine how a replay will look in the list of
  -- replays in-game. One element is required for the replay to show up:
  --
  -- - MapName: This is a 'Rattletrap.PropertyValue.NameProperty' with a
  --   case-insensitive map identifier, like @Stadium_P@.
  --
  -- There are many other properties that affect how the replay looks in the
  -- list of replays.
  --
  -- - Date: A 'Rattletrap.PropertyValue.StrProperty' with the format
  --   @YYYY-mm-dd:HH-MM@. Dates are not validated, but the month must be
  --   between 1 and 12 to show up. The hour is shown modulo 12 with AM or PM.
  -- - MatchType: A 'Rattletrap.PropertyValue.NameProperty'. If this is not
  --   one of the expected values, nothing will be shown next to the replay's
  --   map. The expected values are: @Online@, @Offline@, @Private@, and
  --   @Season@.
  -- - NumFrames: This 'Rattletrap.PropertyValue.IntProperty' is used to
  --   calculate the length of the match. There are 30 frames per second,
  --   a typical 5-minute match has about 9,000 frames.
  -- - PrimaryPlayerTeam: This is an 'Rattletrap.PropertyValue.IntProperty'.
  --   It is either 0 (blue) or 1 (orange). Any other value is ignored. If
  --   this would be 0, you don't have to set it at all.
  -- - ReplayName: An optional 'Rattletrap.PropertyValue.StrProperty' with a
  --   user-supplied name for the replay.
  -- - Team0Score: The blue team's score as an
  --   'Rattletrap.PropertyValue.IntProperty'. Can be omitted if the score is
  --   0.
  -- - Team1Score: The orange team's score as an
  --   'Rattletrap.PropertyValue.IntProperty'. Can also be omitted if the
  --   score is 0.
  -- - TeamSize: An 'Rattletrap.PropertyValue.IntProperty' with the number of
  --   players per team. This value is not validated, so you can put absurd
  --   values like 99. To get an "unfair" team size like 1v4, you must set the
  --   bUnfairBots 'Rattletrap.PropertyValue.BoolProperty' to @True@.
  } deriving (Eq, Ord, Show)

getHeader :: Binary.Get Header
getHeader = do
  engineVersion <- getWord32
  licenseeVersion <- getWord32
  patchVersion <- getPatchVersion engineVersion licenseeVersion
  label <- getText
  properties <- getDictionary getProperty
  pure (Header engineVersion licenseeVersion patchVersion label properties)

getPatchVersion :: Word32 -> Word32 -> Binary.Get (Maybe Word32)
getPatchVersion major minor =
  if hasPatchVersion major minor
    then do
      patchVersion <- getWord32
      pure (Just patchVersion)
    else pure Nothing

hasPatchVersion :: Word32 -> Word32 -> Bool
hasPatchVersion major minor = major >= Word32 868 && minor >= Word32 18

putHeader :: Header -> Binary.Put
putHeader header = do
  putWord32 (headerEngineVersion header)
  putWord32 (headerLicenseeVersion header)
  case headerPatchVersion header of
    Nothing -> pure ()
    Just patchVersion -> putWord32 patchVersion
  putText (headerLabel header)
  putDictionary putProperty (headerProperties header)

getVersion :: Header -> (Int, Int)
getVersion header =
  let major = getMajorVersion header
      minor = getMinorVersion header
  in (major, minor)

getMajorVersion :: Header -> Int
getMajorVersion header =
  fromIntegral (word32Value (headerEngineVersion header))

getMinorVersion :: Header -> Int
getMinorVersion header =
  fromIntegral (word32Value (headerLicenseeVersion header))

getNumFrames :: Header -> Int
getNumFrames header =
  let key = textValue (stringToText "NumFrames")
      properties = dictionaryValue (headerProperties header)
  in case Map.lookup key properties of
       Just (Property _ _ (IntProperty numFrames)) ->
         fromIntegral (int32Value numFrames)
       _ -> 0

getMaxChannels :: Header -> Word
getMaxChannels header =
  let key = textValue (stringToText "MaxChannels")
      properties = dictionaryValue (headerProperties header)
  in case Map.lookup key properties of
       Just (Property _ _ (IntProperty numFrames)) ->
         fromIntegral (int32Value numFrames)
       _ -> 1023