-- |
-- Module     : Simulation.Aivika.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.Ref
       (Ref,
        refChanged,
        refChanged_,
        newRef,
        readRef,
        writeRef,
        modifyRef) where

import Data.IORef
import Control.Monad
import Control.Monad.Trans

import Simulation.Aivika.Internal.Simulation
import Simulation.Aivika.Internal.Event
import Simulation.Aivika.Signal

-- | 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 a = 
  Ref { forall a. Ref a -> IORef a
refValue :: IORef a, 
        forall a. Ref a -> SignalSource a
refChangedSource :: SignalSource a }

-- | Create a new reference.
newRef :: a -> Simulation (Ref a)
newRef :: forall a. a -> Simulation (Ref a)
newRef a
a =
  do IORef a
x <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (IORef a)
newIORef a
a
     SignalSource a
s <- forall a. Simulation (SignalSource a)
newSignalSource
     forall (m :: * -> *) a. Monad m => a -> m a
return Ref { refValue :: IORef a
refValue = IORef a
x, 
                  refChangedSource :: SignalSource a
refChangedSource = SignalSource a
s }
     
-- | Read the value of a reference.
readRef :: Ref a -> Event a
readRef :: forall a. Ref a -> Event a
readRef Ref a
r = forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p -> forall a. IORef a -> IO a
readIORef (forall a. Ref a -> IORef a
refValue Ref a
r)

-- | Write a new value into the reference.
writeRef :: Ref a -> a -> Event ()
writeRef :: forall a. Ref a -> a -> Event ()
writeRef Ref a
r a
a = forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p -> 
  do a
a seq :: forall a b. a -> b -> b
`seq` forall a. IORef a -> a -> IO ()
writeIORef (forall a. Ref a -> IORef a
refValue Ref a
r) a
a
     forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. SignalSource a -> a -> Event ()
triggerSignal (forall a. Ref a -> SignalSource a
refChangedSource Ref a
r) a
a

-- | Mutate the contents of the reference.
modifyRef :: Ref a -> (a -> a) -> Event ()
modifyRef :: forall a. Ref a -> (a -> a) -> Event ()
modifyRef Ref a
r a -> a
f = forall a. (Point -> IO a) -> Event a
Event forall a b. (a -> b) -> a -> b
$ \Point
p -> 
  do a
a <- forall a. IORef a -> IO a
readIORef (forall a. Ref a -> IORef a
refValue Ref a
r)
     let b :: a
b = a -> a
f a
a
     a
b seq :: forall a b. a -> b -> b
`seq` forall a. IORef a -> a -> IO ()
writeIORef (forall a. Ref a -> IORef a
refValue Ref a
r) a
b
     forall a. Point -> Event a -> IO a
invokeEvent Point
p forall a b. (a -> b) -> a -> b
$ forall a. SignalSource a -> a -> Event ()
triggerSignal (forall a. Ref a -> SignalSource a
refChangedSource Ref a
r) a
b

-- | Return a signal that notifies about every change of the reference state.
refChanged :: Ref a -> Signal a
refChanged :: forall a. Ref a -> Signal a
refChanged Ref a
v = forall a. SignalSource a -> Signal a
publishSignal (forall a. Ref a -> SignalSource a
refChangedSource Ref a
v)

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