module Util.UniqueMonad(UniqT,Uniq, runUniq, runUniqT, execUniq1, execUniq, execUniqT) where


import GenUtil
import Data.Unique
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Identity


instance UniqueProducer IO where
    newUniq = do
        u <- newUnique
        return $ hashUnique u

instance Monad m =>  UniqueProducer (UniqT m) where
    newUniq = UniqT $ do
        modify (+1)
        get

-- | Run the transformer version of the unique int generator.
runUniqT :: Monad m =>  UniqT m a -> Int -> m (a,Int)
runUniqT (UniqT sm) s  = runStateT sm s

-- | Run the bare version of the unique int generator.
runUniq :: Int -> Uniq a -> (a,Int)
runUniq x y = runIdentity $ runUniqT y x

-- | Execute the bare unique int generator starting with 1.
execUniq1 :: Uniq a -> a
execUniq1 x = fst $ runUniq 1 x

-- | Execute the bare unique int generator starting with the suplied number.
execUniq :: Int -> Uniq a -> a
execUniq st x = fst $ runUniq st x

-- | Execute the transformer version of the unique int generator starting with the suplied number.
execUniqT :: Monad m =>  Int -> UniqT m a -> m a
execUniqT s (UniqT sm)  = liftM fst $ runStateT sm s

instance (Monad m, Monad (t m), MonadTrans t, UniqueProducer m) => UniqueProducer (t m) where
    newUniq = lift newUniq

-- | Unique integer generator monad transformer.
newtype UniqT m a = UniqT (StateT Int m a)
    deriving(Monad,  MonadTrans, Functor, MonadFix, MonadPlus)

instance MonadReader s m => MonadReader s (UniqT m) where
    ask = UniqT $  ask
    local f (UniqT x) = UniqT $ local f x

-- | Unique integer generator monad.
type Uniq = UniqT Identity