{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies, TypeOperators
           , StandaloneDeriving, GeneralizedNewtypeDeriving
           , TypeSynonymInstances, UndecidableInstances
  #-}
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
----------------------------------------------------------------------
-- |
-- Module      :  FRP.Reactive.Behavior
-- Copyright   :  (c) Conal Elliott 2008
-- License     :  BSD3
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- 
-- Reactive behaviors (continuous time)
----------------------------------------------------------------------

module FRP.Reactive.Behavior
  (
    BehaviorG, Behavior, Behaviour
  , time
  , stepper, switcher --, select
  , snapshotWith, snapshot, snapshot_, whenE
  , accumB, scanlB, monoidB, maybeB, flipFlop, countB
  , sumB, integral
  ) where

import Data.Monoid (Monoid(..))
import Control.Applicative (Applicative,(<$>),pure)
-- import Control.Monad (join)

import Control.Comonad

import Control.Compose ((:.)(..))

import Data.VectorSpace

import qualified FRP.Reactive.Reactive as R
import FRP.Reactive.Reactive
  ( TimeT, EventG, ReactiveG
  , withTimeE,onceRestE,diffE,joinMaybes,result)
import FRP.Reactive.Fun
import FRP.Reactive.Improving
import FRP.Reactive.Internal.Behavior

type EventI    t = EventG    (Improving t)
type ReactiveI t = ReactiveG (Improving t)
type BehaviorI t = BehaviorG (Improving t) t

-- | Time-specialized behaviors.
-- Note: The signatures of all of the behavior functions can be generalized.  Is
-- the interface generality worth the complexity?
type Behavior = BehaviorI TimeT

-- Synonym for 'Behavior'
type Behaviour = Behavior


-- | The identity generalized behavior.  Has value @t@ at time @t@.
-- 
-- > time :: Behavior TimeT
time :: Ord t => BehaviorI t t
time = beh (pure (fun id))

-- Turn a reactive value into a discretly changing behavior.
rToB :: ReactiveI t a -> BehaviorI t a
rToB = beh . fmap pure

-- Then use 'rToB' to promote reactive value functions to behavior
-- functions.

-- | Discretely changing behavior, based on an initial value and a
-- new-value event.
-- 
-- >stepper :: a -> Event a -> Behavior a
stepper :: a -> EventI t a -> BehaviorI t a
stepper = (result.result) rToB R.stepper

-- Suggested by Robin Green:

-- stepper = select pure

-- -- | Use a key event to key into a behaviour-valued function
-- select :: (a -> Behavior b) -> a -> Event a -> Behavior b
-- select f a e = f a `switcher` (f <$> e)

-- Looking for a more descriptive name.

-- | Switch between behaviors.
-- 
-- > switcher :: Behavior a -> Event (Behavior a) -> Behavior a
switcher :: (Ord tr) =>
            BehaviorG tr tf a
         -> EventG tr (BehaviorG tr tf a)
         -> BehaviorG tr tf a
b `switcher` eb = beh (unb b `R.switcher` (unb <$> eb))

-- | Snapshots a behavior whenever an event occurs and combines the values
-- using the combining function passed.  Take careful note of the order of
-- arguments and results.
-- 
-- > snapshotWith :: (a -> b -> c) -> Behavior b -> Event a -> Event c
snapshotWith :: Ord t =>
                (a -> b -> c)
             -> BehaviorI t b -> EventI t a -> EventI t c
snapshotWith h b e = f <$> (unb b `R.snapshot` withTimeE e)
 where
   f ((a,t),tfun) = h a (tfun `apply` t)


-- 'snapshotWith' is where tr meets tf.  withTimeE is specialized from
-- withTimeGE, converting the ITime into a TimeT.  This specialization
-- interferes with the generality of several functions in this module,
-- which are therefore now still using 'Behavior' instead of 'BehaviorG'.
-- Figure out how to get generality.


-- | Snapshot a behavior whenever an event occurs.  See also
-- 'snapshotWith'.  Take careful note of the order of arguments and
-- results.
-- 
-- > snapshot :: Behavior b -> Event a -> Event (a,b)
snapshot :: Ord t => BehaviorI t b -> EventI t a -> EventI t (a,b)
snapshot = snapshotWith (,)

-- TODO: tweak withTimeE so that 'snapshotWith' and 'snapshot' can have
-- more general types.  The problem is that withTimeE gives a friendlier
-- kind of time, namely known and finite.  Necessary?

-- Alternative implementations:
--   snapshotWith c e b = uncurry c <$> snapshot e b
--   snapshotWith c = (result.result.fmap) (uncurry c) snapshot

-- | Like 'snapshot' but discarding event data (often @a@ is '()').
-- 
-- > snapshot_ :: Behavior b -> Event a -> Event b
snapshot_ :: Ord t => BehaviorI t b -> EventI t a -> EventI t b
snapshot_ = snapshotWith (flip const)

-- Alternative implementations
-- e `snapshot_` src = snd <$> (e `snapshot` src)
-- snapshot_ = (result.result.fmap) snd snapshot

-- | Filter an event according to whether a reactive boolean is true.
-- 
-- > whenE :: Event a -> Behavior Bool -> Event a
whenE :: Ord t => EventI t a -> BehaviorI t Bool -> EventI t a
whenE e = joinMaybes . fmap h . flip snapshot e
 where
   h (a,True)  = Just a
   h (_,False) = Nothing

-- TODO: Same comment about generality as with snapshot

-- | Behavior from an initial value and an updater event.  See also
-- 'accumE'.
-- 
-- > accumB :: a -> Event (a -> a) -> Behavior a
accumB :: a -> EventI t (a -> a) -> BehaviorI t a
accumB = (result.result) rToB R.accumR

-- -- | Like 'scanl' for behaviors.  See also 'scanlE'.
-- scanlB :: (a -> b -> a) -> a -> Event b -> Behavior a
-- scanlB = (result.result.result) rToB R.scanlR

-- -- | Accumulate values from a monoid-valued event.  Specialization of
-- -- 'scanlB', using 'mappend' and 'mempty'.  See also 'monoidE'.
-- monoidB :: Monoid a => Event a -> Behavior a
-- monoidB = result rToB R.monoidR


---- The next versions are more continuous:

-- type RF a = Reactive (Fun TimeT a)

-- scanlB :: forall a c. (Behavior a -> c -> Behavior a) -> Behavior a
--        -> Event c -> Behavior a
-- scanlB f b0 e = beh (scanlRF f' (unb b0) e)
--  where
--    f' :: RF a -> c -> RF a
--    f' r c = unb (f (beh r) c)

-- scanlRF :: (RF a -> c -> RF a) -> RF a -> Event c -> RF a
-- scanlRF h rf0 e = join (R.scanlR h rf0 e)

-- monoidB :: Monoid a => Event (Behavior a) -> Behavior a
-- monoidB = scanlB mappend mempty

-- -- I doubt the above definitions work well.  They accumulate reactives without
-- -- aging them.  See 'accumE'.


-- | Like 'scanl' for behaviors.  See also 'scanlE'.
-- 
-- > scanlB :: forall a. (Behavior a -> Behavior a -> Behavior a) -> Behavior a
-- >        -> Event (Behavior a) -> Behavior a

-- TODO: generalize scanlB's type

scanlB :: forall a b tr tf. Ord tr =>
          (b -> BehaviorG tr tf a -> BehaviorG tr tf a)
       -> BehaviorG tr tf a
       -> EventG tr b -> BehaviorG tr tf a
scanlB plus zero = h
 where
   h :: EventG tr b -> BehaviorG tr tf a
   h e = zero `switcher` (g <$> onceRestE e)
   g :: (b, EventG tr b) -> BehaviorG tr tf a
   g (b, e') = b `plus` h e'


-- | Accumulate values from a monoid-valued event.  Specialization of
-- 'scanlB', using 'mappend' and 'mempty'.  See also 'monoidE'.
-- 
-- > monoidB :: Monoid a => Event (Behavior a) -> Behavior a
monoidB :: (Ord tr, Monoid a) => EventG tr (BehaviorG tr tf a)
        -> BehaviorG tr tf a
monoidB = scanlB mappend mempty

-- | Like 'sum' for behaviors.
-- 
-- > sumB :: AdditiveGroup a => Event a -> Behavior a
sumB :: (Ord t, AdditiveGroup a) => EventI t a -> BehaviorI t a
sumB = result rToB R.sumR

-- | Start out blank ('Nothing'), latching onto each new @a@, and blanking
-- on each @b@.  If you just want to latch and not blank, then use
-- 'mempty' for the second event.
-- 
-- > maybeB :: Event a -> Event b -> Behavior (Maybe a)
maybeB :: Ord t =>
          EventI t a -> EventI t b -> BehaviorI t (Maybe a)
maybeB = (result.result) rToB R.maybeR

-- | Flip-flopping behavior.  Turns true whenever first event occurs and
-- false whenever the second event occurs.
-- 
-- > flipFlop :: Event a -> Event b -> Behavior Bool
flipFlop :: Ord t => EventI t a -> EventI t b -> BehaviorI t Bool
flipFlop = (result.result) rToB R.flipFlop

-- | Count occurrences of an event.  See also 'countE'.
-- 
-- > countB :: Num n => Event a -> Behavior n
countB :: (Ord t, Num n) => EventI t a -> BehaviorI t n
countB = result rToB R.countR

-- | Euler integral.
-- 
-- > integral :: (VectorSpace v, Scalar v ~ TimeT) =>
-- >             Event () -> Behavior v -> Behavior v
integral :: (Scalar v ~ t, Ord t, VectorSpace v, Num t) =>
            EventI t a -> BehaviorI t v -> BehaviorI t v
integral t b = sumB (snapshotWith (*^) b (diffE (time `snapshot_` t)))

-- Yow!  That's a mouth full!


-- TODO: find out whether this integral works recursively.  If not, then
-- fix the implementation, rather than changing the semantics.  (No
-- "delayed integral".)
-- 
-- Early experiments suggest that recursive integration gets stuck.
-- Chuan-kai Lin has come up with a new lazier R.snapshotWith, but it
-- leaks when the reactive value changes in between event occurrences.


---- Comonadic stuff

-- Orphan.  Move elsewhere

instance (Functor g, Functor f, Copointed g, Copointed f)
      => Copointed (g :. f) where
  extract = extract . extract . unO

-- instance (Comonad g, Comonad f) => Comonad (g :. f) where
--   duplicate = inO (fmap duplicate . duplicate)


-- WORKING HERE

-- The plan for duplicate:
--
--   (g :. f) a -> g (f a) -> g (f (f a)) -> g (g (f (f a)))
--              -> g (f (g (f a))) -> (g :. f) (g (f a))
--              -> (g :. f) ((g :. f) a) -> 

-- But we'll have to do that middle twiddle, which I couldn't do for
-- behaviors to get a Monad either.  Is there another way?


-- instance Comonad (g :. f) where
--   duplicate 

deriving instance (Monoid tr, Monoid tf) => Copointed (BehaviorG tr tf) 

-- ITime and TimeT are not currently monoids.  They can be when I wrap
-- them in the Sum monoid constructor, in which mempty = 0 and mappend =
-- (+).  This monoid change moves us from absolute to relative time.  What
-- do I do for never-occuring futures and terminating events?

-- 

-- instance (Ord t, Monoid t, Monoid (Improving t)) => Comonad (BehaviorI t) where
--   duplicate = duplicateB

-- duplicateB :: forall t a.
--               (Ord t, Monoid t, Monoid (Improving t)) =>
--               BehaviorI t -> BehaviorI t (BehaviorI t a) where
--   duplicate b@(_ `Stepper`) = bb0 `switcher` 
--    where
--      f0 `R.Stepper` e = unb b
--      bb0 = beh (pure (fun (\ t -> undefined)))

-- f0 :: T a

-- e :: E (T a)

-- duplicate f0 :: T (T a)


-- b :: B a

-- unb b :: R (T a)



-- dup b :: B (B a)


-- TODO: generalize to BehaviorG
-- TODO: something about Monoid (Improving t)

-- Standard instances for applicative functors

-- #define APPLICATIVE Behavior
-- #include "Num-inc.hs"