{-|

This module contains the reference implementation for the pure subset
of the simple variant of Elerea.  I/O embedding is substituted by
conversion from and to lists.

-}

module FRP.Elerea.Simple.Pure
    (
    -- * The signal abstraction
      Signal
    , SignalGen
    -- * Inputs and outputs
    , fromList
    , toList
    , start
    -- * Basic building blocks
    , delay
    , snapshot
    , generator
    , memo
    , until
    -- * Derived combinators
    , stateful
    , transfer
    , transfer2
    , transfer3
    , transfer4
    ) where

import Control.Applicative
import Control.Monad
import Control.Monad.Fix

type Signal a = Int -> a

type SignalGen a = Int -> a

fromList :: [a] -> Signal a
fromList = (!!)

toList :: Signal a -> [a]
toList s = map s [0..]

start :: SignalGen (Signal a) -> [a]
start g = toList (g 0)

delay :: a -> Signal a -> SignalGen (Signal a)
delay x0 s t_start t_sample
    | t_start < t_sample  = s (t_sample-1)
    | t_start == t_sample = x0
    | otherwise           = error "This signal doesn't exist yet."

generator :: Signal (SignalGen a) -> SignalGen (Signal a)
generator s _t_start t_sample = s t_sample t_sample

snapshot :: Signal a -> SignalGen a
snapshot = id

memo :: Signal a -> SignalGen (Signal a)
memo = const

stateful :: a -> (a -> a) -> SignalGen (Signal a)
stateful x0 f = mfix $ \sig -> delay x0 (f <$> sig)

transfer :: a -> (t -> a -> a) -> Signal t -> SignalGen (Signal a)
transfer x0 f s = mfix $ \sig -> do
    sig' <- delay x0 sig
    memo (liftA2 f s sig')

transfer2 :: a -> (t1 -> t2 -> a -> a) -> Signal t1 -> Signal t2 -> SignalGen (Signal a)
transfer2 x0 f s1 s2 = mfix $ \sig -> do
    sig' <- delay x0 sig
    memo (liftA3 f s1 s2 sig')

transfer3 :: a -> (t1 -> t2 -> t3 -> a -> a) -> Signal t1 -> Signal t2 -> Signal t3 -> SignalGen (Signal a)
transfer3 x0 f s1 s2 s3 = mfix $ \sig -> do
    sig' <- delay x0 sig
    memo (liftM4 f s1 s2 s3 sig')

transfer4 :: a -> (t1 -> t2 -> t3 -> t4 -> a -> a) -> Signal t1 -> Signal t2 -> Signal t3 -> Signal t4 -> SignalGen (Signal a)
transfer4 x0 f s1 s2 s3 s4 = mfix $ \sig -> do
    sig' <- delay x0 sig
    memo (liftM5 f s1 s2 s3 s4 sig')