module UHC.Light.Compiler.CodeGen.Tag
( CTag (..), ctagIsRec, ctagTag, ctagChar, ctagInt, emptyCTag
, mkOnlyConInfoCTag, patchTyInfoCTag
, ctag, ppCTag, ppCTagInt
, tagBoolTrue, tagBoolFalse
, tagListCons, tagListNil
, TagDataInfo (..)
, mkTyIsConTagInfo, mkConTagInfo, emptyTagDataInfo
, tagInfoInt, tagInfoChar
, TagLike (..), tagDataInfo
, CTagsMp, emptyCTagsMp
, mkClassCTag )
where
import Data.Maybe
import Control.Monad
import UHC.Util.Pretty
import UHC.Util.AssocL
import UHC.Light.Compiler.Base.HsName
import UHC.Light.Compiler.Base.HsName.Builtin
import UHC.Util.Binary
import UHC.Util.Serialize



{-# LINE 33 "src/ehc/CodeGen/Tag.chs" #-}
data CTag
  = CTagRec
  | CTag
      { ctagTyNm        :: !HsName
      , ctagNm          :: !HsName
      , ctagTag'        :: !Int
      , ctagArity       :: !Int
      , ctagMaxArity    :: !Int
      }
  deriving (Show,Eq,Ord)

ctagIsRec :: CTag -> Bool
ctagIsRec CTagRec = True
ctagIsRec t       = False

ctagTag :: CTag -> Int
ctagTag CTagRec = 0
ctagTag t       = ctagTag' t

ctagInt  =  CTag hsnInt  hsnInt  0 1 1
{-# INLINE ctagInt #-}
ctagChar =  CTag hsnChar hsnChar 0 1 1
{-# INLINE ctagChar #-}

emptyCTag = CTag hsnUnknown hsnUnknown 0 0 0
{-# INLINE emptyCTag #-}

{-# LINE 62 "src/ehc/CodeGen/Tag.chs" #-}
-- | Construct a minimal datatype tag which still must be completed wrt more global datatype info
mkOnlyConInfoCTag :: HsName -> Int -> Int -> CTag
mkOnlyConInfoCTag conNm tg arity = emptyCTag {ctagNm = conNm, ctagTag' = tg, ctagArity = arity}

-- | Patch a datatype tag with datatype global info
patchTyInfoCTag :: HsName -> Int -> CTag -> CTag
patchTyInfoCTag tyNm maxArity t = t {ctagTyNm = tyNm, ctagMaxArity = maxArity}

{-# LINE 72 "src/ehc/CodeGen/Tag.chs" #-}
-- only used when `not ehcCfgClassViaRec'
mkClassCTag :: HsName -> Int -> CTag
mkClassCTag n sz = CTag n n 0 sz sz

{-# LINE 78 "src/ehc/CodeGen/Tag.chs" #-}
ctag :: a -> (HsName -> HsName -> Int -> Int -> Int -> a) -> CTag -> a
ctag n t tg = case tg of {CTag tn cn i a ma -> t tn cn i a ma; _ -> n}
{-# INLINE ctag #-}

ppCTag :: CTag -> PP_Doc
ppCTag = ctag (pp "Rec") (\tn cn t a ma -> pp t >|< "/" >|< pp cn >|< "/" >|< pp a >|< "/" >|< pp ma)

ppCTagInt :: CTag -> PP_Doc
ppCTagInt = ctag (pp "-1") (\_ _ t _ _ -> pp t)

instance PP CTag where
  pp = ppCTag

{-# LINE 97 "src/ehc/CodeGen/Tag.chs" #-}
tagBoolTrue, tagBoolFalse :: Int
tagBoolTrue  = 1 		-- this makes it hardcoded, ideally dependent on datatype def itself !!
tagBoolFalse = 0 		-- this makes it hardcoded, ideally dependent on datatype def itself !!

{-# LINE 103 "src/ehc/CodeGen/Tag.chs" #-}
tagListCons, tagListNil :: Int
tagListCons = 0 		-- this makes it hardcoded, ideally dependent on datatype def itself !!
tagListNil  = 1 		-- this makes it hardcoded, ideally dependent on datatype def itself !!

{-# LINE 113 "src/ehc/CodeGen/Tag.chs" #-}
-- | datatype info about tag: type name & constr name, required throughout various codegen stages
data TagDataInfo = TagDataInfo
  { tagDataInfoTypeNm 	:: !HsName
  , tagDataInfoConstrNm	:: !HsName
  }
  deriving (Show)

instance Eq TagDataInfo where
  i1 == i2 = tagDataInfoConstrNm i1 == tagDataInfoConstrNm i2

instance Ord TagDataInfo where
  i1 `compare` i2 = tagDataInfoConstrNm i1 `compare` tagDataInfoConstrNm i2

{-# LINE 128 "src/ehc/CodeGen/Tag.chs" #-}
mkTyConTagInfo :: HsName -> HsName -> TagDataInfo
mkTyConTagInfo = TagDataInfo
{-# INLINE mkTyConTagInfo #-}

-- | Construct info when Ty and Con name are equal
mkTyIsConTagInfo :: HsName -> TagDataInfo
mkTyIsConTagInfo n = mkTyConTagInfo n n
{-# INLINE mkTyIsConTagInfo #-}

mkConTagInfo :: HsName -> TagDataInfo
mkConTagInfo cn = mkTyConTagInfo hsnUnknown cn
{-# INLINE mkConTagInfo #-}

emptyTagDataInfo = mkTyConTagInfo hsnUnknown hsnUnknown

{-# LINE 145 "src/ehc/CodeGen/Tag.chs" #-}
tagInfoInt  = mkTyIsConTagInfo hsnInt
tagInfoChar = mkTyIsConTagInfo hsnChar

{-# LINE 150 "src/ehc/CodeGen/Tag.chs" #-}
class TagLike t where
  tagIsData			:: t -> Bool
  tagIsTup			:: t -> Bool

  -- | extract data related info, only allowed when tagIsData
  tagMbDataInfo		:: t -> Maybe TagDataInfo
  tagDataTypeNm   	:: t -> HsName
  tagDataConstrNm 	:: t -> HsName
  tagDataTag		:: t -> Int

  -- defaults: either tagDataInfo or tagDataTypeNm and tagDataConstrNm and tagIsData
  tagMbDataInfo		t	= if tagIsData t then Just (emptyTagDataInfo {tagDataInfoTypeNm = tagDataTypeNm t, tagDataInfoConstrNm = tagDataConstrNm t}) else Nothing
  tagDataTypeNm			= tagDataInfoTypeNm . tagDataInfo
  tagDataConstrNm		= tagDataInfoConstrNm . tagDataInfo
  tagIsData 			= isJust . tagMbDataInfo

  -- defaults
  tagIsTup				= not . tagIsData

-- | Assuming a datatype, return info
tagDataInfo :: TagLike t => t -> TagDataInfo
tagDataInfo = fromJust . tagMbDataInfo
{-# INLINE tagDataInfo #-}

instance TagLike CTag where
  tagMbDataInfo	 	= ctag Nothing (\tn cn _ _ _ -> Just (emptyTagDataInfo {tagDataInfoTypeNm = tn, tagDataInfoConstrNm = cn}))
  tagDataTag 		= ctagTag'
  -- not necessary:
  tagIsTup  		= ctagIsRec
  tagDataTypeNm 	= ctagTyNm
  tagDataConstrNm 	= ctagNm

{-# LINE 184 "src/ehc/CodeGen/Tag.chs" #-}
instance PP TagDataInfo where
  pp i = tagDataInfoTypeNm i >|< "#" >|< tagDataInfoConstrNm i

{-# LINE 193 "src/ehc/CodeGen/Tag.chs" #-}
type CTagsMp = AssocL HsName (AssocL HsName CTag)

emptyCTagsMp :: CTagsMp
emptyCTagsMp = []

{-# LINE 204 "src/ehc/CodeGen/Tag.chs" #-}
deriving instance Typeable CTag
deriving instance Data CTag


{-# LINE 214 "src/ehc/CodeGen/Tag.chs" #-}
instance Serialize CTag where
  sput = sputShared
  sget = sgetShared
  sputNested (CTagRec          ) = sputWord8 0
  sputNested (CTag    a b c d e) = sputWord8 1 >> sput a >> sput b >> sput c >> sput d >> sput e
  sgetNested
    = do t <- sgetWord8
         case t of
           0 -> return CTagRec
           1 -> liftM5 CTag    sget sget sget sget sget