{-# LANGUAGE TypeFamilies, MultiParamTypeClasses #-}

-- |
-- Module     : Simulation.Aivika.Trans.Var.Unboxed
-- 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
--
-- This module defines an unboxed variable that is bound up with the event queue and 
-- that keeps the history of changes storing the values in unboxed arrays, which
-- allows using the variable in differential and difference equations of
-- System Dynamics within hybrid discrete-continuous simulation.
--
-- Because of using the arrays, it would usually be a logical mistake to
-- use this variable for collecting statistics. In most cases,
-- the statistics can actually be collected with a very small footprint
-- by updating immutable 'SamplingStats' and 'TimingStats' values in
-- a mutable 'Ref' reference.
--
module Simulation.Aivika.Trans.Var.Unboxed (MonadVar(..)) where

import Data.Array

import Simulation.Aivika.Trans.Ref
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Dynamics
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Signal
import Simulation.Aivika.Trans.Statistics

-- | A type class of monads within which we can create mutable unboxed variables.
class MonadDES m => MonadVar m a where

  -- | Like the 'Ref' reference but keeps the history of changes in 
  -- different time points. The 'Var' variable is safe to be used in
  -- the hybrid discrete-continuous simulation. Only this variable can
  -- be much slower than the reference.
  --
  -- For example, the memoised values of the variable can be used in
  -- the differential and difference equations of System Dynamics, while
  -- the variable iself can be updated within the discrete event simulation.
  --
  -- Because of using arrays, it would usually be a logical mistake to use
  -- the variable for collecting statistics. In most cases, the statistics
  -- can actually be collected with a very small footprint by updating immutable
  -- 'SamplingStats' and 'TimingStats' values in a mutable @Ref@ reference.
  data Var m a

  -- | Create a new variable.
  newVar :: a -> Simulation m (Var m a)

  -- | Read the first actual, i.e. memoised, value of a variable for the requested time
  -- actuating the current events from the queue if needed.
  --
  -- This computation can be used in the ordinary differential and
  -- difference equations of System Dynamics.
  varMemo :: Var m a -> Dynamics m a

  -- | Read the recent actual value of a variable for the requested time.
  --
  -- This computation is destined to be used within discrete event simulation.
  readVar :: Var m a -> Event m a

  -- | Write a new value into the variable.
  writeVar :: Var m a -> a -> Event m ()

  -- | Mutate the contents of the variable.
  modifyVar :: Var m a -> (a -> a) -> Event m ()

  -- | Freeze the variable and return in arrays the time points and the corresponding 
  -- first and last values when the variable had changed or had been memoised in
  -- different time points: (1) the time points are sorted in ascending order;
  -- (2) the first and last actual values per each time point are provided.
  --
  -- If you need to get all changes including those ones that correspond to the same
  -- simulation time points then you can use the 'newSignalHistory' function passing
  -- in the 'varChanged' signal to it and then call function 'readSignalHistory'.
  freezeVar :: Var m a -> Event m (Array Int Double, Array Int a, Array Int a)

  -- | Return a signal that notifies about every change of the variable state.
  varChanged :: Var m a -> Signal m a

  -- | Return a signal that notifies about every change of the variable state.
  varChanged_ :: MonadDES m => Var m a -> Signal m ()