module Simulation.Aivika.Trans.Net
       (
        Net(..),
        iterateNet,
        iterateNetMaybe,
        iterateNetEither,
        
        emptyNet,
        arrNet,
        accumNet,
        withinNet,
        
        netUsingId,
        
        arrivalNet,
        
        delayNet,
        
        netProcessor,
        processorNet,
        
        traceNet) where
import qualified Control.Category as C
import Control.Arrow
import Control.Monad.Trans
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Parameter
import Simulation.Aivika.Trans.Simulation
import Simulation.Aivika.Trans.Dynamics
import Simulation.Aivika.Trans.Event
import Simulation.Aivika.Trans.Cont
import Simulation.Aivika.Trans.Process
import Simulation.Aivika.Trans.Stream
import Simulation.Aivika.Trans.QueueStrategy
import Simulation.Aivika.Trans.Resource.Base
import Simulation.Aivika.Trans.Processor
import Simulation.Aivika.Trans.Circuit
import Simulation.Aivika.Arrival (Arrival(..))
newtype Net m a b =
  Net { runNet :: a -> Process m (b, Net m a b)
        
      }
instance MonadDES m => C.Category (Net m) where
  
  id = Net $ \a -> return (a, C.id)
  
  (.) = dot
    where 
      (Net g) `dot` (Net f) =
        Net $ \a ->
        do (b, p1) <- f a
           (c, p2) <- g b
           return (c, p2 `dot` p1)
instance MonadDES m => Arrow (Net m) where
  
  arr f = Net $ \a -> return (f a, arr f)
  
  first (Net f) =
    Net $ \(b, d) ->
    do (c, p) <- f b
       return ((c, d), first p)
  
  second (Net f) =
    Net $ \(d, b) ->
    do (c, p) <- f b
       return ((d, c), second p)
  
  (Net f) *** (Net g) =
    Net $ \(b, b') ->
    do (c, p1) <- f b
       (c', p2) <- g b'
       return ((c, c'), p1 *** p2)
       
  
  (Net f) &&& (Net g) =
    Net $ \b ->
    do (c, p1) <- f b
       (c', p2) <- g b
       return ((c, c'), p1 &&& p2)
instance MonadDES m => ArrowChoice (Net m) where
  
  left x@(Net f) =
    Net $ \ebd ->
    case ebd of
      Left b ->
        do (c, p) <- f b
           return (Left c, left p)
      Right d ->
        return (Right d, left x)
  
  right x@(Net f) =
    Net $ \edb ->
    case edb of
      Right b ->
        do (c, p) <- f b
           return (Right c, right p)
      Left d ->
        return (Left d, right x)
  
  x@(Net f) +++ y@(Net g) =
    Net $ \ebb' ->
    case ebb' of
      Left b ->
        do (c, p1) <- f b
           return (Left c, p1 +++ y)
      Right b' ->
        do (c', p2) <- g b'
           return (Right c', x +++ p2)
  
  x@(Net f) ||| y@(Net g) =
    Net $ \ebc ->
    case ebc of
      Left b ->
        do (d, p1) <- f b
           return (d, p1 ||| y)
      Right b' ->
        do (d, p2) <- g b'
           return (d, x ||| p2)
emptyNet :: MonadDES m => Net m a b
emptyNet = Net $ const neverProcess
arrNet :: MonadDES m => (a -> Process m b) -> Net m a b
arrNet f =
  let x =
        Net $ \a ->
        do b <- f a
           return (b, x)
  in x
accumNet :: MonadDES m => (acc -> a -> Process m (acc, b)) -> acc -> Net m a b
accumNet f acc =
  Net $ \a ->
  do (acc', b) <- f acc a
     return (b, accumNet f acc') 
withinNet :: MonadDES m => Process m () -> Net m a a
withinNet m =
  Net $ \a ->
  do { m; return (a, withinNet m) }
netUsingId :: MonadDES m => ProcessId m -> Net m a b -> Net m a b
netUsingId pid (Net f) =
  Net $ processUsingId pid . f
netProcessor :: MonadDES m => Net m a b -> Processor m a b
netProcessor = Processor . loop
  where loop x as =
          Cons $
          do (a, as') <- runStream as
             (b, x') <- runNet x a
             return (b, loop x' as')
processorNet :: MonadDES m => Processor m a b -> Net m a b
processorNet x =
  Net $ \a ->
  do readingA <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1)
     writingA <- liftSimulation $ newResourceWithMaxCount FCFS 1 (Just 1)
     readingB <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1)
     writingB <- liftSimulation $ newResourceWithMaxCount FCFS 1 (Just 1)
     conting  <- liftSimulation $ newResourceWithMaxCount FCFS 0 (Just 1)
     refA <- liftSimulation $ newRef Nothing
     refB <- liftSimulation $ newRef Nothing
     let input =
           do requestResource readingA
              Just a <- liftEvent $ readRef refA
              liftEvent $ writeRef refA Nothing
              releaseResource writingA
              return (a, Cons input)
         consume bs =
           do (b, bs') <- runStream bs
              requestResource writingB
              liftEvent $ writeRef refB (Just b)
              releaseResource readingB
              requestResource conting
              consume bs'
         loop a =
           do requestResource writingA
              liftEvent $ writeRef refA (Just a)
              releaseResource readingA
              requestResource readingB
              Just b <- liftEvent $ readRef refB
              liftEvent $ writeRef refB Nothing
              releaseResource writingB
              return (b, Net $ \a -> releaseResource conting >> loop a)
     spawnProcess $
       consume $ runProcessor x (Cons input)
     loop a
arrivalNet :: MonadDES m => Net m a (Arrival a)
arrivalNet =
  let loop t0 =
        Net $ \a ->
        do t <- liftDynamics time
           let b = Arrival { arrivalValue = a,
                             arrivalTime  = t,
                             arrivalDelay = 
                               case t0 of
                                 Nothing -> Nothing
                                 Just t0 -> Just (t  t0) }
           return (b, loop $ Just t)
  in loop Nothing
delayNet :: MonadDES m => a -> Net m a a
delayNet a0 =
  Net $ \a ->
  return (a0, delayNet a)
iterateNet :: MonadDES m => Net m a a -> a -> Process m ()
iterateNet (Net f) a =
  do (a', x) <- f a
     iterateNet x a'
iterateNetMaybe :: MonadDES m => Net m a (Maybe a) -> a -> Process m ()
iterateNetMaybe (Net f) a =
  do (a', x) <- f a
     case a' of
       Nothing -> return ()
       Just a' -> iterateNetMaybe x a'
iterateNetEither :: MonadDES m => Net m a (Either b a) -> a -> Process m b
iterateNetEither (Net f) a =
  do (ba', x) <- f a
     case ba' of
       Left b'  -> return b'
       Right a' -> iterateNetEither x a'
traceNet :: MonadDES m
            => Maybe String
            
            -> Maybe String
            
            -> Net m a b
            
            -> Net m a b
traceNet request response x = Net $ loop x where
  loop x a =
    do (b, x') <-
         case request of
           Nothing -> runNet x a
           Just message -> 
             traceProcess message $
             runNet x a
       case response of
         Nothing -> return (b, Net $ loop x')
         Just message ->
           traceProcess message $
           return (b, Net $ loop x')