module Simulation.Aivika.Trans.Operation
       (
        Operation,
        newOperation,
        newPreemptibleOperation,
        
        operationProcess,
        
        operationTotalUtilisationTime,
        operationTotalPreemptionTime,
        operationUtilisationTime,
        operationPreemptionTime,
        operationUtilisationFactor,
        operationPreemptionFactor,
        
        operationSummary,
        
        operationTotalUtilisationTimeChanged,
        operationTotalUtilisationTimeChanged_,
        operationTotalPreemptionTimeChanged,
        operationTotalPreemptionTimeChanged_,
        operationUtilisationTimeChanged,
        operationUtilisationTimeChanged_,
        operationPreemptionTimeChanged,
        operationPreemptionTimeChanged_,
        operationUtilisationFactorChanged,
        operationUtilisationFactorChanged_,
        operationPreemptionFactorChanged,
        operationPreemptionFactorChanged_,
        
        operationUtilising,
        operationUtilised,
        operationPreemptionBeginning,
        operationPreemptionEnding,
        
        operationChanged_) where
import Data.Monoid
import Control.Monad
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.Internal.Specs
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Signal
import Simulation.Aivika.Trans.Cont
import Simulation.Aivika.Trans.Process
import Simulation.Aivika.Trans.Activity
import Simulation.Aivika.Trans.Server
import Simulation.Aivika.Trans.Statistics
data Operation m a b =
  Operation { operationInitProcess :: a -> Process m b,
              
              operationProcessPreemptible :: Bool,
              
              operationStartTime :: Double,
              
              operationLastTimeRef :: Ref m Double,
              
              operationTotalUtilisationTimeRef :: Ref m Double,
              
              operationTotalPreemptionTimeRef :: Ref m Double,
              
              operationUtilisationTimeRef :: Ref m (SamplingStats Double),
              
              operationPreemptionTimeRef :: Ref m (SamplingStats Double),
              
              operationUtilisingSource :: SignalSource m a,
              
              operationUtilisedSource :: SignalSource m (a, b),
              
              operationPreemptionBeginningSource :: SignalSource m a,
              
              operationPreemptionEndingSource :: SignalSource m a
              
           }
newOperation :: MonadDES m
                => (a -> Process m b)
                
                -> Event m (Operation m a b)
newOperation = newPreemptibleOperation False
newPreemptibleOperation :: MonadDES m
                           => Bool
                           
                           -> (a -> Process m b)
                           
                           -> Event m (Operation m a b)
newPreemptibleOperation preemptible provide =
  do t0 <- liftDynamics time
     r0 <- liftSimulation $ newRef t0
     r1 <- liftSimulation $ newRef 0
     r2 <- liftSimulation $ newRef 0
     r3 <- liftSimulation $ newRef emptySamplingStats
     r4 <- liftSimulation $ newRef emptySamplingStats
     s1 <- liftSimulation newSignalSource
     s2 <- liftSimulation newSignalSource
     s3 <- liftSimulation newSignalSource
     s4 <- liftSimulation newSignalSource
     return Operation { operationInitProcess = provide,
                        operationProcessPreemptible = preemptible,
                        operationStartTime = t0,
                        operationLastTimeRef = r0,
                        operationTotalUtilisationTimeRef = r1,
                        operationTotalPreemptionTimeRef = r2,
                        operationUtilisationTimeRef = r3,
                        operationPreemptionTimeRef = r4,
                        operationUtilisingSource = s1,
                        operationUtilisedSource = s2,
                        operationPreemptionBeginningSource = s3,
                        operationPreemptionEndingSource = s4 }
operationProcess :: MonadDES m => Operation m a b -> a -> Process m b
operationProcess op a =
  do t0 <- liftDynamics time
     liftEvent $
       triggerSignal (operationUtilisingSource op) a
     
     (b, dt) <- if operationProcessPreemptible op
                then operationProcessPreempting op a
                else do b <- operationInitProcess op a
                        return (b, 0)
     t1 <- liftDynamics time
     liftEvent $
       do modifyRef (operationTotalUtilisationTimeRef op) (+ (t1  t0  dt))
          modifyRef (operationUtilisationTimeRef op) $
            addSamplingStats (t1  t0  dt)
          writeRef (operationLastTimeRef op) t1
          triggerSignal (operationUtilisedSource op) (a, b)
     return b
operationProcessPreempting :: MonadDES m => Operation m a b -> a -> Process m (b, Double)
operationProcessPreempting op a =
  do pid <- processId
     t0  <- liftDynamics time
     rs  <- liftSimulation $ newRef 0
     r0  <- liftSimulation $ newRef t0
     h1  <- liftEvent $
            handleSignal (processPreemptionBeginning pid) $ \() ->
            do t0 <- liftDynamics time
               writeRef r0 t0
               triggerSignal (operationPreemptionBeginningSource op) a
     h2  <- liftEvent $
            handleSignal (processPreemptionEnding pid) $ \() ->
            do t0 <- readRef r0
               t1 <- liftDynamics time
               let dt = t1  t0
               modifyRef rs (+ dt)
               modifyRef (operationTotalPreemptionTimeRef op) (+ dt)
               modifyRef (operationPreemptionTimeRef op) $
                 addSamplingStats dt
               writeRef (operationLastTimeRef op) t1
               triggerSignal (operationPreemptionEndingSource op) a 
     let m1 =
           do b <- operationInitProcess op a
              dt <- liftEvent $ readRef rs
              return (b, dt)
         m2 =
           liftEvent $
           do disposeEvent h1
              disposeEvent h2
     finallyProcess m1 m2
operationTotalUtilisationTime :: MonadDES m => Operation m a b -> Event m Double
operationTotalUtilisationTime op =
  Event $ \p -> invokeEvent p $ readRef (operationTotalUtilisationTimeRef op)
  
operationTotalUtilisationTimeChanged :: MonadDES m => Operation m a b -> Signal m Double
operationTotalUtilisationTimeChanged op =
  mapSignalM (const $ operationTotalUtilisationTime op) (operationTotalUtilisationTimeChanged_ op)
  
operationTotalUtilisationTimeChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationTotalUtilisationTimeChanged_ op =
  mapSignal (const ()) (operationUtilised op)
operationTotalPreemptionTime :: MonadDES m => Operation m a b -> Event m Double
operationTotalPreemptionTime op =
  Event $ \p -> invokeEvent p $ readRef (operationTotalPreemptionTimeRef op)
  
operationTotalPreemptionTimeChanged :: MonadDES m => Operation m a b -> Signal m Double
operationTotalPreemptionTimeChanged op =
  mapSignalM (const $ operationTotalPreemptionTime op) (operationTotalPreemptionTimeChanged_ op)
  
operationTotalPreemptionTimeChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationTotalPreemptionTimeChanged_ op =
  mapSignal (const ()) (operationPreemptionEnding op)
operationUtilisationTime :: MonadDES m => Operation m a b -> Event m (SamplingStats Double)
operationUtilisationTime op =
  Event $ \p -> invokeEvent p $ readRef (operationUtilisationTimeRef op)
  
operationUtilisationTimeChanged :: MonadDES m => Operation m a b -> Signal m (SamplingStats Double)
operationUtilisationTimeChanged op =
  mapSignalM (const $ operationUtilisationTime op) (operationUtilisationTimeChanged_ op)
  
operationUtilisationTimeChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationUtilisationTimeChanged_ op =
  mapSignal (const ()) (operationUtilised op)
operationPreemptionTime :: MonadDES m => Operation m a b -> Event m (SamplingStats Double)
operationPreemptionTime op =
  Event $ \p -> invokeEvent p $ readRef (operationPreemptionTimeRef op)
  
operationPreemptionTimeChanged :: MonadDES m => Operation m a b -> Signal m (SamplingStats Double)
operationPreemptionTimeChanged op =
  mapSignalM (const $ operationPreemptionTime op) (operationPreemptionTimeChanged_ op)
  
operationPreemptionTimeChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationPreemptionTimeChanged_ op =
  mapSignal (const ()) (operationPreemptionEnding op)
  
operationUtilisationFactor :: MonadDES m => Operation m a b -> Event m Double
operationUtilisationFactor op =
  Event $ \p ->
  do let t0 = operationStartTime op
     t1 <- invokeEvent p $ readRef (operationLastTimeRef op)
     x  <- invokeEvent p $ readRef (operationTotalUtilisationTimeRef op)
     return (x / (t1  t0))
  
operationUtilisationFactorChanged :: MonadDES m => Operation m a b -> Signal m Double
operationUtilisationFactorChanged op =
  mapSignalM (const $ operationUtilisationFactor op) (operationUtilisationFactorChanged_ op)
  
operationUtilisationFactorChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationUtilisationFactorChanged_ op =
  mapSignal (const ()) (operationUtilised op) <>
  mapSignal (const ()) (operationPreemptionEnding op)
  
operationPreemptionFactor :: MonadDES m => Operation m a b -> Event m Double
operationPreemptionFactor op =
  Event $ \p ->
  do let t0 = operationStartTime op
     t1 <- invokeEvent p $ readRef (operationLastTimeRef op)
     x  <- invokeEvent p $ readRef (operationTotalPreemptionTimeRef op)
     return (x / (t1  t0))
  
operationPreemptionFactorChanged :: MonadDES m => Operation m a b -> Signal m Double
operationPreemptionFactorChanged op =
  mapSignalM (const $ operationPreemptionFactor op) (operationPreemptionFactorChanged_ op)
  
operationPreemptionFactorChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationPreemptionFactorChanged_ op =
  mapSignal (const ()) (operationUtilised op) <>
  mapSignal (const ()) (operationPreemptionEnding op)
  
operationUtilising :: MonadDES m => Operation m a b -> Signal m a
operationUtilising = publishSignal . operationUtilisingSource
operationUtilised :: MonadDES m => Operation m a b -> Signal m (a, b)
operationUtilised = publishSignal . operationUtilisedSource
operationPreemptionBeginning :: MonadDES m => Operation m a b -> Signal m a
operationPreemptionBeginning = publishSignal . operationPreemptionBeginningSource
operationPreemptionEnding :: MonadDES m => Operation m a b -> Signal m a
operationPreemptionEnding = publishSignal . operationPreemptionEndingSource
operationChanged_ :: MonadDES m => Operation m a b -> Signal m ()
operationChanged_ op =
  mapSignal (const ()) (operationUtilising op) <>
  mapSignal (const ()) (operationUtilised op) <>
  mapSignal (const ()) (operationPreemptionEnding op)
operationSummary :: MonadDES m => Operation m a b -> Int -> Event m ShowS
operationSummary op indent =
  Event $ \p ->
  do let t0 = operationStartTime op
     t1  <- invokeEvent p $ readRef (operationLastTimeRef op)
     tx1 <- invokeEvent p $ readRef (operationTotalUtilisationTimeRef op)
     tx2 <- invokeEvent p $ readRef (operationTotalPreemptionTimeRef op)
     let xf1 = tx1 / (t1  t0)
         xf2 = tx2 / (t1  t0)
     xs1 <- invokeEvent p $ readRef (operationUtilisationTimeRef op)
     xs2 <- invokeEvent p $ readRef (operationPreemptionTimeRef op)
     let tab = replicate indent ' '
     return $
       showString tab .
       showString "total utilisation time = " . shows tx1 .
       showString "\n" .
       showString tab .
       showString "total preemption time = " . shows tx2 .
       showString "\n" .
       showString tab .
       showString "utilisation factor (from 0 to 1) = " . shows xf1 .
       showString "\n" .
       showString tab .
       showString "preemption factor (from 0 to 1) = " . shows xf2 .
       showString "\n" .
       showString tab .
       showString "utilisation time:\n\n" .
       samplingStatsSummary xs1 (2 + indent) .
       showString "\n\n" .
       showString tab .
       showString "preemption time:\n\n" .
       samplingStatsSummary xs2 (2 + indent)