{-# LANGUAGE FlexibleInstances #-}

-- |
-- Module     : Simulation.Aivika.Results.Transform
-- Copyright  : Copyright (c) 2009-2017, David Sorokin <david.sorokin@gmail.com>
-- License    : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- Stability  : experimental
-- Tested with: GHC 8.0.1
--
-- The module defines useful result transformations that can
-- be used in simulation experiments.
--
module Simulation.Aivika.Results.Transform
       (-- * Basic Class Type
        ResultTransformer(..),
        -- * Sampling Statistics
        SamplingStats(..),
        samplingStatsCount,
        samplingStatsMin,
        samplingStatsMax,
        samplingStatsMean,
        samplingStatsMean2,
        samplingStatsVariance,
        samplingStatsDeviation,
        -- * Time-dependent Statistics
        TimingStats(..),
        timingStatsCount,
        timingStatsMin,
        timingStatsMax,
        timingStatsMean,
        timingStatsVariance,
        timingStatsDeviation,
        timingStatsMinTime,
        timingStatsMaxTime,
        timingStatsStartTime,
        timingStatsLastTime,
        timingStatsSum,
        timingStatsSum2,
        -- * Sampling-based Counter
        SamplingCounter(..),
        samplingCounterValue,
        samplingCounterStats,
        -- * Time-dependent Counter
        TimingCounter(..),
        timingCounterValue,
        timingCounterStats,
        -- * Queue
        Queue(..),
        enqueueStrategy,
        enqueueStoringStrategy,
        dequeueStrategy,
        queueNull,
        queueFull,
        queueMaxCount,
        queueCount,
        queueCountStats,
        enqueueCount,
        enqueueLostCount,
        enqueueStoreCount,
        dequeueCount,
        dequeueExtractCount,
        queueLoadFactor,
        enqueueRate,
        enqueueStoreRate,
        dequeueRate,
        dequeueExtractRate,
        queueWaitTime,
        queueTotalWaitTime,
        enqueueWaitTime,
        dequeueWaitTime,
        queueRate,
        -- * Arrival Timer
        ArrivalTimer(..),
        arrivalProcessingTime,
        -- * Server
        Server(..),
        serverInitState,
        serverState,
        serverTotalInputWaitTime,
        serverTotalProcessingTime,
        serverTotalOutputWaitTime,
        serverTotalPreemptionTime,
        serverInputWaitTime,
        serverProcessingTime,
        serverOutputWaitTime,
        serverPreemptionTime,
        serverInputWaitFactor,
        serverProcessingFactor,
        serverOutputWaitFactor,
        serverPreemptionFactor,
        -- * Activity
        Activity(..),
        activityInitState,
        activityState,
        activityTotalUtilisationTime,
        activityTotalIdleTime,
        activityTotalPreemptionTime,
        activityUtilisationTime,
        activityIdleTime,
        activityPreemptionTime,
        activityUtilisationFactor,
        activityIdleFactor,
        activityPreemptionFactor,
        -- * Resource
        Resource(..),
        resourceCount,
        resourceCountStats,
        resourceUtilisationCount,
        resourceUtilisationCountStats,
        resourceQueueCount,
        resourceQueueCountStats,
        resourceTotalWaitTime,
        resourceWaitTime,
        -- * Operation
        Operation(..),
        operationTotalUtilisationTime,
        operationTotalPreemptionTime,
        operationUtilisationTime,
        operationPreemptionTime,
        operationUtilisationFactor,
        operationPreemptionFactor) where

import Control.Arrow

import Simulation.Aivika.Results
import Simulation.Aivika.Results.Locale

-- | Something that can transform the results.
class ResultTransformer a where

  -- | Return the result transform.
  tr :: a -> ResultTransform

-- | Represents a statistics based upon observations.
newtype SamplingStats = SamplingStats ResultTransform

instance ResultTransformer SamplingStats where
  tr (SamplingStats a) = a

-- | The total number of samples.
samplingStatsCount :: SamplingStats -> ResultTransform
samplingStatsCount (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsCountId

-- | The minimum value among the samples.
samplingStatsMin :: SamplingStats -> ResultTransform
samplingStatsMin (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsMinId

-- | The maximum value among the samples.
samplingStatsMax :: SamplingStats -> ResultTransform
samplingStatsMax (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsMaxId

-- | The average value.
samplingStatsMean :: SamplingStats -> ResultTransform
samplingStatsMean (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsMeanId

-- | The average square value.
samplingStatsMean2 :: SamplingStats -> ResultTransform
samplingStatsMean2 (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsMean2Id

-- | Return tha variance.
samplingStatsVariance :: SamplingStats -> ResultTransform
samplingStatsVariance (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsVarianceId

-- | Return the deviation.
samplingStatsDeviation :: SamplingStats -> ResultTransform
samplingStatsDeviation (SamplingStats a) =
  a >>> expandResults >>> resultById SamplingStatsDeviationId

-- | A counter for which the statistics is collected too.
newtype SamplingCounter = SamplingCounter ResultTransform

instance ResultTransformer SamplingCounter where
  tr (SamplingCounter a) = a

-- | The counter value.
samplingCounterValue :: SamplingCounter -> ResultTransform
samplingCounterValue (SamplingCounter a) =
  a >>> resultById SamplingCounterValueId

-- | The counter statistics.
samplingCounterStats :: SamplingCounter -> SamplingStats
samplingCounterStats (SamplingCounter a) =
  SamplingStats (a >>> resultById SamplingCounterStatsId)

-- | The time-dependent statistics.
newtype TimingStats = TimingStats ResultTransform

instance ResultTransformer TimingStats where
  tr (TimingStats a) = a

-- | Return the number of samples.
timingStatsCount :: TimingStats -> ResultTransform
timingStatsCount (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsCountId

-- | Return the minimum value.
timingStatsMin :: TimingStats -> ResultTransform
timingStatsMin (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsMinId

-- | Return the maximum value.
timingStatsMax :: TimingStats -> ResultTransform
timingStatsMax (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsMaxId

-- | Return the average value.
timingStatsMean :: TimingStats -> ResultTransform
timingStatsMean (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsMeanId

-- | Return the variance.
timingStatsVariance :: TimingStats -> ResultTransform
timingStatsVariance (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsVarianceId

-- | Return the deviation.
timingStatsDeviation :: TimingStats -> ResultTransform
timingStatsDeviation (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsDeviationId

-- | Return the time at which the minimum is attained.
timingStatsMinTime :: TimingStats -> ResultTransform
timingStatsMinTime (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsMinTimeId

-- | Return the time at which the maximum is attained.
timingStatsMaxTime :: TimingStats -> ResultTransform
timingStatsMaxTime (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsMaxTimeId

-- | Return the start time of sampling.
timingStatsStartTime :: TimingStats -> ResultTransform
timingStatsStartTime (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsStartTimeId

-- | Return the last time of sampling.
timingStatsLastTime :: TimingStats -> ResultTransform
timingStatsLastTime (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsLastTimeId

-- | Return the sum of values.
timingStatsSum :: TimingStats -> ResultTransform
timingStatsSum (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsSumId

-- | Return the sum of square values.
timingStatsSum2 :: TimingStats -> ResultTransform
timingStatsSum2 (TimingStats a) =
  a >>> expandResults >>> resultById TimingStatsSum2Id

-- | A time-dependent counter that collects the statistics too.
newtype TimingCounter = TimingCounter ResultTransform

instance ResultTransformer TimingCounter where
  tr (TimingCounter a) = a

-- | The counter value.
timingCounterValue :: TimingCounter -> ResultTransform
timingCounterValue (TimingCounter a) =
  a >>> resultById TimingCounterValueId

-- | The counter statistics.
timingCounterStats :: TimingCounter -> TimingStats
timingCounterStats (TimingCounter a) =
  TimingStats (a >>> resultById TimingCounterStatsId)

-- | Represents either finite or infinite queue.
newtype Queue = Queue ResultTransform

instance ResultTransformer Queue where
  tr (Queue a) = a

-- | The strategy applied to the enqueueing (input) processes when the finite queue is full.
enqueueStrategy :: Queue -> ResultTransform
enqueueStrategy (Queue a) =
  a >>> resultById EnqueueStrategyId

-- | The strategy applied when storing (in memory) items in the queue.
enqueueStoringStrategy :: Queue -> ResultTransform
enqueueStoringStrategy (Queue a) =
  a >>> resultById EnqueueStoringStrategyId

-- | The strategy applied to the dequeueing (output) processes when the queue is empty.
dequeueStrategy :: Queue -> ResultTransform
dequeueStrategy (Queue a) =
  a >>> resultById DequeueStrategyId

-- | Test whether the queue is empty.
queueNull :: Queue -> ResultTransform
queueNull (Queue a) =
  a >>> resultById QueueNullId

-- | Test whether the finite queue is full.
queueFull :: Queue -> ResultTransform
queueFull (Queue a) =
  a >>> resultById QueueFullId

-- | The finite queue capacity.
queueMaxCount :: Queue -> ResultTransform
queueMaxCount (Queue a) =
  a >>> resultById QueueMaxCountId

-- | Return the current queue size.
queueCount :: Queue -> ResultTransform
queueCount (Queue a) =
  a >>> resultById QueueCountId

-- | Return the queue size statistics.
queueCountStats :: Queue -> TimingStats
queueCountStats (Queue a) =
  TimingStats (a >>> resultById QueueCountStatsId)

-- | Return the total number of input items that were enqueued in the finite queue.
enqueueCount :: Queue -> ResultTransform
enqueueCount (Queue a) =
  a >>> resultById EnqueueCountId

-- | Return the number of lost items for the finite queue.
enqueueLostCount :: Queue -> ResultTransform
enqueueLostCount (Queue a) =
  a >>> resultById EnqueueLostCountId

-- | Return the total number of input items that were stored.
enqueueStoreCount :: Queue -> ResultTransform
enqueueStoreCount (Queue a) =
  a >>> resultById EnqueueStoreCountId

-- | Return the total number of requests for dequeueing the items, not taking
-- into account the failed attempts to dequeue immediately without suspension.
dequeueCount :: Queue -> ResultTransform
dequeueCount (Queue a) =
  a >>> resultById DequeueCountId

-- | Return the total number of output items that were actually dequeued.
dequeueExtractCount :: Queue -> ResultTransform
dequeueExtractCount (Queue a) =
  a >>> resultById DequeueExtractCountId

-- | Return the load factor: the finite queue size divided by its capacity.
queueLoadFactor :: Queue -> ResultTransform
queueLoadFactor (Queue a) =
  a >>> resultById QueueLoadFactorId

-- | Return the rate of the input items that were enqueued in the finite queue:
-- how many items per time.
enqueueRate :: Queue -> ResultTransform
enqueueRate (Queue a) =
  a >>> resultById EnqueueRateId

-- | Return the rate of the items that were stored: how many items per time.
enqueueStoreRate :: Queue -> ResultTransform
enqueueStoreRate (Queue a) =
  a >>> resultById EnqueueStoreRateId

-- | Return the rate of the requests for dequeueing the items: how many
-- requests per time. It does not include the failed attempts to dequeue
-- immediately without suspension.
dequeueRate :: Queue -> ResultTransform
dequeueRate (Queue a) =
  a >>> resultById DequeueRateId

-- | Return the rate of the output items that were dequeued: how many items per time.
dequeueExtractRate :: Queue -> ResultTransform
dequeueExtractRate (Queue a) =
  a >>> resultById DequeueExtractRateId

-- | Return the wait time from the time at which the item was stored in
-- the queue to the time at which it was dequeued.
queueWaitTime :: Queue -> SamplingStats
queueWaitTime (Queue a) =
  SamplingStats (a >>> resultById QueueWaitTimeId)

-- | Return the total wait time for the finite queue from the time at which
-- the enqueueing operation was initiated to the time at which the item was dequeued.
queueTotalWaitTime :: Queue -> SamplingStats
queueTotalWaitTime (Queue a) =
  SamplingStats (a >>> resultById QueueTotalWaitTimeId)

-- | Return the wait time from the time at which the item was stored in
-- the queue to the time at which it was dequeued.
enqueueWaitTime :: Queue -> SamplingStats
enqueueWaitTime (Queue a) =
  SamplingStats (a >>> resultById EnqueueWaitTimeId)

-- | Return the dequeue wait time from the time at which the item was requested
-- for dequeueing to the time at which it was actually dequeued.
dequeueWaitTime :: Queue -> SamplingStats
dequeueWaitTime (Queue a) =
  SamplingStats (a >>> resultById DequeueWaitTimeId)

-- | Return a long-term average queue rate calculated as the average queue size
-- divided by the average wait time.
queueRate :: Queue -> ResultTransform
queueRate (Queue a) =
  a >>> resultById QueueRateId

-- | Accumulates the statistics about that how long the arrived events are processed.
newtype ArrivalTimer = ArrivalTimer ResultTransform

instance ResultTransformer ArrivalTimer where
  tr (ArrivalTimer a) = a

-- | Return the statistics about that how long the arrived events were processed.
arrivalProcessingTime :: ArrivalTimer -> SamplingStats
arrivalProcessingTime (ArrivalTimer a) =
  SamplingStats (a >>> resultById ArrivalProcessingTimeId)

-- | It models the server that prodives a service.
newtype Server = Server ResultTransform

instance ResultTransformer Server where
  tr (Server a) = a

-- | The initial state of the server.
serverInitState :: Server -> ResultTransform
serverInitState (Server a) =
  a >>> resultById ServerInitStateId

-- | Return the current state of the server.
serverState :: Server -> ResultTransform
serverState (Server a) =
  a >>> resultById ServerStateId

-- | Return the counted total time when the server was locked while
-- awaiting the input.
serverTotalInputWaitTime :: Server -> ResultTransform
serverTotalInputWaitTime (Server a) =
  a >>> resultById ServerTotalInputWaitTimeId

-- | Return the counted total time spent by the server while
-- processing the tasks.
serverTotalProcessingTime :: Server -> ResultTransform
serverTotalProcessingTime (Server a) =
  a >>> resultById ServerTotalProcessingTimeId

-- | Return the counted total time when the server was locked while
-- trying to deliver the output.
serverTotalOutputWaitTime :: Server -> ResultTransform
serverTotalOutputWaitTime (Server a) =
  a >>> resultById ServerTotalOutputWaitTimeId

-- | Return the counted total time spent by the server while it was
-- preempted waiting for the further proceeding.
serverTotalPreemptionTime :: Server -> ResultTransform
serverTotalPreemptionTime (Server a) =
  a >>> resultById ServerTotalPreemptionTimeId

-- | Return the statistics of the time when the server was locked
-- while awaiting the input.
serverInputWaitTime :: Server -> SamplingStats
serverInputWaitTime (Server a) =
  SamplingStats (a >>> resultById ServerInputWaitTimeId)

-- | Return the statistics of the time spent by the server while
-- processing the tasks.
serverProcessingTime :: Server -> SamplingStats
serverProcessingTime (Server a) =
  SamplingStats (a >>> resultById ServerProcessingTimeId)

-- | Return the statistics of the time when the server was locked
-- while trying to deliver the output.
serverOutputWaitTime :: Server -> SamplingStats
serverOutputWaitTime (Server a) =
  SamplingStats (a >>> resultById ServerOutputWaitTimeId)

-- | Return the statistics of the time spent by the server while
-- it was preempted waiting for the further proceeding.
serverPreemptionTime :: Server -> SamplingStats
serverPreemptionTime (Server a) =
  SamplingStats (a >>> resultById ServerPreemptionTimeId)

-- | It returns the factor changing from 0 to 1, which estimates
-- how often the server was awaiting for the next input task.
serverInputWaitFactor :: Server -> ResultTransform
serverInputWaitFactor (Server a) =
  a >>> resultById ServerInputWaitFactorId

-- | It returns the factor changing from 0 to 1, which estimates
-- how often the server was busy with direct processing its tasks.
serverProcessingFactor :: Server -> ResultTransform
serverProcessingFactor (Server a) =
  a >>> resultById ServerProcessingFactorId

-- | It returns the factor changing from 0 to 1, which estimates
-- how often the server was locked trying to deliver the output
-- after the task is finished.
serverOutputWaitFactor :: Server -> ResultTransform
serverOutputWaitFactor (Server a) =
  a >>> resultById ServerOutputWaitFactorId

-- | It returns the factor changing from 0 to 1, which estimates
-- how often the server was preempted waiting for the further proceeding.
serverPreemptionFactor :: Server -> ResultTransform
serverPreemptionFactor (Server a) =
  a >>> resultById ServerPreemptionFactorId

-- | It models an activity that can be utilised.
newtype Activity = Activity ResultTransform

instance ResultTransformer Activity where
  tr (Activity a) = a

-- | The initial state of the activity.
activityInitState :: Activity -> ResultTransform
activityInitState (Activity a) =
  a >>> resultById ActivityInitStateId

-- | Return the current state of the activity.
activityState :: Activity -> ResultTransform
activityState (Activity a) =
  a >>> resultById ActivityStateId

-- | Return the counted total time when the activity was utilised.
activityTotalUtilisationTime :: Activity -> ResultTransform
activityTotalUtilisationTime (Activity a) =
  a >>> resultById ActivityTotalUtilisationTimeId

-- | Return the counted total time when the activity was idle.
activityTotalIdleTime :: Activity -> ResultTransform
activityTotalIdleTime (Activity a) =
  a >>> resultById ActivityTotalIdleTimeId

-- | Return the counted total time when the activity was preemted
-- waiting for the further proceeding.
activityTotalPreemptionTime :: Activity -> ResultTransform
activityTotalPreemptionTime (Activity a) =
  a >>> resultById ActivityTotalPreemptionTimeId

-- | Return the statistics for the time when the activity was utilised.
activityUtilisationTime :: Activity -> SamplingStats
activityUtilisationTime (Activity a) =
  SamplingStats (a >>> resultById ActivityUtilisationTimeId)

-- | Return the statistics for the time when the activity was idle.
activityIdleTime :: Activity -> SamplingStats
activityIdleTime (Activity a) =
  SamplingStats (a >>> resultById ActivityIdleTimeId)

-- | Return the statistics for the time when the activity was preempted
-- waiting for the further proceeding.
activityPreemptionTime :: Activity -> SamplingStats
activityPreemptionTime (Activity a) =
  SamplingStats (a >>> resultById ActivityPreemptionTimeId)

-- | It returns the factor changing from 0 to 1, which estimates how often
-- the activity was utilised.
activityUtilisationFactor :: Activity -> ResultTransform
activityUtilisationFactor (Activity a) =
  a >>> resultById ActivityUtilisationFactorId

-- | It returns the factor changing from 0 to 1, which estimates how often
-- the activity was idle.
activityIdleFactor :: Activity -> ResultTransform
activityIdleFactor (Activity a) =
  a >>> resultById ActivityIdleFactorId

-- | It returns the factor changing from 0 to 1, which estimates how often
-- the activity was preempted waiting for the further proceeding.
activityPreemptionFactor :: Activity -> ResultTransform
activityPreemptionFactor (Activity a) =
  a >>> resultById ActivityPreemptionFactorId

-- | The resource which can be acquired and then released.
newtype Resource = Resource ResultTransform

instance ResultTransformer Resource where
  tr (Resource a) = a

-- | Return the current available count of the resource.
resourceCount :: Resource -> ResultTransform
resourceCount (Resource a) =
  a >>> resultById ResourceCountId

-- | Return the statistics for the available count of the resource.
resourceCountStats :: Resource -> TimingStats
resourceCountStats (Resource a) =
  TimingStats (a >>> resultById ResourceCountStatsId)

-- | Return the current utilisation count of the resource.
resourceUtilisationCount :: Resource -> ResultTransform
resourceUtilisationCount (Resource a) =
  a >>> resultById ResourceUtilisationCountId

-- | Return the statistics for the utilisation count of the resource.
resourceUtilisationCountStats :: Resource -> TimingStats
resourceUtilisationCountStats (Resource a) =
  TimingStats (a >>> resultById ResourceUtilisationCountStatsId)

-- | Return the current queue length of the resource.
resourceQueueCount :: Resource -> ResultTransform
resourceQueueCount (Resource a) =
  a >>> resultById ResourceQueueCountId

-- | Return the statistics for the queue length of the resource.
resourceQueueCountStats :: Resource -> TimingStats
resourceQueueCountStats (Resource a) =
  TimingStats (a >>> resultById ResourceQueueCountStatsId)

-- | Return the total wait time of the resource.
resourceTotalWaitTime :: Resource -> ResultTransform
resourceTotalWaitTime (Resource a) =
  a >>> resultById ResourceTotalWaitTimeId

-- | Return the statistics for the wait time of the resource.
resourceWaitTime :: Resource -> SamplingStats
resourceWaitTime (Resource a) =
  SamplingStats (a >>> resultById ResourceWaitTimeId)

-- | It models an opreation which actvity can be utilised.
newtype Operation = Operation ResultTransform

instance ResultTransformer Operation where
  tr (Operation a) = a

-- | Return the counted total time when the operation activity was utilised.
operationTotalUtilisationTime :: Operation -> ResultTransform
operationTotalUtilisationTime (Operation a) =
  a >>> resultById OperationTotalUtilisationTimeId

-- | Return the counted total time when the operation activity was preemted
-- waiting for the further proceeding.
operationTotalPreemptionTime :: Operation -> ResultTransform
operationTotalPreemptionTime (Operation a) =
  a >>> resultById OperationTotalPreemptionTimeId

-- | Return the statistics for the time when the operation activity was utilised.
operationUtilisationTime :: Operation -> SamplingStats
operationUtilisationTime (Operation a) =
  SamplingStats (a >>> resultById OperationUtilisationTimeId)

-- | Return the statistics for the time when the operation activity was preempted
-- waiting for the further proceeding.
operationPreemptionTime :: Operation -> SamplingStats
operationPreemptionTime (Operation a) =
  SamplingStats (a >>> resultById OperationPreemptionTimeId)

-- | It returns the factor changing from 0 to 1, which estimates how often
-- the operation activity was utilised.
operationUtilisationFactor :: Operation -> ResultTransform
operationUtilisationFactor (Operation a) =
  a >>> resultById OperationUtilisationFactorId

-- | It returns the factor changing from 0 to 1, which estimates how often
-- the operation activity was preempted waiting for the further proceeding.
operationPreemptionFactor :: Operation -> ResultTransform
operationPreemptionFactor (Operation a) =
  a >>> resultById OperationPreemptionFactorId