{-# LANGUAGE RecordWildCards #-}
module ParserGen.Types
    ( Decl (..)
    , unzipDecls

    , Datatype (..)

    , DataConstructor (..)
    , getConstructorWidth

    , DataField (..)
    , getFieldWidth
    , getFieldRepeatType
    , getFieldHasRepeat
    , getFieldIsIgnored

    , ParserType (..)

    , Repacker (..)
    , RepackerField (..)
    ) where

import Data.Maybe (isJust, isNothing)
import Language.Haskell.TH (Exp, Type (..))

data Decl
    = DatatypeDecl Datatype
    | RepackerDecl Repacker
    deriving (Show)

unzipDecls :: [Decl] -> ([Datatype], [Repacker])
unzipDecls decls =
    ( [d | DatatypeDecl d <- decls]
    , [r | RepackerDecl r <- decls]
    )

data Datatype
    = Datatype
    { typeName     :: String
    , typeConstrs  :: [DataConstructor]
    } deriving (Show)

data DataConstructor
    = DataConstructor
    { constrName   :: String
    , constrPrefix :: Maybe String
    , constrFields :: [DataField]
    } deriving (Show)

getConstructorWidth :: DataConstructor -> Int
getConstructorWidth = sum . map getFieldWidth . constrFields

data DataField
    = DataField
    { fieldName    :: Maybe String
    , fieldRepeat  :: Maybe Int
    , fieldType    :: Type
    , fieldStrict  :: Bool
    , fieldWidth   :: Int
    , fieldParser  :: ParserType
    } deriving (Show)

-- get size to skip taking into account its repetition and sign if exists
getFieldWidth :: DataField -> Int
getFieldWidth (DataField {..}) =
    let width = fieldWidth + if fieldParser == SignedParser then 1 else 0
        times = maybe 1 id fieldRepeat
    in width * times

getFieldRepeatType :: DataField -> Type
getFieldRepeatType df
    | getFieldHasRepeat df = AppT ListT $ fieldType df
    | otherwise            = fieldType df

getFieldHasRepeat :: DataField -> Bool
getFieldHasRepeat = isJust . fieldRepeat

getFieldIsIgnored :: DataField -> Bool
getFieldIsIgnored df = case fieldParser df of
    CustomParser    _ -> False
    HardcodedString _ -> False
    _                 -> isNothing (fieldName df)

data ParserType
    = CustomParser    Exp    -- user provided parser, ex: issue
    | UnsignedParser         -- type/newtype wrapper around supported datatypes
    | SignedParser           -- type/newtype wrapper around numerical datatypes only
    | HardcodedString String -- raw string, ex "B7014"
    deriving (Show, Eq)

data Repacker = Repacker
    { repackerName        :: String
    , repackerConstructor :: String
    , repackerFields      :: [RepackerField]
    } deriving (Show)

data RepackerField = RepackerField
    { repackerFieldName     :: String
    , repackerFieldUnparser :: Maybe Exp
    } deriving (Show)