{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS -Wall #-}

-- |
-- Module      :  FRP.Reactive.Internal.Reactive
-- Copyright   :  (c) Conal Elliott 2008
-- License     :  BSD3
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- Representation for 'Reactive' and 'Event' types.  Combined here,
-- because they're mutually recursive.
-- The representation used in this module is based on a close connection
-- between these two types.  A reactive value is defined by an initial
-- value and an event that yields future values; while an event is given
-- as a future reactive value.

module FRP.Reactive.Internal.Reactive
    EventG(..), inEvent, inEvent2, eFutures
  , ReactiveG(..), inREvent, inFutR
  , runE, runR, forkE, forkR
  ) where

import Data.List (intersperse)

import Control.Concurrent (forkIO,ThreadId)

import FRP.Reactive.Internal.Misc
import FRP.Reactive.Internal.Future
import Data.Max
import Data.AddBounds

-- | Events.  Semantically: time-ordered list of future values.
-- Instances: 
-- * 'Monoid': 'mempty' is the event that never occurs, and @e `mappend`
--   e'@ is the event that combines occurrences from @e@ and @e'@.
-- * 'Functor': @fmap f e@ is the event that occurs whenever @e@ occurs,
--   and whose occurrence values come from applying @f@ to the values from
--   @e@.
-- * 'Applicative': @pure a@ is an event with a single occurrence at time
--   -Infinity.  @ef \<*\> ex@ is an event whose occurrences are made from
--   the /product/ of the occurrences of @ef@ and @ex@.  For every occurrence
--   @f@ at time @tf@ of @ef@ and occurrence @x@ at time @tx@ of @ex@, @ef
--   \<*\> ex@ has an occurrence @f x@ at time @tf `max` tx@.  N.B.: I
--   don't expect this instance to be very useful.  If @ef@ has @nf@
--   instances and @ex@ has @nx@ instances, then @ef \<*\> ex@ has @nf*nx@
--   instances.  However, there are only @nf+nx@ possibilities for @tf
--   `max` tx@, so many of the occurrences are simultaneous.  If you think
--   you want to use this instance, consider using 'Reactive' instead.
-- * 'Monad': @return a@ is the same as @pure a@ (as usual).  In @e >>= f@,
--   each occurrence of @e@ leads, through @f@, to a new event.  Similarly
--   for @join ee@, which is somehow simpler for me to think about.  The
--   occurrences of @e >>= f@ (or @join ee@) correspond to the union of the
--   occurrences (temporal interleaving) of all such events.  For example,
--   suppose we're playing Asteroids and tracking collisions.  Each collision
--   can break an asteroid into more of them, each of which has to be tracked
--   for more collisions.  Another example: A chat room has an /enter/ event,
--   whose occurrences contain new events like /speak/.  An especially useful
--   monad-based function is 'joinMaybes', which filters a Maybe-valued
--   event.

newtype EventG t a = Event { eFuture :: FutureG t (ReactiveG t a) }

-- The event representation requires temporal monotonicity but does not
-- enforce it, which invites bugs.  Every operation therefore must be
-- tested for preserving monotonicity.  (Better yet, find an efficient
-- representation that either enforces or doesn't require monotonicity.)

-- Why the newtype for 'EventG?'  Because the 'Monoid' instance of 'Future'
-- does not do what I want for 'EventG'.  It will pick just the
-- earlier-occurring event, while I want an interleaving of occurrences
-- from each.  Similarly for other classes.

-- TODO: Alternative and MonadPlus instances for EventG

-- | Reactive value: a discretely changing value.  Reactive values can be
-- understood in terms of (a) a simple denotational semantics of reactive
-- values as functions of time, and (b) the corresponding instances for
-- functions.  The semantics is given by the function @at :: ReactiveG t a ->
-- (t -> a)@.  A reactive value may also be thought of (and in this module
-- is implemented as) a current value and an event (stream of future values).
-- The semantics of 'ReactiveG' instances are given by corresponding
-- instances for the semantic model (functions):
-- * 'Functor': @at (fmap f r) == fmap f (at r)@, i.e., @fmap f r `at`
--   t == f (r `at` t)@.
-- * 'Applicative': @at (pure a) == pure a@, and @at (s \<*\> r) == at s
--   \<*\> at t@.  That is, @pure a `at` t == a@, and @(s \<*\> r) `at` t
--   == (s `at` t) (r `at` t)@.
-- * 'Monad': @at (return a) == return a@, and @at (join rr) == join (at
--   . at rr)@.  That is, @return a `at` t == a@, and @join rr `at` t ==
--   (rr `at` t) `at` t@.  As always, @(r >>= f) == join (fmap f r)@.
--   @at (r >>= f) == at r >>= at . f@.
-- * 'Monoid': a typical lifted monoid.  If @o@ is a monoid, then
--   @Reactive o@ is a monoid, with @mempty == pure mempty@, and @mappend
--   == liftA2 mappend@.  That is, @mempty `at` t == mempty@, and @(r
--   `mappend` s) `at` t == (r `at` t) `mappend` (s `at` t).@

data ReactiveG t a = a `Stepper` EventG t a

    Applying functions inside of representations

-- | Apply a unary function inside an 'EventG' representation.
inEvent :: (FutureG s (ReactiveG s a) -> FutureG t (ReactiveG t b))
        -> (EventG s a -> EventG t b)
inEvent f = Event . f . eFuture

-- | Apply a unary function inside an 'EventG' representation.
inEvent2 :: (FutureG t (ReactiveG t a) -> FutureG t (ReactiveG t b)
                                       -> FutureG t (ReactiveG t c))
         -> (EventG t a -> EventG t b -> EventG t c)
inEvent2 f = inEvent . f . eFuture

-- | Apply a unary function inside the 'rEvent' part of a 'Reactive'
-- representation.
inREvent :: (EventG    s a -> EventG    t a)
         -> (ReactiveG s a -> ReactiveG t a)
inREvent f (a `Stepper` e) = a `Stepper` f e

-- | Apply a unary function inside the future reactive inside a 'Reactive'
-- representation.
inFutR :: (FutureG s (ReactiveG s b) -> FutureG t (ReactiveG t b))
       -> (ReactiveG s b -> ReactiveG t b)
inFutR = inREvent . inEvent

    Showing values (exposing rep)

-- | Make the event into a list of futures
eFutures :: EventG t a -> [FutureG t a]
eFutures (Event (Future (Max MaxBound,_)))  = []
eFutures (Event (Future (t,a `Stepper` e))) = Future (t,a) : eFutures e

-- TODO: redefine 'eFutures' as an unfold

-- Show a future
sFuture :: (Show t, Show a) => FutureG t a -> String
sFuture (Future (Max MinBound,a)) = "(-infty," ++ show a ++ ")"
sFuture (Future (Max MaxBound,_)) = "(infty,_)"
sFuture (Future (Max (NoBound t),a)) = "(" ++ show t ++ "," ++ show a ++ ")"

-- TODO: Better re-use in sFuture.

-- Truncated show
sFutures :: (Show t, Show a) => [FutureG t a] -> String
sFutures fs =
  let maxleng = 20
      a   = (intersperse "->" . map sFuture) fs
      inf = length (take maxleng a) == maxleng
    if not inf then concat a
               else concat (take maxleng a) ++ "..."

-- TODO: clean up sFutures def: use intercalate, concat before trimming,
-- and define&use a general function for truncating and adding "...".
-- Test.

instance (Show a, Show b) => Show (EventG a b) where
  show = sFutures . eFutures

instance (Show x, Show y) => Show (ReactiveG x y) where
  show (x `Stepper` e) = show x ++ " `Stepper` " ++ show e


-- | Run an event in the current thread.  Use the given time sink to sync
-- time, i.e., to wait for an output time before performing the action.
runE :: forall t. Ord t => Sink t -> Sink (EventG t Action)
runE sync (Event (Future (Max bt,r))) = tsync bt (runR sync r)
   tsync :: AddBounds t -> Sink Action
   tsync MinBound    = id                               -- no wait
   tsync (NoBound t) = (sync t >>)                      -- wait
   tsync MaxBound    = const (return ())                -- finished!

-- TODO: I'm not sure about the MaxBound case.  We could instead just wait
-- forever (cheaply).  Try out this terminating definition instead.

-- | Run an event in a new thread, using the given time sink to sync time.
forkE :: Ord t => Sink t -> EventG t Action -> IO ThreadId
forkE = (fmap.fmap) forkIO runE

-- TODO: Revisit this tsync definition.  For instance, maybe the MaxBound
-- case ought to simply return.

-- | Run a reactive value in the current thread, using the given time sink
-- to sync time.
runR :: Ord t => Sink t -> Sink (ReactiveG t Action)
runR sync (act `Stepper` e) = act >> runE sync e
-- | Run a reactive value in a new thread, using the given time sink to
-- sync time.  The initial action happens in the current thread.
forkR :: Ord t => Sink t -> ReactiveG t Action -> IO ThreadId
forkR = (fmap.fmap) forkIO runR