{-# LANGUAGE TypeOperators, ScopedTypeVariables
           , FlexibleInstances, MultiParamTypeClasses
           , GeneralizedNewtypeDeriving
 #-}
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}

-- For ghc-6.6 compatibility
-- {-# OPTIONS_GHC -fglasgow-exts -Wall #-}

----------------------------------------------------------------------
-- |
-- Module      :  FRP.Reactive.PrimReactive
-- Copyright   :  (c) Conal Elliott 2007
-- License     :  GNU AGPLv3 (see COPYING)
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- 
-- Functional /events/ and /reactive values/.  Semantically, an 'Event' is
-- stream of future values in time order.  A 'Reactive' value is a
-- discretly time-varying value.
-- 
-- Many of the operations on events and reactive values are packaged as
-- instances of the standard type classes 'Monoid', 'Functor',
-- 'Applicative', and 'Monad'.
-- 
-- This module focuses on representation and primitives defined in terms
-- of the representation.  See also "FRP.Reactive.Reactive", which
-- re-exports this module, plus extras that do not exploit the
-- representation.  My intention for this separation is to ease
-- experimentation with alternative representations.
-- 
-- Although the basic 'Reactive' type describes /discretely/-changing
-- values, /continuously/-changing values can be modeled simply as
-- reactive functions.  See "FRP.Reactive.Behavior" for a convenient type
-- composition of 'Reactive' and a constant-optimized representation of
-- functions of time.  The exact packaging of discrete vs continuous will
-- probably change with more experience.
----------------------------------------------------------------------

module FRP.Reactive.PrimReactive
  ( -- * Events and reactive values
    EventG, ReactiveG
    -- * Operations on events and reactive values
  , stepper, switcher, withTimeGE, withTimeGR
  , futuresE, futureStreamE, listEG, atTimesG, atTimeG
  , snapshotWith, accumE, accumR, once
  , withRestE, untilE
  , justE, filterE
  -- , traceE, traceR
  -- , mkEvent, mkEventTrace, mkEventShow
  , eventOcc
    -- * To be moved elsewhere
  , joinMaybes, filterMP, result
  -- * To be removed when it gets used somewhere
  , isMonotoneR
  -- * Testing
  , batch, infE, monoid_E
  -- * Temporary exports, while debugging
  -- , snap, merge
  ) where

import Prelude hiding (zip,zipWith)

import Data.Monoid
import Control.Applicative
import Control.Arrow (first)
import Control.Monad
import Data.Function (on)
-- import Debug.Trace (trace)

-- TODO: eliminate the needs for this stuff.
import Control.Concurrent (threadDelay)
import Control.Exception (evaluate)
import System.IO.Unsafe

import Data.Stream (Stream(..))

import Control.Comonad

import Test.QuickCheck
import Test.QuickCheck.Instances
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes
-- import Data.List

-- TypeCompose
import Control.Compose ((:.)(..), inO2, Monoid_f(..))
import Data.Zip
import Control.Instances () -- Monoid (IO ())


import Data.Unamb (unamb, assuming)
import Data.Unamb (race)  -- eliminate

-- import Data.Max
-- import Data.AddBounds
import FRP.Reactive.Future hiding (batch)
import FRP.Reactive.Internal.Reactive

{--------------------------------------------------------------------
    Events and reactive values
--------------------------------------------------------------------}

-- Bogus EqProp instance.  TODO: replace with a random equality test, such
-- that the collection of all generated tests covers equality.

instance (Bounded t, Eq t, Eq a, EqProp t, EqProp a) => EqProp (EventG t a) where
  a =-= b = foldr (.&.) (property True) $ zipWith (=-=) (f a) (f b)
    where
      f = take 20 . eFutures

-- TODO: work less and reach further per (=-=).

arbitraryE :: (Num t, Ord t, Bounded t, Arbitrary t, Arbitrary u) => Gen (EventG t u)
arbitraryE = frequency 
  [ -- (1, liftA2 ((liftA. liftA) futuresE addStart) arbitrary futureList)
    (4, liftA futuresE futureList)
  ]
  where
    -- earliestFuture = Future . (,) (Max MinBound)
    -- addStart = (:).earliestFuture
    futureList = futureListFinite
                 -- frequency [(10, futureListFinite), (1,futureListInf)]
    futureListFinite = liftA2 (zipWith future) nondecreasing arbitrary
--     futureListInf =
--       liftA2 (zipWith future) (resize 10 nondecreasingInf)
--                               (infiniteList arbitrary)

instance (Arbitrary t, Ord t, Bounded t, Num t, Arbitrary a) => Arbitrary (EventG t a) where
  arbitrary   = arbitraryE

instance (CoArbitrary t, CoArbitrary a) => CoArbitrary (EventG t a) where
  coarbitrary = coarbitrary . eFuture

----

-- Arbitrary works just like pairs:

-- instance (Arbitrary t, Arbitrary a, Num t, Ord t, Bounded t) => Arbitrary (ReactiveG t a) where
--   arbitrary = liftA2 Stepper arbitrary arbitrary
--   coarbitrary (a `Stepper` e) = coarbitrary e . coarbitrary a

instance (Arbitrary t, Arbitrary a, Num t, Ord t, Bounded t) => Arbitrary (ReactiveG t a) where
  arbitrary = liftA2 Stepper arbitrary arbitrary

instance (CoArbitrary t, CoArbitrary a) => CoArbitrary (ReactiveG t a) where
  coarbitrary (a `Stepper` e) = coarbitrary e . coarbitrary a

instance (Ord t, Bounded t) => Model (ReactiveG t a) (t -> a) where
  model = rat

instance (Ord t, Bounded t, Arbitrary t, Show t, EqProp a) => EqProp (ReactiveG t a)
 where
   (=-=) = (=-=) `on` model

-- Initial value of a 'Reactive'
rInit :: ReactiveG t a -> a
rInit (a `Stepper` _) = a


{--------------------------------------------------------------------
    Instances
--------------------------------------------------------------------}

instance (Ord t, Bounded t) => Monoid (EventG t a) where
  mempty  = Event mempty
  mappend = inEvent2 merge

-- Standard instance for Applicative of Monoid
instance (Ord t, Bounded t, Monoid a) => Monoid (ReactiveG t a) where
  mempty  = pure mempty
  mappend = liftA2 mappend

-- | Merge two 'Future' reactives into one.
merge :: (Ord t, Bounded t) => Binop (FutureG t (ReactiveG t a))

-- The following two lines seem to be too strict and are causing
-- reactive to lock up.  I.e. the time argument of one of these
-- must have been _|_, so when we pattern match against it, we 
-- block.
-- 
-- On the other hand, they patch a massive space leak in filterE.  Perhaps
-- there's an unamb solution.

u `merge` v =
  assuming (isNeverF u) v `unamb`
  assuming (isNeverF v) u `unamb`
  (inFutR (`merge` v) <$> u) `mappend` (inFutR (u `merge`) <$> v)

-- TODO: redefine via parIdentity from Data.Unamb

-- u `merge` v | isNever u = v
--             | isNever v = u

-- Future (Max MaxBound,_) `merge` v = v
-- u `merge` Future (Max MaxBound,_) = u

-- u `merge` v = 
--   (inFutR (`merge` v) <$> u) `mappend` (inFutR (u `merge`) <$> v)

-- What's going on in this 'merge' definition?  Try two different
-- future paths.  If u arrives before v (or simultaneously), then
-- begin as u begins and then merge v with the rest of u.  Otherwise,
-- begin as v begins and then merge u with the rest of v.  Because of
-- the left-bias, make sure u fragments are always the first argument
-- to merge and v fragments are always the second.


-- Define functor instances in terms of each other.
instance Functor (EventG t) where
  fmap = inEvent.fmap.fmap

instance Functor (ReactiveG t) where
  fmap f ~(a `Stepper` e) = f a `stepper` fmap f e

-- standard instance
instance (Ord t, Bounded t) => Applicative (EventG t) where
  pure = return
  (<*>) = ap
--   _ <*> (Event (Future (Max MaxBound,_))) = mempty
--   x <*> y = x `ap` y

-- standard instance
instance (Ord t, Bounded t) => Alternative (EventG t) where
  { empty = mempty; (<|>) = mappend }

instance (Ord t, Bounded t) => Zip (ReactiveG t) where
  -- zip :: ReactiveG t a -> ReactiveG t b -> ReactiveG t (a,b)
  (c `Stepper` ce) `zip` (d `Stepper` de) =
    (c,d) `accumR` pairEdit (ce,de)

instance (Ord t, Bounded t) => Applicative (ReactiveG t) where
  pure a = a `stepper` mempty
  -- Standard definition.  See 'Zip'.
  rf <*> rx = zipWith ($) rf rx

-- A wonderful thing about the <*> definition for ReactiveG is that it
-- automatically caches the previous value of the function or argument
-- when the argument or function changes.


instance (Ord t, Bounded t) => Monad (EventG t) where
  return a = Event (pure (pure a))
  e >>= f  = joinE (fmap f e)


-- From Jules Bean (quicksilver):

-- joinE :: (Ord t) => EventG t (EventG t a) -> EventG t a
-- joinE (Event u) =
--   Event . join $
--   fmap (\ (e `Stepper` ee) ->
--          let (Event uu) = (e `mappend` joinE ee) in uu)
--   u

-- plus some fiddling:

joinE :: (Ord t, Bounded t) => EventG t (EventG t a) -> EventG t a

joinE (Event u) = Event (u >>= eFuture . g)
 where 
   g (e `Stepper` ee) = e `mappend` joinE ee

-- joinE = inEvent (>>= eFuture . g)
--  where 
--    g (e `Stepper` ee) = e `mappend` joinE ee


-- | Experimental specialization of 'joinMaybes'.
justE :: (Ord t, Bounded t) => EventG t (Maybe a) -> EventG t a
justE ~(Event (Future (t, mb `Stepper` e'))) =
  assuming (t == maxBound) mempty `unamb`
  (inEvent.inFuture.first) (max t) $
    case mb of
      Nothing -> justE e'
      Just a  -> Event (Future (t, a `Stepper` justE e'))


-- This definition is much more efficient than the following.

-- justE = (>>= maybe mzero return)

-- On the other hand, this simpler definition inserts the necessary max
-- applications so that we needn't find a Just in order to have a lower bound.

-- TODO: find and fix the inefficiency.





-- | Experimental specialization of 'filterMP'.
filterE :: (Ord t, Bounded t) => (a -> Bool) -> EventG t a -> EventG t a
filterE p m = justE (liftM f m)
 where
   f a | p a        = Just a
       | otherwise  = Nothing


{-

-- happy a t b. Same as (a `mappend` b) except takes advantage of knowledge
-- that t is a lower bound for the occurences of b. This allows for extra
-- laziness.
happy :: (Ord t) => EventG t a ->
                    Time t ->
                    EventG t a ->
                    EventG t a
happy a t b =
  assuming (isNeverE a) b `unamb`
  assuming (isNeverF b) a `unamb`
  happy' a t b ...


happy a (Max MaxBound) _ = a
happy (Event (Future (Max MaxBound, _))) _ b = b
happy a@(Event (Future (t0, e `Stepper` ee'))) t b 
  | t0 <= t = (Event (Future (t0, e `Stepper` (happy ee' t b))))
  | otherwise = a `mappend` b

-- Note, joinE should not be called with an infinite list of events that all
-- occur at the same time.  It can't decide which occurs first.
joinE :: (Ord t) => EventG t (EventG t a) -> EventG t a
joinE (Event (Future (Max MaxBound, _))) = mempty
joinE (Event (Future (t0h, e `Stepper` ((Event (Future (Max MaxBound, _)))))))
  = adjustE t0h e
joinE (Event (Future (t0h, e `Stepper` ee'@((Event (Future (t1h, _)))))))
  = happy (adjustE t0h e) t1h (adjustTopE t0h (joinE ee'))
-}

{-
-- Note, joinE should not be called with an infinite list of events that all
-- occur at the same time.  It can't decide which occurs first.
joinE :: (Ord t) => EventG t (EventG t a) -> EventG t a
joinE (Event (Future (t0h, e `Stepper` ee'))) =
  assuming (t0h == maxBound) mempty $
  adjustE t0h (e `mappend` joinE ee')

-- TODO: revisit this def.


-- Original Version:
-- joinE (Event (Future (t0h, e `Stepper` ee'))) =
--   adjustE t0h e `mappend` adjustTopE t0h (joinE ee')

adjustTopE :: (Ord t, Bounded t) => Time t -> EventG t t1 -> EventG t t1

-- adjustTopE t0h = (inEvent.inFuture.first) (max t0h)

adjustTopE t0h ~(Event (Future (tah, r))) =
  Event (Future (t0h `max` tah,r))

adjustE :: (Ord t, Bounded t) => Time t -> EventG t t1 -> EventG t t1

adjustE _ e@(Event (Future (Max MaxBound, _))) = e

adjustE t0h (Event (Future (tah, a `Stepper` e))) =
  Event (Future (t1h,a `Stepper` adjustE t1h e))
   where
     t1h = t0h `max` tah

-}

-- The two-caseness of adjustE prevents the any info from coming out until
-- tah is known to be Max or non-Max.  Problem?

-- Is the MaxBound case really necessary?

-- TODO: add adjustE explanation.  What's going on and why t1 in the
-- recursive call?  David's comment:
-- If we have an event [t1, t2] we know t2 >= t1 so (max t t2) == (max (max t t1) t2).
-- See http://hpaste.org/11518 for a def that doesn't change the lower bound.
-- 
-- What I remember is that this function is quite subtle w.r.t laziness.
-- There are some notes in the paper.  If i find instead that a simpler
-- definition is possible, so much the better.

-- Here's an alternative to joinE that is less strict, and doesn't cause
-- reactive to lock up.  Need to verify correctness.  (Does lock up with
-- the mappend optimization that eliminates a space/time leak.)
{-
joinE :: (Ord t, Bounded t) => EventG t (EventG t a) -> EventG t a
joinE (Event (Future (t0h, ~(e `Stepper` ee')))) =
   adjustE t0h (e `mappend` joinE ee')

adjustE t0h (Event (Future (tah, ~(a `Stepper` e)))) =
  Event (Future (t1h,a `Stepper` adjustE t1h e))
  where
    t1h = t0h `max` tah
-}


-- These two joinE defs both lock up in my tests.


instance (Ord t, Bounded t) => MonadPlus (EventG t) where
  { mzero = mempty; mplus = mappend }

-- Standard instance for Applicative w/ join
instance (Ord t, Bounded t) => Monad (ReactiveG t) where
  return  = pure
  r >>= f = joinR (f <$> r)


-- -- Temporary
-- justE :: (Ord t, Bounded t) => EventG t (Maybe a) -> EventG t a
-- justE = joinMaybes

-- filterE :: (Ord t, Bounded t, Show a) => (a -> Bool) -> EventG t a -> EventG t a
-- filterE = filterMP

{-

-- | Pass through the 'Just' occurrences, stripped.  Experimental
-- specialization of 'joinMaybes'.
justE :: (Ord t, Bounded t) => EventG t (Maybe a) -> EventG t a
justE (Event (Future (ta, Just a `Stepper` e'))) =
  Event (Future (ta, a `Stepper` justE e'))
justE (Event (Future (ta, Nothing `Stepper` e'))) =
  adjustE ta (justE e')

-- The adjustE lets consumers know that the resulting event occurs no
-- earlier than ta.

-- | Pass through values satisfying a given predicate.  Experimental
-- specialization of 'filterMP'.
filterE :: (Ord t, Show a) => (a -> Bool) -> EventG t a -> EventG t a

-- filterE p e = joinMaybes (f <$> e)
--  where
--    f a | p a        = Just a
--        | otherwise  = Nothing

filterE _ e@(Event (Future (Max MaxBound, _))) = e

filterE p (Event (Future (ta, a `Stepper` e'))) =
  adjustTopE ta $
    if p a then
      Event (Future (ta, a `Stepper` filterE p e'))
    else filterE p e'
-}

-- The adjustTopE ta guarantees a lower bound even before we've looked at a.

-- filterE p (Event (Future (ta, a `Stepper` e')))
--   | p a       = Event (Future (ta, a `Stepper` filterE p e'))
--   | otherwise = adjustTopE ta (filterE p e')

-- filterE p (Event (Future (ta, a `Stepper` e'))) = h (filterE p e')
--  where  
--    h | p a = -- trace ("pass " ++ show a) $
--             \ e'' -> Event (Future (ta, a `Stepper` e''))
--      | otherwise = -- trace ("skip " ++ show a) $
--                    adjustTopE ta

-- Or maybe move the adjustTopE to the second filterE

-- adjustTopE t0h = (inEvent.inFuture.first) (max t0h)


-- Laziness problem: no information at all can come out of filterE's
-- result until @p a@ is known.

-- filterE p ~(Event (Future (ta, a `Stepper` e'))) =
--   Event (Future (ta', r'))
--  where
--    ta' 
-- 
--   if p a then
--     Event (Future (ta, a `Stepper` filterE p e'))
--   else
--     adjustE ta (filterE p e')


{--------------------------------------------------------------------
    Operations on events and reactive values
--------------------------------------------------------------------}

-- | Reactive value from an initial value and a new-value event.
stepper :: a -> EventG t a -> ReactiveG t a
stepper = Stepper

-- -- | Turn a reactive value into an event, with the initial value
-- -- occurring at -Infinity.
-- --
-- -- Oops: breaks the semantic abstraction of 'Reactive' as a step
-- function.
-- rToE :: (Ord t, Bounded t) => ReactiveG t a -> EventG t a
-- rToE (a `Stepper` e) = pure a `mappend` e

-- | Switch between reactive values.
switcher :: (Ord t, Bounded t) => ReactiveG t a -> EventG t (ReactiveG t a) -> ReactiveG t a
r `switcher` e = join (r `stepper` e)

-- | Reactive 'join' (equivalent to 'join' but slightly more efficient, I think)
joinR :: (Ord t, Bounded t) => ReactiveG t (ReactiveG t a) -> ReactiveG t a

joinR ((a `Stepper` Event ur) `Stepper` e'@(Event urr)) = a `stepper` Event u
 where
   u = ((`switcher` e') <$> ur) `mappend` (join <$> urr)

-- The following simpler definition is wrong.  It keeps listening to @e@
-- even after @er@ has occurred.
-- joinR ((a `Stepper` e) `Stepper` er) = 
--   a `stepper` (e `mappend` join (rToE <$> er))

-- e  :: EventG t a
-- er :: EventG t (ReactiveG t a)
-- 
-- rToE <$> er ::: EventG t (EventG t a)
-- join (rToE <$> er) ::: EventG t a


-- | Access occurrence times in an event.  See also 'withTimeGR'.
withTimeGE :: EventG t a -> EventG t (a, Time t)
withTimeGE = inEvent $ inFuture $ \ (t,r) -> (t, withTimeGR t r)

-- | Access occurrence times in a reactive value.  See also 'withTimeGE'.
withTimeGR :: Time t -> ReactiveG t a -> ReactiveG t (a, Time t)
withTimeGR t (a `Stepper` e) = (a,t) `Stepper` withTimeGE e

-- | Convert a temporally monotonic list of futures to an event.  See also
-- the specialization 'listE'
listEG :: (Ord t, Bounded t) => [(t,a)] -> EventG t a
listEG = futuresE . map (uncurry future)

-- | Convert a temporally monotonic list of futures to an event
futuresE :: (Ord t, Bounded t) => [FutureG t a] -> EventG t a
futuresE [] = mempty
futuresE (Future (t,a) : futs) =
  -- trace ("l2E: "++show t) $
  Event (Future (t, a `stepper` futuresE futs))

-- TODO: redefine 'futuresE' as a fold
-- futuresE = foldr (\ fut e -> Event ((`stepper` e) <$> fut)) mempty

-- TODO: hide futuresE.  currently exported for use in TVal.  If I move to
-- Internal/Reactive, I have to move the monoid instance there, which
-- requires moving others as well.

-- | Convert a temporally monotonic stream of futures to an event.  Like
-- 'futuresE' but it can be lazier, because there's not empty case.
futureStreamE :: (Ord t, Bounded t) => Stream (FutureG t a) -> EventG t a
futureStreamE (~(Cons (Future (t,a)) futs)) =
  Event (Future (t, a `stepper` futureStreamE futs))

-- | Event at given times.  See also 'atTimeG'.
atTimesG :: (Ord t, Bounded t) => [t] -> EventG t ()
atTimesG = listEG . fmap (flip (,) ())

-- | Single-occurrence event at given time.
atTimeG :: (Ord t, Bounded t) => t -> EventG t ()
atTimeG = atTimesG . pure

-- | Snapshot a reactive value whenever an event occurs and apply a
-- combining function to the event and reactive's values.
snapshotWith :: (Ord t, Bounded t) =>
                (a -> b -> c) -> ReactiveG t b -> EventG t a -> EventG t c

-- snapshotWith f e r = joinMaybes $ fmap h (e `snap` r)
--  where
--    h (Nothing,_) = Nothing
--    h (Just a ,b) = Just (f a b)

-- -- This variant of 'snapshot' has 'Nothing's where @b@ changed and @a@
-- -- didn't.
-- snap :: forall a b t. (Ord t, Bounded t) =>
--         ReactiveG t b -> EventG t a -> EventG t (Maybe a, b)
-- (b0 `Stepper` eb) `snap` ea =
--   assuming (isNeverE ea) mempty $
--   (Nothing, b0) `accumE` (fmap fa ea `mappend` fmap fb eb)
--  where
--    fa :: a -> Unop (Maybe a, b)
--    fb :: b -> Unop (Maybe a, b)
--    fa a (_,b) = (Just a , b)
--    fb b _     = (Nothing, b)

-- This next version from Chuan-kai Lin, so that snapshot is lazy enough
-- for recursive cases.  It leaks when the reactive changes faster than
-- the event occurs.

snapshotWith f r e =
    fmap snap $ accumE seed $ fmap advance $ withTimeGE e
        where snap (a, sr)           = f a (rInit sr)
              seed                   = (error "snapshotWith seed", r)
              advance (a, t) (_, sr) = (a, skipRT sr t)

-- | Skip reactive values until the given time.
skipRT :: (Ord t, Bounded t) => ReactiveG t a -> Time t -> ReactiveG t a
r@(_ `Stepper` Event (Future (t, r1))) `skipRT` start =
    if t < start then r1 `skipRT` start else r

-- From Beelsebob:

-- snapshotWith f r e@(Event (Future (t,_ `Stepper` ne))) =
--   Event (Future (t, v' `stepper` snapshotWith f r ne))
--   where
--     Event (Future (_,v' `Stepper` _)) = snapshotWith' f r e
--     snapshotWith' f' r' e' = joinMaybes $ fmap h (r' `snap` e')
--       where
--         h (Nothing,_) = Nothing
--         h (Just a ,b) = Just (f' a b)



-- | Accumulating event, starting from an initial value and a
-- update-function event.  See also 'accumR'.
accumE :: a -> EventG t (a -> a) -> EventG t a
accumE a = inEvent $ fmap $ \ (f `Stepper` e') -> f a `accumR` e'

-- | Reactive value from an initial value and an updater event.  See also
-- 'accumE'.
accumR :: a -> EventG t (a -> a) -> ReactiveG t a
a `accumR` e = a `stepper` (a `accumE` e)

-- | Just the first occurrence of an event.
once :: (Ord t, Bounded t) => EventG t a -> EventG t a
once = (inEvent.fmap) (pure . rInit)

-- | Extract a future representing the first occurrence of the event together
-- with the event of all occurrences after that one.
eventOcc :: (Ord t) => EventG t a -> FutureG t (a, EventG t a)
eventOcc (Event fut)  = (\ (Stepper a e) -> (a,e)) <$> fut


-- | Access the remainder with each event occurrence.
withRestE :: EventG t a -> EventG t (a, EventG t a)
withRestE = (inEvent.fmap) $
	    \ (a `Stepper` e') -> (a,e') `stepper` withRestE e'


-- | Truncate first event at first occurrence of second event.
untilE :: (Ord t, Bounded t) => EventG t a -> EventG t b -> EventG t a
ea `untilE` Event (Future ~(tb,_)) = ea `untilET` tb

-- | Truncate first event at the given time.
untilET :: (Ord t, Bounded t) => EventG t a -> Time t -> EventG t a


-- Event (Future (ta, ~(a `Stepper` e'))) `untilET` t = 
--   if ta < t then
--     Event (Future (ta, a `Stepper` (e' `untilET` t)))
--   else
--     mempty

-- Hm.  I doubt that the definition above gives sufficient temporal
-- laziness.  No information can come out of the result until the value of
-- @ta < t@ is determined, which is usually at about time @ta `min` t@.

-- So, try the following definition instead.  It immediately provides
-- lower bounds of both @ta@ and @t@ as lower bounds of the constructed
-- event occurrences.

Event (Future ~(ta, a `Stepper` e')) `untilET` t = 
  Event (Future (ta', a `Stepper` (e' `untilET` t)))
 where
   ta' = (ta `min` t) `max` (if ta < t then ta else maxBound)

-- I'm not sure about @<@ vs @<=@ above.


-- | Sample a reactive value at a sequence of monotonically non-decreasing
-- times.  Deprecated, because it does not reveal when value is known to
-- be repeated in the output.  Those values won't be recomputed, but they
-- may be re-displayed.
rats :: (Ord t, Bounded t) => ReactiveG t a -> [t] -> [a] -- increasing times

_ `rats` [] = []

r@(a `Stepper` Event (Future (tr',r'))) `rats` ts@(t:ts')
  | ftime t <= tr' = a : r `rats` ts'
  | otherwise      = r' `rats` ts

-- Just for testing
rat :: (Ord t, Bounded t) => ReactiveG t a -> t -> a
rat r = head . rats r . (:[])


{--------------------------------------------------------------------
    Other instances
--------------------------------------------------------------------}

-- Standard instances
instance (Monoid_f f, Ord t, Bounded t) => Monoid_f (ReactiveG t :. f) where
    { mempty_f = O (pure mempty_f); mappend_f = inO2 (liftA2 mappend_f) }
instance (Ord t, Bounded t, Zip f) => Zip (ReactiveG t :. f) where zip = apZip

instance Unzip (ReactiveG t) where {fsts = fmap fst; snds = fmap snd}

-- Standard instances
instance (Ord t, Bounded t) => Monoid_f (EventG t) where
  { mempty_f = mempty ; mappend_f = mappend }
instance (Ord t, Bounded t) => Monoid ((EventG t :. f) a) where
  { mempty = O mempty; mappend = inO2 mappend }
instance (Ord t, Bounded t) => Monoid_f (EventG t :. f) where
  { mempty_f = mempty ; mappend_f = mappend }
instance (Ord t, Bounded t, Cozip f) => Zip (EventG t :. f) where
  zip = cozip

-- Standard instance for functors
instance Unzip (EventG t) where {fsts = fmap fst; snds = fmap snd}


{--------------------------------------------------------------------
    Comonadic stuff
--------------------------------------------------------------------}

instance Copointed (EventG t) where
  -- E a -> F (R a) -> R a -> a
  extract = extract . extract . eFuture

-- Here's the plan for 'duplicate':
-- 
--   E a -> F (R a) -> F (R (R a)) -> F (F (R (R a)))
--       -> F (R (F (R a))) -> E (F (R a)) -> E (E a)


instance Monoid t => Comonad (EventG t) where
  duplicate =
    fmap Event . Event . fmap frTOrf . duplicate . fmap duplicate . eFuture

-- This frTOrf definition type-checks.  Is it what we want?
frTOrf :: FutureG t (ReactiveG t a) -> ReactiveG t (FutureG t a)
frTOrf ~(Future (ta,e)) = (Future . (,) ta) <$> e

-- TODO: Reconsider E = F :. R .  Didn't work with absolute time.  What
-- about relative time?

instance (Ord t, Bounded t) => Pointed (ReactiveG t) where
  point = (`stepper` mempty)

-- TODO: I think we can bypass mempty and so eliminate the Ord
-- constraint.  If so, remove Ord tr from 'time' in Behavior.

instance Copointed (ReactiveG t) where
  -- extract = extract . rat
  -- Semantically: extract == extract . rat == (`rat` mempty) But mempty
  -- is the earliest time (since I'm using the Max monoid *), so here's a
  -- cheap alternative that also doesn't require Ord t:
  extract (a `Stepper` _) = a

-- extract r == extract (rat r) == rat r mempty

-- * Moreover, mempty is the earliest time in the Sum monoid on
-- non-negative values, for relative-time behaviors.

instance Monoid t => Comonad (ReactiveG t) where
  duplicate r@(_ `Stepper` Event u) =
    r `Stepper` Event (duplicate <$> u)

-- TODO: Prove the morphism law:
-- 
--   fmap rat . rat . dup == dup . rat

-- Reactive is like the stream comonad
-- TODO: try again letting events and reactives be streams of futures.


{--------------------------------------------------------------------
    To be moved elsewhere
--------------------------------------------------------------------}

-- | Pass through @Just@ occurrences.
joinMaybes :: MonadPlus m => m (Maybe a) -> m a
joinMaybes = (>>= maybe mzero return)

-- | Pass through values satisfying @p@.
filterMP :: MonadPlus m => (a -> Bool) -> m a -> m a
filterMP p m = joinMaybes (liftM f m)
 where
   f a | p a        = Just a
       | otherwise  = Nothing

-- Alternatively:
-- filterMP p m = m >>= guarded p
--  where
--    guarded p x = guard (p x) >> return x


-- | Apply a given function inside the results of other functions.
-- Equivalent to '(.)', but has a nicer reading when composed
result :: (b -> b') -> ((a -> b) -> (a -> b'))
result =  (.)


{--------------------------------------------------------------------
    Tests
--------------------------------------------------------------------}

-- TODO: Define more types like ApTy, use in batch below.  Move to checkers.
type ApTy f a b = f (a -> b) -> f a -> f b

batch :: TestBatch
batch = ( "Reactive.PrimReactive"
        , concatMap unbatch
          [ 
          -- monad associativity fails
          -- , monad  (undefined :: EventG NumT (NumT,T,NumT))
            monoid (undefined :: EventG NumT T)
          , monoid (undefined :: ReactiveG NumT [T])
          , monad  (undefined :: ReactiveG NumT (NumT,T,NumT))
--           , ("occurence count",
--              [("joinE", joinEOccuranceCount)]
--             )
          , ("monotonicity",
              [ monotonicity2 "<*>"           
                 ((<*>) :: ApTy (EventG NumT) T T)
{-
              , monotonicity2 "adjustE"       (adjustE
                ::    Time NumT
                   -> EventG NumT NumT
                   -> EventG NumT NumT)
-}
              , monotonicity  "join"          (join
                ::    EventG NumT (EventG NumT T)
                   -> EventG NumT T)
              , monotonicity  "withTimeGE"    (withTimeGE
                ::    EventG NumT T
                   -> EventG NumT (T, Time NumT))
              , monotonicity  "once"          (once
                ::    EventG NumT T
                   -> EventG NumT T)
              , monotonicity2 "accumE"        (accumE
                ::    T
                   -> EventG NumT (T -> T)
                   -> EventG NumT T)
              , monotonicity2 "mappend"       (mappend
                ::    EventG NumT T
                   -> EventG NumT T
                   -> EventG NumT T)
              , monotonicity2 "mplus"         (mplus
                ::    EventG NumT T
                   -> EventG NumT T
                   -> EventG NumT T)
              , monotonicity2 "<|>"           ((<|>)
                ::    EventG NumT T
                   -> EventG NumT T
                   -> EventG NumT T)
              , monotonicity2 "fmap"          (fmap
                ::    (T -> T)
                   -> EventG NumT T
                   -> EventG NumT T)
--              ,monotonicity2 "flip (>>=)"    (flip (>>=))
--              ,monotonicity2 (flip snapshot) "flip snapshot"
              ])
          , ("order preservation",
              [ simulEventOrder  "once"       (once
                ::    EventG NumT NumT
                   -> EventG NumT NumT)
              ])
          ]
        )

monoid_E :: TestBatch
monoid_E = monoid (undefined :: EventG NumT T)


-- joinEOccuranceCount :: Property
-- joinEOccuranceCount =
--   forAll (finiteEvent $ finiteEvent arbitrary
--            :: Gen (EventG NumT (EventG NumT T)))
--          ((==) <$> (sum . map (length . toListE_) . toListE_)
--                <*> (length . toListE_ . joinE))

{-
toListE :: EventG t a -> [FutureG t a]
toListE (Event (Future (Max MaxBound, _             ))) = []
toListE (Event (Future (t0          , v `Stepper` e'))) = Future (t0,v) : toListE e'

toListE_ :: EventG t a -> [a]
toListE_ = map futVal . toListE
-}

monotonicity :: (Show a, Arbitrary a, Arbitrary t
                ,Num t, Ord t, Bounded t, Ord t', Bounded t')
             => String -> (EventG t a -> EventG t' a')
             -> (String,Property)
monotonicity n f = (n, property $ monotoneTest f)

monotonicity2 :: (Show a, Show b, Arbitrary a, Arbitrary b, Arbitrary t
                 ,Num t, Ord t, Bounded t, Ord t', Bounded t')
              => String -> (b -> EventG t a -> EventG t' a')
              -> (String,Property)
monotonicity2 n f = (n, property $ monotoneTest2 f)

monotoneTest :: (Ord t', Bounded t') =>
                (EventG t a -> EventG t' a')
             -> EventG t a
             -> Bool
monotoneTest f e = unsafePerformIO (       (evaluate (isMonotoneE . f $ e))
                                    `race` slowTrue)

monotoneTest2 :: (Show a, Show b, Arbitrary a, Arbitrary b, Arbitrary t
                 ,Num t, Ord t, Bounded t, Ord t', Bounded t')
              => (b -> EventG t a -> EventG t' a')
              -> (b ,  EventG t a) -> Bool
monotoneTest2 f (x,e) =
  unsafePerformIO (       (evaluate (isMonotoneE (x `f` e)))
                   `race` slowTrue)

slowTrue :: IO Bool
slowTrue = do threadDelay 10
              return True

-- TODO: Replace this stuff with a use of delay from Data.Later in checkers.


isMonotoneE :: (Ord t, Bounded t) => EventG t a -> Bool
isMonotoneE = liftA2 (||) isNeverE
                          ((uncurry isMonotoneR') . unFuture . eFuture)

isMonotoneE' :: (Ord t, Bounded t) => (Time t) -> EventG t a -> Bool
isMonotoneE' t =
  liftA2 (||) isNeverE
              ((\(t',r) -> t <= t' && isMonotoneR' t' r) . unFuture . eFuture)

isMonotoneR :: (Ord t, Bounded t) => ReactiveG t a -> Bool
isMonotoneR (_ `Stepper` e) = isMonotoneE e

isMonotoneR' :: (Ord t, Bounded t) => Time t -> ReactiveG t a -> Bool
isMonotoneR' t (_ `Stepper` e) = isMonotoneE' t e

simulEventOrder :: ( Arbitrary t, Num t, Ord t, Bounded t
                   , Arbitrary t', Num t', Ord t', Bounded t'
                   , Num t'', Ord t'', Bounded t''
                   , Num t''', Ord t''', Bounded t''')
                => String -> (EventG t t' -> EventG t'' t''')
                -> (String, Property)
simulEventOrder n f =
  (n,forAll genEvent (isStillOrderedE . f))
  where
    genEvent :: ( Arbitrary t1, Num t1, Ord t1, Bounded t1
                , Arbitrary t2, Num t2, Ord t2, Bounded t2)
             => Gen (EventG t1 t2)
    genEvent = liftA futuresE (liftA2 (zipWith future) nondecreasing
                                                          increasing)
    isStillOrderedE :: ( Num t1, Ord t1, Bounded t1
                       , Num t2, Ord t2, Bounded t2) => EventG t1 t2 -> Bool
    isStillOrderedE =
      liftA2 (||) isNeverE
                  (isStillOrderedR . futVal . eFuture)
    
    isStillOrderedR (a `Stepper` e) =
      isStillOrderedE' a e
    
    isStillOrderedE' a =
      liftA2 (||) isNeverE
                  (isStillOrderedR' a . futVal . eFuture)
    
    isStillOrderedR' a (b `Stepper` e) =
      a < b && isStillOrderedE' b e

-- An infinite event.  handy for testing.
infE :: EventG NumT NumT
infE = futuresE (zipWith future [1..] [1..])