{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}

module Types
    ( module Types, Word8, Word16, Word32 )
where

import qualified Data.ByteString.Lazy as B
import Data.Word
import qualified Data.Map as M

-- Main data types

type Offset = Word32
type Segment = (Offset, Word32, [String])
type Segments = [Segment]

data Register = RegPos Word16 | RegName String
    deriving (Show, Eq, Ord)

type ResReg = Word16

data TVal r
    = Reg r
    | Const Word16
    deriving (Eq, Show, Functor, Foldable)

data Conditional r = Cond (TVal r) CondOp (TVal r)
    deriving (Eq, Functor, Foldable)

data CondOp
    = Eq
    | Gt
    | Lt
    | GEq
    | LEq
    | NEq
    | Unknowncond B.ByteString
    deriving (Eq)

data ArithOp
    = Inc | Dec | Mult | Div | Mod | And | Or | XOr | Set
    deriving (Eq, Bounded, Enum)

data Command r
    = Play Word16
    | Random Word8 Word8
    | PlayAll Word8 Word8
    | PlayAllVariant (TVal r)
    | RandomVariant (TVal r)
    | Cancel
    | Game Word16
    | ArithOp ArithOp r (TVal r)
    | Neg r
    | Unknown B.ByteString r (TVal r)
    | Jump (TVal r)
    | NamedJump String -- Only in YAML files, never read from GMEs
    | Timer r (TVal r)
    deriving (Eq, Functor, Foldable)

type PlayList = [Word16]

data Line r = Line Offset [Conditional r] [Command r] PlayList
    deriving (Functor, Foldable)

type ProductID = Word32

data Similarity = Absent | Equal | Similar deriving (Show, Eq)

data TipToiFile = TipToiFile
    { ttProductId :: ProductID
    , ttRawXor :: Word32
    , ttComment :: B.ByteString
    , ttDate :: B.ByteString
    , ttLang :: B.ByteString
    , ttInitialRegs :: [Word16]
    , ttWelcome :: [PlayList]
    , ttScripts :: [(Word16, Maybe [Line ResReg])]
    , ttGames :: [Game]
    , ttAudioFiles :: [B.ByteString]
    , ttAudioFilesDoubles :: Similarity
    , ttAudioXor :: Word8
    , ttMediaFlags :: Maybe [Word16]
    , ttBinaries1 :: [(B.ByteString, B.ByteString)]
    , ttBinaries2 :: [(B.ByteString, B.ByteString)]
    , ttBinaries3 :: [(B.ByteString, B.ByteString)]
    , ttBinaries4 :: [(B.ByteString, B.ByteString)]
    , ttBinaries5 :: [(B.ByteString, B.ByteString)]
    , ttBinaries6 :: [(B.ByteString, B.ByteString)]
    , ttSpecialOIDs :: Maybe (Word16, Word16)
    , ttChecksum :: Word32
    , ttChecksumCalc :: Word32
    }

type PlayListList = [PlayList]
type GameId = Word16

data Game =
    CommonGame
        { gGameType                 :: Word16
        , gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        }
    | Game6
        { gRounds                   :: Word16
        , gBonusSubgameCount        :: Word16
        , gBonusRounds              :: Word16
        , gBonusTarget              :: Word16
        , gUnknownI                 :: Word16
        , gEarlyRounds              :: Word16
        , gUnknownQ                 :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gRoundStartPlayList2      :: PlayListList
        , gLaterRoundStartPlayList2 :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gBonusTargetScores        :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gBonusFinishPlayLists     :: [PlayListList]
        , gBonusSubgameIds          :: [Word16]
        }
    | Game7
        { gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gSubgameGroups            :: [[GameId]]
        }
    | Game8
        { gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gGameSelectOIDs           :: [Word16]
        , gGameSelect               :: [Word16]
        , gGameSelectErrors1        :: PlayListList
        , gGameSelectErrors2        :: PlayListList
        }
    | Game9
        { gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gExtraPlayLists           :: [PlayListList]
        }
    | Game10
        { gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gExtraPlayLists           :: [PlayListList]
        }
    | Game16
        { gRounds                   :: Word16
        , gUnknownC                 :: Word16
        , gEarlyRounds              :: Word16
        , gRepeatLastMedia          :: Word16
        , gUnknownX                 :: Word16
        , gUnknownW                 :: Word16
        , gUnknownV                 :: Word16
        , gStartPlayList            :: PlayListList
        , gRoundEndPlayList         :: PlayListList
        , gFinishPlayList           :: PlayListList
        , gRoundStartPlayList       :: PlayListList
        , gLaterRoundStartPlayList  :: PlayListList
        , gSubgames                 :: [SubGame]
        , gTargetScores             :: [Word16]
        , gFinishPlayLists          :: [PlayListList]
        , gExtraOIDs                :: [Word16]
        , gExtraPlayLists           :: [PlayListList]
        }
    | Game253
    deriving Show


gameType :: Game -> Word16
gameType (CommonGame {gGameType = gGameType }) = gGameType
gameType Game6 {} = 6
gameType Game7 {} = 7
gameType Game8 {} = 8
gameType Game9 {} = 9
gameType Game10 {} = 10
gameType Game16 {} = 16
gameType Game253 {} = 253

type OID = Word16

data SubGame = SubGame
    { sgUnknown :: B.ByteString
    , sgOids1 :: [OID]
    , sgOids2 :: [OID]
    , sgOids3 :: [OID]
    , sgPlaylist :: [PlayListList]
    }
    deriving Show


type Transscript = M.Map Word16 String
type CodeMap = M.Map String Word16


-- Command options

data ImageFormat = SVG { withPNG :: Bool } | PNG | PDF
    deriving Show

suffixOf :: ImageFormat -> String
suffixOf (SVG _) = "svg"
suffixOf PNG = "png"
suffixOf PDF = "pdf"

data Conf = Conf
    { cTransscriptFile :: Maybe FilePath
    , cCodeDim :: (Int, Int)
    , cDPI :: Int
    , cPixelSize :: Int
    , cImageFormat :: Maybe ImageFormat
    }
    deriving Show

