{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module GHC.StgToJS.Heap
  ( closureType
  , infoClosureType
  , infoFunArity
  , isObject
  , isThunk
  , isThunk'
  , isBlackhole
  , isFun
  , isFun'
  , isPap
  , isPap'
  , isCon
  , isCon'
  , conTag
  , conTag'
  , closureInfo
  , closureMeta
  , closureField1
  , closureField2
  , closureCC
  , funArity
  , papArity
  , funOrPapArity
  -- * Field names
  , closureInfo_
  , closureMeta_
  , closureCC_
  , closureField1_
  , closureField2_
  -- * Javascript Type literals
  , jTyObject
  )
where

import GHC.Prelude

import GHC.JS.JStg.Syntax
import GHC.JS.Make
import GHC.StgToJS.Types
import GHC.Data.FastString

-- | Closure infotable field name
closureInfo_ :: FastString
closureInfo_ = "f"

-- | Closure first payload field name
closureField1_ :: FastString
closureField1_ = "d1"

-- | Closure second payload field name
closureField2_ :: FastString
closureField2_ = "d2"

-- | Closure meta field name
closureMeta_ :: FastString
closureMeta_ = "m"

-- | Closure cost-center field name
closureCC_ :: FastString
closureCC_ = "cc"

-- | Infotable type field name
infoClosureType_ :: FastString
infoClosureType_ = "t"

-- | Infotable tag field name
infoConTag_ :: FastString
infoConTag_ = "a"

-- | Infotable arity field name
infoFunArity_ :: FastString
infoFunArity_ = "a"

jTyObject :: JStgExpr
jTyObject = jString "object"

-- | Closure type from infotable
infoClosureType :: JStgExpr -> JStgExpr
infoClosureType f = f .^ infoClosureType_

-- | Function arity from infotable
infoFunArity :: JStgExpr -> JStgExpr
infoFunArity f = f .^ infoFunArity_

closureType :: JStgExpr -> JStgExpr
closureType = infoClosureType . closureInfo

isObject :: JStgExpr -> JStgExpr
isObject c = typeOf c .===. String "object"

isThunk :: JStgExpr -> JStgExpr
isThunk c = closureType c .===. toJExpr Thunk

isThunk' :: JStgExpr -> JStgExpr
isThunk' f = infoClosureType f .===. toJExpr Thunk

isBlackhole :: JStgExpr -> JStgExpr
isBlackhole c = closureType c .===. toJExpr Blackhole

isFun :: JStgExpr -> JStgExpr
isFun c = closureType c .===. toJExpr Fun

isFun' :: JStgExpr -> JStgExpr
isFun' f = infoClosureType f .===. toJExpr Fun

isPap :: JStgExpr -> JStgExpr
isPap c = closureType c .===. toJExpr Pap

isPap' :: JStgExpr -> JStgExpr
isPap' f = infoClosureType f .===. toJExpr Pap

isCon :: JStgExpr -> JStgExpr
isCon c = closureType c .===. toJExpr Con

isCon' :: JStgExpr -> JStgExpr
isCon' f = infoClosureType f .===. toJExpr Con

conTag :: JStgExpr -> JStgExpr
conTag = conTag' . closureInfo

conTag' :: JStgExpr -> JStgExpr
conTag' f = f .^ infoConTag_

-- | Get closure infotable
closureInfo :: JStgExpr -> JStgExpr
closureInfo p = p .^ closureInfo_

-- | Get closure metadata
closureMeta :: JStgExpr -> JStgExpr
closureMeta p = p .^ closureMeta_

-- | Get closure cost-center
closureCC :: JStgExpr -> JStgExpr
closureCC p = p .^ closureCC_

-- | Get closure extra field 1
closureField1 :: JStgExpr -> JStgExpr
closureField1 p = p .^ closureField1_

-- | Get closure extra field 2
closureField2 :: JStgExpr -> JStgExpr
closureField2 p = p .^ closureField2_

-- | Number of  arguments (arity & 0xff = arguments, arity >> 8 = number of registers)
funArity :: JStgExpr -> JStgExpr
funArity = infoFunArity . closureInfo

-- arity of a partial application
papArity :: JStgExpr -> JStgExpr
papArity cp = closureField1 (closureField2 cp)

funOrPapArity
  :: JStgExpr       -- ^ heap object
  -> Maybe JStgExpr -- ^ reference to infotable, if you have one already (saves a c.f lookup twice)
  -> JStgExpr       -- ^ arity tag (tag >> 8 = registers, tag & 0xff = arguments)
funOrPapArity c = \case
  Nothing -> ((IfExpr (toJExpr (isFun c))) (toJExpr (funArity c)))
             (toJExpr (papArity c))
  Just f  -> ((IfExpr (toJExpr (isFun' f))) (toJExpr (infoFunArity f)))
             (toJExpr (papArity c))
