-- |
-- Module     : Simulation.Aivika.Channel
-- 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 a channel that transforms one 'Signal' to another
-- within the 'Composite' computation.
--
module Simulation.Aivika.Channel
       (-- * Channel Computation
        Channel(..),
        -- * Delay Channel
        delayChannel,
        delayChannelM,
        -- * Sinking Signal
        sinkSignal,
        -- * Debugging
        traceChannel) where

import qualified Control.Category as C
import Control.Monad

import Simulation.Aivika.Simulation
import Simulation.Aivika.Dynamics
import Simulation.Aivika.Event
import Simulation.Aivika.Signal
import Simulation.Aivika.Composite

-- | It allows representing a signal transformation.
newtype Channel a b =
  Channel { runChannel :: Signal a -> Composite (Signal b)
            -- ^ Run the channel transform.
          }

instance C.Category Channel where

  id = Channel return

  (Channel g) . (Channel f) =
    Channel $ \a -> f a >>= g

-- | Return a delayed signal.
--
-- This is actually the 'delaySignal' function wrapped in the 'Channel' type. 
delayChannel :: Double            -- ^ the delay
                -> Channel a a    -- ^ the delay channel
delayChannel delay =
  Channel $ \a -> return $ delaySignal delay a

-- | Like 'delayChannel', but it re-computes the delay each time.
--
-- This is actually the 'delaySignalM' function wrapped in the 'Channel' type. 
delayChannelM :: Event Double     -- ^ the delay
                 -> Channel a a    -- ^ the delay channel
delayChannelM delay =
  Channel $ \a -> return $ delaySignalM delay a

-- | Sink the signal. It returns a computation that subscribes to
-- the signal and then ignores the received data. The resulting
-- computation can be a moving force to simulate the whole system of
-- the interconnected signals and channels.
sinkSignal :: Signal a -> Composite ()
sinkSignal a =
  do h <- liftEvent $
          handleSignal a $
          const $ return ()
     disposableComposite h

-- | Show the debug message with the current simulation time,
-- when emitting the output signal.
traceChannel :: String -> Channel a b -> Channel a b
traceChannel message (Channel f) =
  Channel $ \a ->
  do b <- f a
     return $
       traceSignal message b