{-----------------------------------------------------------------------------
    Reactive-Banana
------------------------------------------------------------------------------}
{-# LANGUAGE GADTs #-}
module Reactive.Banana.Internal.Model (
    -- * Synopsis
    -- | Model implementation of the abstract syntax tree.
    
    -- * Description
    -- $model

    -- Combinators
    -- Event(..), Behavior(..),
    -- never, filterE, unionWith, applyE, accumE, stepper,
   
    -- * Interpretation
    interpretModel,
    ) where

import Control.Applicative
import Control.Monad
import Control.Monad.Fix
import Control.Monad.Trans.State
import qualified Data.Vault as Vault

import qualified Reactive.Banana.Internal.AST as AST
import Reactive.Banana.Internal.InputOutput

{-$model

This module contains the model implementation for the primitive combinators
defined "Reactive.Banana.Internal.AST"
which in turn are the basis for the official combinators
documented in "Reactive.Banana.Combinators".

This module does not export any combinators,
you have to look at the source code to make use of it.
(If there is no link to the source code at every type signature,
then you have to run cabal with --hyperlink-source flag.)

This model is /authoritative/: when observed with the 'interpretModel' function,
both the actual implementation and its model /must/ agree on the result.
Note that this must also hold for recursive and partial definitions
(at least in spirit, I'm not going to split hairs over @_|_@ vs @\\_ -> _|_@).

Concerning time and space complexity, the model is not authoritative, however.
Implementations are free to be much more efficient.
-}

{-----------------------------------------------------------------------------
    Combinators
------------------------------------------------------------------------------}
-- due to observable sharing, the types have to be imported from
-- the module Reactive.Banana.Internal.AST
type Event a    = AST.EventModel a     -- = [Maybe a]
type Behavior a = AST.BehaviorModel a  -- = StepperB a (Event a)

never :: Event a
never = repeat Nothing

filterE :: (a -> Bool) -> Event a -> Event a
filterE p = map (>>= \x -> if p x then Just x else Nothing)

unionWith :: (a -> a -> a) -> Event a -> Event a -> Event a
unionWith f = zipWith g
    where
    g (Just x) (Just y) = Just $ f x y
    g (Just x) Nothing  = Just x
    g Nothing  (Just y) = Just y
    g Nothing  Nothing  = Nothing

applyE :: Behavior (a -> b) -> Event a -> Event b
applyE _                   []     = []
applyE (AST.StepperB f fe) (x:xs) = fmap f x : applyE (step f fe) xs
    where
    step a (Nothing:b) = stepper a b
    step _ (Just a :b) = stepper a b

accumE :: a -> Event (a -> a) -> Event a
accumE x []           = []
accumE x (Nothing:fs) = Nothing : accumE x fs
accumE x (Just f :fs) = let y = f x in y `seq` (Just y:accumE y fs) 

stepper :: a -> [Maybe a] -> Behavior a
stepper = AST.StepperB

{-----------------------------------------------------------------------------
    Interpretation, pays attention to observable sharing
------------------------------------------------------------------------------}
-- state monad for evaluation
type Eval = State Vault.Vault

-- | Interpret an event graph with the model implementation.
-- Mainly useful for testing library internals.
interpretModel
    :: (AST.Event AST.Expr a -> AST.Event AST.Expr b)
    -> Event a -> IO (Event b)
interpretModel f input = do
    i0 <- newInputChannel
    
    let
        evalE :: AST.EventD AST.Expr a -> Eval (Event a)
        evalE (AST.Never)             = return $ never
        evalE (AST.UnionWith f e1 e2) = unionWith f <$> goE e1 <*> goE e2
        evalE (AST.FilterE p e)       = filterE p   <$> goE e
        evalE (AST.ApplyE b e )       = applyE      <$> goB b  <*> goE e
        evalE (AST.AccumE x e )       = accumE x    <$> goE e
        evalE (AST.InputPure i)       =
            return $ maybe err id $ fromValue i (toValue i0 input)
            where err = error "Reactive.Banana.PushIO.interpretModel: internal error: Input"
        evalE _                       =
            error "Reactive.Banana.PushIO.interpretModel: internal error: E"

        evalB :: AST.BehaviorD AST.Expr a -> Eval (Behavior a)
        evalB (AST.Stepper x e) = stepper x <$> goE e
        evalB _                 =
            error "Reactive.Banana.PushIO.interpretModel: internal error: B"

        goE :: AST.Event AST.Expr a -> Eval (Event a)
        goE (AST.Pair node e) = do
            values <- get
            case Vault.lookup (AST.keyModelE node) values of
                Nothing -> mfix $ \v -> do
                    modify $ Vault.insert (AST.keyModelE node) v
                    evalE e
                Just v  -> return v

        goB :: AST.Behavior AST.Expr a -> Eval (Behavior a)
        goB (AST.Pair node b) = do
            values <- get
            case Vault.lookup (AST.keyModelB node) values of
                Nothing -> mfix $ \v -> do
                    modify $ Vault.insert (AST.keyModelB node) v
                    evalB b
                Just v  -> return v
    
    return $
        zipWith const
            (evalState (goE $ f $ AST.inputPure i0) Vault.empty)
            input