module DDC.Core.Check.Judge.Type.Base
        ( Checker
        , Demand (..)
        , Table  (..)
        , returnX
        , runForDemand

        , module DDC.Core.Check.Base
        , module DDC.Core.Check.Judge.Inst
        , module DDC.Core.Check.Judge.Sub
        , module DDC.Core.Check.Judge.Eq
        , module DDC.Core.Check.Witness
        , module DDC.Core.Check.Error
        , module DDC.Core.Transform.Reannotate
        , module DDC.Core.Transform.SubstituteTX
        , module DDC.Core.Exp.Annot.AnTEC

        , module DDC.Type.Transform.SubstituteT
        , module DDC.Type.Transform.Instantiate
        , module DDC.Type.Transform.BoundT)
where
import DDC.Core.Check.Base
import DDC.Core.Check.Judge.Inst
import DDC.Core.Check.Judge.Sub
import DDC.Core.Check.Judge.Eq
import DDC.Core.Check.Witness
import DDC.Core.Check.Error
import DDC.Core.Transform.Reannotate
import DDC.Core.Transform.SubstituteTX
import DDC.Core.Exp.Annot.AnTEC

import DDC.Type.Transform.SubstituteT
import DDC.Type.Transform.Instantiate
import DDC.Type.Transform.BoundT


-- | Demand placed on suspensions by the surrounding context.
data Demand
        -- | Run suspensions as we encounter them.
        = DemandRun

        -- | Ignore suspensions, don't run them.
        | DemandNone
        deriving Show


-- | Type of the function that checks some node of the core AST.
type Checker a n
        =  (Show a, Show n, Ord n, Pretty n)
        => Table a n                    -- ^ Static configuration.
        -> Context n                    -- ^ Input context.
        -> Mode n                       -- ^ Type checker mode.
        -> Demand                       -- ^ Demand on the expression.
        -> Exp a n                      -- ^ Expression to check.
        -> CheckM a n
                ( Exp (AnTEC a n) n     -- Annotated, checked expression.
                , Type n                -- Type of the expression.
                , TypeSum n             -- Effect sum of expression.
                , Context n)            -- Output context.


-- | Table of environment things that do not change during type checking
--
--   We've got the static config,
--    global kind and type environments,
--    and a type checking function for each node of the AST.
--
--   We split the type checker into separate functions and dispatch them
--   via this table so we can handle each AST node in a separate module,
--   while avoiding the explicit mutual recursion. If the functions were
--   explicitly mutually recursive then we'd need to write GHC boot modules,
--   which is annoying.
--
data Table a n
        = Table
        { tableConfig           :: Config n
        , tableKindEnv          :: KindEnv n
        , tableTypeEnv          :: TypeEnv n
        , tableCheckExp         :: Checker a n
        , tableCheckVarCon      :: Checker a n
        , tableCheckAppT        :: Checker a n
        , tableCheckAppX        :: Checker a n
        , tableCheckLamT        :: Checker a n
        , tableCheckLamX        :: Checker a n
        , tableCheckLet         :: Checker a n
        , tableCheckLetPrivate  :: Checker a n
        , tableCheckCase        :: Checker a n
        , tableCheckCast        :: Checker a n
        , tableCheckWitness     :: Checker a n }


-- | Helper function for building the return value of checkExpM'
--   It builts the AnTEC annotation and attaches it to the new AST node,
--   as well as returning the current effect and closure in the appropriate
--   form as part of the tuple.
returnX :: Ord n
        => a                            -- ^ Annotation for the returned expression.
        -> (AnTEC a n
                -> Exp (AnTEC a n) n)   -- ^ Fn to build the returned expression.
        -> Type n                       -- ^ Type of expression.
        -> TypeSum n                    -- ^ Effect sum of expression.
        -> Context n                    -- ^ Input context.
        -> CheckM a n
                ( Exp (AnTEC a n) n     -- Annotated, checked expression.
                , Type n                -- Type of expression.       (id to above)
                , TypeSum n             -- Effect sum of expression. (id to above)
                , Context n)            -- Output context.

returnX !a !f !t !es !ctx
 = let  e       = TSum es
   in   return  (f (AnTEC t e (tBot kClosure) a)
                , t, es, ctx)
{-# INLINE returnX #-}


-- Run ------------------------------------------------------------------------
-- | If an expression has suspension type then run it.
runForDemand 
        :: Ord n
        => Config n                     -- ^ Type checker config.
        -> a                            -- ^ Annotation for new
        -> Demand                       -- ^ Demand placed on expression.
        -> Exp    (AnTEC a n) n         -- ^ Expression to inspect.
        -> Type   n                     -- ^ Type of the expression.
        -> Effect n                     -- ^ Effect of the expression.
        -> CheckM a n
                ( Exp (AnTEC a n) n     -- New expression, possibly with run cast.
                , Type   n              -- New type of expression.
                , Effect n)             -- New effect of expression.

runForDemand _config _a DemandNone xExp tExp eExp 
 = return (xExp, tExp, eExp)

runForDemand config a  DemandRun  xExp tExp eExp

 -- If the expression is wrapped in an explicit box or run then
 -- don't run it again. Doing this will just confuse the client
 -- programmer.
 | isXCastBox xExp || isXCastRun xExp
 = return (xExp, tExp, eExp)

 -- Insert an implicit run cast for this suspension.
 | configImplicitRun config
 , Just (eResult, tResult)  <- takeTSusp tExp
 = let
        -- Effect of overall expression is effect of computing
        -- the suspension plus the effect we get by running 
        -- that suspension.
        eTotal  = tSum kEffect [eExp, eResult]

        -- Annotation for the cast expression.
        aCast   = AnTEC tResult eTotal (tBot kClosure) a

   in   return  ( XCast aCast CastRun xExp
                , tResult
                , eTotal)

 | otherwise
 = return (xExp, tExp, eExp)