{-# LANGUAGE Safe #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module
    Control.Arrow.Machine.Misc.Discrete
      (
        -- *Discrete
        -- | This module should be imported manually.
        T(),
        updates,
        value,
        
        arr,
        arr2,
        arr3,
        arr4,
        arr5,

        constant,
        hold,
        accum,
        fromEq,
        
        edge,
        asUpdater,
        Alg
      )
where

import Prelude hiding (id, (.))
import Control.Category
import Control.Arrow hiding (arr)
import Control.Applicative
import qualified Control.Arrow as Arr
import qualified Control.Arrow.Machine as P
import Data.Monoid (mconcat, mappend)

data T a = T {
    updates :: (P.Event ()),
    value :: a
  }

makeT ::
    ArrowApply a =>
    P.ProcessA a (P.Event (), b) (T b)
makeT = Arr.arr $ uncurry T

arr ::
    ArrowApply a =>
    (b->c) ->
    P.ProcessA a (T b) (T c)
arr f =
    Arr.arr $ \(T ev x) ->
        T ev (f x)

arr2 ::
    ArrowApply a =>
    (b1->b2->c) ->
    P.ProcessA a (T b1, T b2) (T c)
arr2 f =
    Arr.arr $ \(T ev1 x1, T ev2 x2) ->
        T (mconcat [ev1, ev2]) (f x1 x2)

arr3 ::
    ArrowApply a =>
    (b1->b2->b3->c) ->
    P.ProcessA a (T b1, T b2, T b3) (T c)
arr3 f =
    Arr.arr $ \(T ev1 x1, T ev2 x2, T ev3 x3) ->
        T (mconcat [ev1, ev2, ev3]) (f x1 x2 x3)

arr4 ::
    ArrowApply a =>
    (b1->b2->b3->b4->c) ->
    P.ProcessA a (T b1, T b2, T b3, T b4) (T c)
arr4 f =
    Arr.arr $ \(T ev1 x1, T ev2 x2, T ev3 x3, T ev4 x4) ->
        T (mconcat [ev1, ev2, ev3, ev4]) (f x1 x2 x3 x4)

arr5 ::
    ArrowApply a =>
    (b1->b2->b3->b4->b5->c) ->
    P.ProcessA a (T b1, T b2, T b3, T b4, T b5) (T c)
arr5 f =
    Arr.arr $ \(T ev1 x1, T ev2 x2, T ev3 x3, T ev4 x4, T ev5 x5) ->
        T (mconcat [ev1, ev2, ev3, ev4, ev5]) (f x1 x2 x3 x4 x5)

constant::
    ArrowApply a =>
    c ->
    P.ProcessA a b (T c)
constant x =
    (P.now &&& Arr.arr (const x)) >>> makeT

onUpdate ::
    ArrowApply a =>
    P.ProcessA a (P.Event b) (P.Event ())
onUpdate = proc ev ->
  do
    n <- P.now -< ()
    returnA -< n `mappend` P.collapse ev

hold ::
    ArrowApply a =>
    b ->
    P.ProcessA a (P.Event b) (T b)
hold i =
    (onUpdate &&& P.hold i) >>> makeT

accum ::
    ArrowApply a =>
    b ->
    P.ProcessA a (P.Event (b->b)) (T b)
accum i =
    (onUpdate &&& P.accum i) >>> makeT

fromEq ::
    (ArrowApply a, Eq b) =>
    P.ProcessA a b (T b)
fromEq = proc x ->
  do
    ev <- P.edge -< x
    returnA -< T (P.collapse ev) x

edge ::
    ArrowApply a =>
    P.ProcessA a (T b) (P.Event b)
edge = Arr.arr $ \(T ev x) -> x <$ ev

asUpdater ::
    ArrowApply a =>
    a b c ->
    P.ProcessA a (T b) (P.Event c)
asUpdater ar = edge >>> P.anytime ar



newtype Alg a i o = Alg { eval :: P.ProcessA a i (T o) }

instance
    ArrowApply a => Functor (Alg a i)
  where
    fmap f alg = Alg $ eval alg >>> arr f

instance
    ArrowApply a => Applicative (Alg a i)
  where
    pure = Alg . constant
    af <*> aa = Alg $ (eval af &&& eval aa) >>> arr2 ($)