{-# LANGUAGE FlexibleContexts #-}
module CSPM.TypeChecker (
    typeCheck, typeCheckExpect,
    typeOfExp, dependenciesOfExp,
    
    initTypeChecker,
    TypeCheckMonad, TypeInferenceState,
    runTypeChecker, runFromStateToState,
) where

import Control.Monad.Trans

import CSPM.DataStructures.Names
import CSPM.DataStructures.Syntax
import CSPM.DataStructures.Types
import CSPM.TypeChecker.BuiltInFunctions
import qualified CSPM.TypeChecker.Common as TC
import CSPM.TypeChecker.Compressor
import CSPM.TypeChecker.Dependencies
import CSPM.TypeChecker.Environment
import CSPM.TypeChecker.Expr
import CSPM.TypeChecker.InteractiveStmt
import CSPM.TypeChecker.Module
import CSPM.TypeChecker.Monad
import CSPM.TypeChecker.Unification
import Util.Annotated
import Util.Exception

runFromStateToState :: TypeInferenceState -> TypeCheckMonad a -> 
            IO (a, [ErrorMessage], TypeInferenceState)
runFromStateToState st prog = runTypeChecker st $ do
    r <- prog
    ws <- getWarnings
    resetWarnings
    s <- getState
    return (r, ws, s)

initTypeChecker :: IO TypeInferenceState
initTypeChecker = runTypeChecker newTypeInferenceState $ do
    injectBuiltInFunctions
    -- Add a blank level in the environment to allow built in functions
    -- to be overriden.
    local [] getState

typeCheckExpect :: (Compressable a, TC.TypeCheckable a Type) => Type -> a -> TypeCheckMonad a
typeCheckExpect t exp = TC.typeCheckExpect exp t >> mcompress exp

typeCheck :: (Compressable a, TC.TypeCheckable a b) => a -> TypeCheckMonad a
typeCheck exp = TC.typeCheck exp >> mcompress exp

typeOfExp :: PExp -> TypeCheckMonad Type
typeOfExp exp = do
    -- See if has been type checked, if so, return type,
    -- else type check
    mt <- liftIO $ readPType (snd (annotation exp))
    case mt of 
        Just t -> evaluateDots t >>= compress
        Nothing -> typeCheck exp >> typeOfExp exp

-- | Returns the list of names that this expression depends on
dependenciesOfExp :: TCExp -> TypeCheckMonad [Name]
dependenciesOfExp exp = dependencies exp