-----------------------------------------------------------------------------
-- |
-- Module      :  ForSyDe.Shallow.MoC.Synchronous.Process
-- Copyright   :  (c) ForSyDe Group, KTH 2007-2008
-- License     :  BSD-style (see the file LICENSE)
-- 
-- Maintainer  :  forsyde-dev@ict.kth.se
-- Stability   :  experimental
-- Portability :  portable
--
-- The synchronous process library defines processes for the
-- synchronous computational model. It is based on the synchronous
-- library "ForSyDe.Shallow.MoC.Synchronous".
-----------------------------------------------------------------------------
module ForSyDe.Shallow.MoC.Synchronous.Process (
  fifoDelaySY, finiteFifoDelaySY,
  memorySY, mergeSY, groupSY, counterSY
  ) where

import ForSyDe.Shallow.MoC.Synchronous.Lib
import ForSyDe.Shallow.Core
import ForSyDe.Shallow.Utility.Queue
import ForSyDe.Shallow.Utility.Memory

-- | The process 'fifoDelaySY' implements a synchronous model of a
-- FIFO with infinite size. The FIFOs take a list of values at each
-- event cycle and output one value. There is a delay of one cycle.
fifoDelaySY     :: Signal [a] -> Signal (AbstExt a)

-- | The process 'finiteFifoDelaySY' implements a FIFO with finite
-- size. The FIFOs take a list of values at each event cycle and
-- output one value. There is a delay of one cycle.
finiteFifoDelaySY   :: Int -> Signal [a] -> Signal (AbstExt a)

-- | The process 'memorySY' implements a synchronous memory. It uses
-- access functions of the type 'Read adr' and 'Write adr value'.
memorySY        :: Int -> Signal (Access a) -> Signal (AbstExt a)

-- | The process 'mergeSY' merges two input signals into a single
-- signal. The process has an internal buffer in order to prevent loss
-- of data. The process is deterministic and outputs events according
-- to their time tag. If there are two valid values at on both
-- signals. The value of the first signal is output first.
mergeSY         :: Signal (AbstExt a) -> Signal (AbstExt a)
           -> Signal (AbstExt a)

-- | The process 'counterSY' implements a counter, that counts from
--   min to max. The process 'counterSY' has no input and its output is
--   an infinite signal.
counterSY       :: (Enum a, Ord a) => a -> a -> Signal a

-- | The function 'groupSY' groups values into a vector of size n,
-- which takes n cycles. While the grouping takes place the output
-- from this process consists of absent values.
groupSY :: Int -> Signal a -> Signal (AbstExt (Vector a))

fifoDelaySY xs =  mooreSY fifoState fifoOutput (queue []) xs

fifoState :: Queue a -> [a] -> Queue a
fifoState (Q []) xs =  (Q xs)
fifoState q xs      =  fst (popQ (pushListQ q xs))

fifoOutput :: Queue a -> AbstExt a
fifoOutput (Q [])    = Abst
fifoOutput (Q (x:_)) = Prst x

finiteFifoDelaySY n xs
  = mooreSY fifoStateFQ fifoOutputFQ (finiteQueue n []) xs

fifoStateFQ :: FiniteQueue a -> [a] -> FiniteQueue a
fifoStateFQ (FQ n []) xs = (FQ n xs)
fifoStateFQ q         xs = fst (popFQ (pushListFQ q xs))

fifoOutputFQ :: FiniteQueue a -> AbstExt a
fifoOutputFQ (FQ _ [])    = Abst
fifoOutputFQ (FQ _ (x:_)) = Prst x

memorySY size xs       = mealySY ns o (newMem size) xs
  where
    ns mem (Read x)    = memState mem (Read x)
    ns mem (Write x v) = memState mem (Write x v)
    o  mem (Read x)    = memOutput mem (Read x)
    o  mem (Write x v) = memOutput mem (Write x v)


mergeSY xs ys                           = moore2SY mergeState mergeOutput [] xs ys
  where
    mergeState []     Abst     Abst     = []
    mergeState []     Abst     (Prst y) = [y]
    mergeState []     (Prst x) Abst     = [x]
    mergeState []     (Prst x) (Prst y) = [x, y]
    mergeState (_:us) Abst     Abst     = us
    mergeState (_:us) Abst     (Prst y) = us ++ [y]
    mergeState (_:us) (Prst x) Abst     = us ++ [x]
    mergeState (_:us) (Prst x) (Prst y) = us ++ [x, y]
    mergeOutput []    = Abst
    mergeOutput (u:_) = Prst u

groupSY k                      = mealySY f g s0
  where
    s0                         = NullV
    f v x | (lengthV v) == 0   = unitV x
          | (lengthV v) == k   = unitV x
          | otherwise          = v <: x
    g v _ | (lengthV v) == 0   = Abst
    g v x | (lengthV v) == k-1 = Prst (v<:x)
    g _ _ | otherwise          = Abst

counterSY m n = sourceSY f m
  where
    f x | x >= n    = m
        | otherwise = succ x