-- Please, see the file LICENSE for copyright and license information. A monad to keep an environment with hylomorphism definitions and throwing errors.
>module HFusion.Internal.FuseEnvironment(
>                       clearFuseEnv,
>                       insertFuseEnv,
>                       deleteFuseEnv,
>                       lookupFuseEnv,
>                       fuseFuseEnv,
>                       renameFuseEnv,
>                       toListFuseEnv,
>                       runEnv,
>                       env2FusionState,
>                       Binds,
>                       emptyBinds,
>                       Env) where
> import HFusion.Internal.HsSyn(Variable(..))
> import HFusion.Internal.Utils(newVarGen)
> import Control.Monad.State(State,StateT(..),get,put,evalStateT)
> import Control.Monad.Error(throwError,ErrorT)
> import Control.Monad.Trans(lift)
> import Data.Map(empty,Map,insert,lookup,delete,toList)
> import HFusion.Internal.Utils(VarGen)
> import HFusion.Internal.HyloFace
> import List(elemIndex)
> import HFusion.Internal.FuseFace(fusionar,HyloT,getNames,renameHT)
> import Prelude(maybe,Int,Monad(..),(.),error,id,($),
>                show,fst,Either(..),either,foldr,flip)
data Key a = SingleKey a | KeyList [a]
> type Binds = (Map [Variable] HyloT,Map Variable [Variable])
> type Env a = StateT Binds (ErrorT FusionError (State VarGen)) a
> emptyBinds :: Binds
> emptyBinds = (empty,empty)
> env2FusionState :: Env a -> FusionState a
> env2FusionState m = evalStateT m emptyBinds
> runEnv :: Env a -> Either FusionError a
> runEnv = runFusionState newVarGen . env2FusionState
> clearFuseEnv :: Env ()
> clearFuseEnv = put emptyBinds
> insertFuseEnv :: HyloT -> Env ()
> insertFuseEnv h = do (mh,mv)<-get;put (insert names h mh,foldr (flip insert names) mv names)
>  where names = getNames h
> deleteFuseEnv :: Variable -> Env ()
> deleteFuseEnv v = do (mh,mv)<-get
>                      maybe (return ()) (\ns -> put (delete ns mh,foldr delete mv ns)) (Data.Map.lookup v mv)
> lookupFuseEnv :: Variable -> Env HyloT
> lookupFuseEnv v = do (mh,mv)<-get 
>                      maybe err (maybe err return . flip Data.Map.lookup mh) (Data.Map.lookup v mv)
>   where err = throwError (NotFound (show v))
> fuseFuseEnv :: [Variable] -> Variable -> Int -> Variable -> Env HyloT
> fuseFuseEnv names v1 ia v2 = 
>       do h1<-lookupFuseEnv v1
>          h2<-lookupFuseEnv v2
>          let getIndex v h = maybe (error "fuseFuseEnv: this should not happen.") id $ elemIndex v (getNames h)
>          (_,h3)<-lift (fusionar names h1 (getIndex v1 h1) ia h2 (getIndex v2 h2))
>          insertFuseEnv h3
>          return h3
> renameFuseEnv :: Variable -> Variable -> Env (HyloT,HyloT)
> renameFuseEnv v1 v2 = do h1<-lookupFuseEnv v1
>                          h2<-lookupFuseEnv v2
>                          let getIndex v h = maybe (error "fuseFuseEnv: this should not happen.") id $ elemIndex v (getNames h)
>                          lift (renameHT h1 h2)
> toListFuseEnv :: Env [([Variable],HyloT)]
> toListFuseEnv = get >>= return . toList . fst