{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}

-- |
-- Module     : Simulation.Aivika.Trans.Ref
-- 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 updatable reference that depends on the event queue.
--
module Simulation.Aivika.Trans.Ref
       (Ref,
        refChanged,
        refChanged_,
        newRef,
        newRef0,
        readRef,
        writeRef,
        modifyRef) where

import Data.IORef

import Control.Monad
import Control.Monad.Trans

import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Event
import Simulation.Aivika.Trans.Signal
import qualified Simulation.Aivika.Trans.Ref.Base as B
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Observable

-- | The 'Ref' type represents a mutable variable similar to the 'IORef' variable 
-- but only dependent on the event queue, which allows synchronizing the reference
-- with the model explicitly through the 'Event' monad.
data Ref m a = 
  Ref { forall (m :: * -> *) a. Ref m a -> Ref m a
refValue :: B.Ref m a, 
        forall (m :: * -> *) a. Ref m a -> SignalSource m a
refChangedSource :: SignalSource m a }

-- | Create a new reference.
newRef :: MonadDES m => a -> Simulation m (Ref m a)
{-# INLINABLE newRef #-}
newRef :: forall (m :: * -> *) a. MonadDES m => a -> Simulation m (Ref m a)
newRef a
a =
  forall (m :: * -> *) a. (Run m -> m a) -> Simulation m a
Simulation forall a b. (a -> b) -> a -> b
$ \Run m
r ->
  do Ref m a
x <- forall (m :: * -> *) a. Run m -> Simulation m a -> m a
invokeSimulation Run m
r forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadRef m => a -> Simulation m (Ref m a)
B.newRef a
a
     SignalSource m a
s <- forall (m :: * -> *) a. Run m -> Simulation m a -> m a
invokeSimulation Run m
r forall (m :: * -> *) a.
MonadDES m =>
Simulation m (SignalSource m a)
newSignalSource
     forall (m :: * -> *) a. Monad m => a -> m a
return Ref { refValue :: Ref m a
refValue = Ref m a
x, 
                  refChangedSource :: SignalSource m a
refChangedSource = SignalSource m a
s }

-- | Create a new reference within more low level computation than 'Simulation'.
newRef0 :: (MonadDES m, B.MonadRef0 m) => a -> m (Ref m a)
{-# INLINABLE newRef0 #-}
newRef0 :: forall (m :: * -> *) a.
(MonadDES m, MonadRef0 m) =>
a -> m (Ref m a)
newRef0 a
a =
  do Ref m a
x <- forall (m :: * -> *) a. MonadRef0 m => a -> m (Ref m a)
B.newRef0 a
a
     SignalSource m a
s <- forall (m :: * -> *) a.
(MonadDES m, MonadRef0 m) =>
m (SignalSource m a)
newSignalSource0
     forall (m :: * -> *) a. Monad m => a -> m a
return Ref { refValue :: Ref m a
refValue = Ref m a
x, 
                  refChangedSource :: SignalSource m a
refChangedSource = SignalSource m a
s }
     
-- | Read the value of a reference.
readRef :: MonadDES m => Ref m a -> Event m a
{-# INLINE readRef #-}
readRef :: forall (m :: * -> *) a. MonadDES m => Ref m a -> Event m a
readRef Ref m a
r = forall (m :: * -> *) a. MonadRef m => Ref m a -> Event m a
B.readRef (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r)

-- | Write a new value into the reference.
writeRef :: MonadDES m => Ref m a -> a -> Event m ()
{-# INLINABLE writeRef #-}
writeRef :: forall (m :: * -> *) a. MonadDES m => Ref m a -> a -> Event m ()
writeRef Ref m a
r a
a = forall (m :: * -> *) a. (Point m -> m a) -> Event m a
Event forall a b. (a -> b) -> a -> b
$ \Point m
p -> 
  do a
a seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) a. Point m -> Event m a -> m a
invokeEvent Point m
p forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadRef m => Ref m a -> a -> Event m ()
B.writeRef (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r) a
a
     forall (m :: * -> *) a. Point m -> Event m a -> m a
invokeEvent Point m
p forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. SignalSource m a -> a -> Event m ()
triggerSignal (forall (m :: * -> *) a. Ref m a -> SignalSource m a
refChangedSource Ref m a
r) a
a

-- | Mutate the contents of the reference.
modifyRef :: MonadDES m => Ref m a -> (a -> a) -> Event m ()
{-# INLINABLE modifyRef #-}
modifyRef :: forall (m :: * -> *) a.
MonadDES m =>
Ref m a -> (a -> a) -> Event m ()
modifyRef Ref m a
r a -> a
f = forall (m :: * -> *) a. (Point m -> m a) -> Event m a
Event forall a b. (a -> b) -> a -> b
$ \Point m
p -> 
  do a
a <- forall (m :: * -> *) a. Point m -> Event m a -> m a
invokeEvent Point m
p forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadRef m => Ref m a -> Event m a
B.readRef (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r)
     let b :: a
b = a -> a
f a
a
     a
b seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) a. Point m -> Event m a -> m a
invokeEvent Point m
p forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadRef m => Ref m a -> a -> Event m ()
B.writeRef (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r) a
b
     forall (m :: * -> *) a. Point m -> Event m a -> m a
invokeEvent Point m
p forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. SignalSource m a -> a -> Event m ()
triggerSignal (forall (m :: * -> *) a. Ref m a -> SignalSource m a
refChangedSource Ref m a
r) a
b

-- | Return a signal that notifies about every change of the reference state.
refChanged :: Ref m a -> Signal m a
{-# INLINE refChanged #-}
refChanged :: forall (m :: * -> *) a. Ref m a -> Signal m a
refChanged Ref m a
r = forall (m :: * -> *) a. SignalSource m a -> Signal m a
publishSignal (forall (m :: * -> *) a. Ref m a -> SignalSource m a
refChangedSource Ref m a
r)

-- | Return a signal that notifies about every change of the reference state.
refChanged_ :: MonadDES m => Ref m a -> Signal m ()
{-# INLINABLE refChanged_ #-}
refChanged_ :: forall (m :: * -> *) a. MonadDES m => Ref m a -> Signal m ()
refChanged_ Ref m a
r = forall (m :: * -> *) a b.
MonadDES m =>
(a -> b) -> Signal m a -> Signal m b
mapSignal (forall a b. a -> b -> a
const ()) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Ref m a -> Signal m a
refChanged Ref m a
r

instance MonadDES m => Eq (Ref m a) where

  {-# INLINE (==) #-}
  Ref m a
r1 == :: Ref m a -> Ref m a -> Bool
== Ref m a
r2 = (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r1) forall a. Eq a => a -> a -> Bool
== (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r2)

instance (MonadDES m, Observable (B.Ref m) (t m))  => Observable (Ref m) (t m) where

  {-# INLINE readObservable #-}
  readObservable :: forall a. Ref m a -> t m a
readObservable Ref m a
r = forall (o :: * -> *) (m :: * -> *) a. Observable o m => o a -> m a
readObservable (forall (m :: * -> *) a. Ref m a -> Ref m a
refValue Ref m a
r)