    module Vectorise.Monad.Global (
    	readGEnv,
    	setGEnv,
    	updGEnv,

    	-- * Vars
    	defGlobalVar,

    	-- * Scalars
    	globalScalars,

    	-- * TyCons
    	lookupTyCon,
    	lookupBoxedTyCon,
    	defTyCon,

    	-- * Datacons
    	lookupDataCon,
    	defDataCon,

    	-- * PA Dictionaries
    	lookupTyConPA,
    	defTyConPA,
    	defTyConPAs,

    	-- * PR Dictionaries
    	lookupTyConPR
    ) where
    import Vectorise.Monad.Base
    import Vectorise.Env
    import TyCon
    import DataCon
    import NameEnv
    import Var
    import VarEnv
    import VarSet

    -- Global Environment ---------------------------------------------------------
    -- | Project something from the global environment.
    readGEnv :: (GlobalEnv -> a) -> VM a
    readGEnv f	= VM $ \_ genv lenv -> return (Yes genv lenv (f genv))

    -- | Set the value of the global environment.
    setGEnv :: GlobalEnv -> VM ()
    setGEnv genv	= VM $ \_ _ lenv -> return (Yes genv lenv ())

    -- | Update the global environment using the provided function.
    updGEnv :: (GlobalEnv -> GlobalEnv) -> VM ()
    updGEnv f	= VM $ \_ genv lenv -> return (Yes (f genv) lenv ())

    -- Vars -----------------------------------------------------------------------
    -- | Add a mapping between a global var and its vectorised version to the state.
    defGlobalVar :: Var -> Var -> VM ()
    defGlobalVar v v' = updGEnv $ \env ->
      env { global_vars = extendVarEnv (global_vars env) v v'
          , global_exported_vars = upd (global_exported_vars env)
          }
      where
        upd env | isExportedId v = extendVarEnv env v (v, v')
                | otherwise      = env

    -- Scalars --------------------------------------------------------------------
    -- | Get the set of global scalar variables.
    globalScalars :: VM VarSet
    globalScalars
    	= readGEnv global_scalars

    -- TyCons ---------------------------------------------------------------------
    -- | Lookup the vectorised version of a `TyCon` from the global environment.
    lookupTyCon :: TyCon -> VM (Maybe TyCon)
    lookupTyCon tc
      | isUnLiftedTyCon tc || isTupleTyCon tc
      = return (Just tc)

      | otherwise
      = readGEnv $ \env -> lookupNameEnv (global_tycons env) (tyConName tc)

    -- | Lookup the vectorised version of a boxed `TyCon` from the global environment.
    lookupBoxedTyCon :: TyCon -> VM (Maybe TyCon)
    lookupBoxedTyCon tc
    	= readGEnv $ \env -> lookupNameEnv (global_boxed_tycons env)
                                               (tyConName tc)

    -- | Add a mapping between plain and vectorised `TyCon`s to the global environment.
    defTyCon :: TyCon -> TyCon -> VM ()
    defTyCon tc tc' = updGEnv $ \env ->
      env { global_tycons = extendNameEnv (global_tycons env) (tyConName tc) tc' }

    -- DataCons -------------------------------------------------------------------
    -- | Lookup the vectorised version of a `DataCon` from the global environment.
    lookupDataCon :: DataCon -> VM (Maybe DataCon)
    lookupDataCon dc
      | isTupleTyCon (dataConTyCon dc)
      = return (Just dc)

      | otherwise
      = readGEnv $ \env -> lookupNameEnv (global_datacons env) (dataConName dc)

    -- | Add the mapping between plain and vectorised `DataCon`s to the global environment.
    defDataCon :: DataCon -> DataCon -> VM ()
    defDataCon dc dc' = updGEnv $ \env ->
      env { global_datacons = extendNameEnv (global_datacons env) (dataConName dc) dc' }

    -- PA dictionaries ------------------------------------------------------------
    -- | Lookup a PA `TyCon` from the global environment.
    lookupTyConPA :: TyCon -> VM (Maybe Var)
    lookupTyConPA tc
    	= readGEnv $ \env -> lookupNameEnv (global_pa_funs env) (tyConName tc)

    -- | Add a mapping between a PA TyCon and is vectorised version to the global environment.
    defTyConPA :: TyCon -> Var -> VM ()
    defTyConPA tc pa = updGEnv $ \env ->
      env { global_pa_funs = extendNameEnv (global_pa_funs env) (tyConName tc) pa }

    -- | Add several mapping between PA TyCons and their vectorised versions to the global environment.
    defTyConPAs :: [(TyCon, Var)] -> VM ()
    defTyConPAs ps = updGEnv $ \env ->
      env { global_pa_funs = extendNameEnvList (global_pa_funs env)
                                               [(tyConName tc, pa) | (tc, pa) <- ps] }

    -- PR Dictionaries ------------------------------------------------------------
    lookupTyConPR :: TyCon -> VM (Maybe Var)
    lookupTyConPR tc = readGEnv $ \env -> lookupNameEnv (global_pr_funs env) (tyConName tc)