#ifndef MIN_VERSION_mtl
#define MIN_VERSION_mtl(x,y,z) 0
#endif
module Data.Machine.Plan
  (
  
    Plan
  , runPlan
  , PlanT(..)
  , yield
  , await
  , stop
  , awaits
  ) where
import Control.Applicative
import Control.Category
import Control.Monad (ap, MonadPlus(..))
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import Control.Monad.State.Class
import Control.Monad.Reader.Class
import Control.Monad.Error.Class
import Control.Monad.Writer.Class
import Data.Functor.Identity
import Prelude hiding ((.),id)
newtype PlanT k o m a = PlanT
  { runPlanT :: forall r.
      (a -> m r) ->                                     
      (o -> m r -> m r) ->                              
      (forall z. (z -> m r) -> k z -> m r -> m r) ->  
      m r ->                                            
      m r
  }
type Plan k o a = forall m. PlanT k o m a
runPlan :: PlanT k o Identity a
        -> (a -> r)
        -> (o -> r -> r)
        -> (forall z. (z -> r) -> k z -> r -> r)
        -> r
        -> r
runPlan m kp ke kr kf = runIdentity $ runPlanT m
  (Identity . kp)
  (\o (Identity r) -> Identity (ke o r))
  (\f k (Identity r) -> Identity (kr (runIdentity . f) k r))
  (Identity kf)
instance Functor (PlanT k o m) where
  fmap f (PlanT m) = PlanT $ \k -> m (k . f)
  
instance Applicative (PlanT k o m) where
  pure a = PlanT (\kp _ _ _ -> kp a)
  
  (<*>) = ap
  
instance Alternative (PlanT k o m) where
  empty = PlanT $ \_ _ _ kf -> kf
  
  PlanT m <|> PlanT n = PlanT $ \kp ke kr kf -> m kp ke (\ks kir _ -> kr ks kir (n kp ke kr kf)) (n kp ke kr kf)
  
instance Monad (PlanT k o m) where
  return a = PlanT (\kp _ _ _ -> kp a)
  
  PlanT m >>= f = PlanT (\kp ke kr kf -> m (\a -> runPlanT (f a) kp ke kr kf) ke kr kf)
  fail _ = PlanT (\_ _ _ kf -> kf)
  
instance MonadPlus (PlanT k o m) where
  mzero = empty
  
  mplus = (<|>)
  
instance MonadTrans (PlanT k o) where
  lift m = PlanT (\kp _ _ _ -> m >>= kp)
  
instance MonadIO m => MonadIO (PlanT k o m) where
  liftIO m = PlanT (\kp _ _ _ -> liftIO m >>= kp)
  
instance MonadState s m => MonadState s (PlanT k o m) where
  get = lift get
  
  put = lift . put
  
#ifdef MIN_VERSION_mtl(2,1,0)
  state f = PlanT $ \kp _ _ _ -> state f >>= kp
  
#endif
instance MonadReader e m => MonadReader e (PlanT k o m) where
  ask = lift ask
#ifdef MIN_VERSION_mtl(2,1,0)
  reader = lift . reader
#endif
  local f m = PlanT $ \kp ke kr kf -> local f (runPlanT m kp ke kr kf)
instance MonadWriter w m  => MonadWriter w (PlanT k o m) where
#ifdef MIN_VERSION_mtl(2,1,0)
  writer = lift . writer
#endif
  tell   = lift . tell
  listen m = PlanT $ \kp ke kr kf -> runPlanT m ((kp =<<) . listen . return) ke kr kf
  pass m = PlanT $ \kp ke kr kf -> runPlanT m ((kp =<<) . pass . return) ke kr kf
instance MonadError e m => MonadError e (PlanT k o m) where
  throwError = lift . throwError
  catchError m k = PlanT $ \kp ke kr kf -> runPlanT m kp ke kr kf `catchError` \e -> runPlanT (k e) kp ke kr kf
yield :: o -> Plan k o ()
yield o = PlanT (\kp ke _ _ -> ke o (kp ()))
await :: Category k => Plan (k i) o i
await = PlanT (\kp _ kr kf -> kr kp id kf)
awaits :: k i -> Plan k o i
awaits h = PlanT $ \kp _ kr -> kr kp h
stop :: Plan k o a
stop = empty