{-# LANGUAGE TupleSections #-}
module Reactive.Bacon.EventStream.Timed where

import Reactive.Bacon.Core
import Reactive.Bacon.EventStream.IO
import Reactive.Bacon.EventStream.Monadic
import System.Time
import Control.Concurrent(threadDelay, forkIO)
import Control.Monad(void, unless)
import Data.IORef

laterE :: TimeDiff -> a -> IO (EventStream a)
laterE diff x = timedE [(diff, x)] >>= return . fst

periodicallyE :: TimeDiff -> a -> IO (EventStream a, Disposable)
periodicallyE diff x = timedE (repeat (diff, x))

sequentiallyE :: TimeDiff -> [a] -> IO (EventStream a)
sequentiallyE delay xs = timedE (map (delay,) xs) >>= return . fst

timedE :: [(TimeDiff, a)] -> IO (EventStream a, Disposable)
timedE events = fromStoppableProcess $ \sink stopper -> void $ forkIO $ serve sink events stopper
  where serve sink [] _ = sink End
        serve sink ((diff, event) : events) getStopState = do
          stop <- getStopState
          unless stop $ do
            threadDelay (toMicros diff)
            sink $ Next event
            serve sink events getStopState

delayE :: EventSource s => TimeDiff -> s a -> IO (EventStream a)
delayE diff = flatMapE (laterE diff)

throttleE :: EventSource s => TimeDiff -> s a -> IO (EventStream a)
throttleE diff = switchE (laterE diff)

toMicros :: TimeDiff -> Int
toMicros diff = fromInteger((toPicos diff) `div` 1000000)
 where
    toPicos :: TimeDiff -> Integer
    toPicos (TimeDiff 0 0 0 h m s p) = p + (fromHours h) + (fromMinutes m) + (fromSeconds s)
      where fromSeconds s = 1000000000000 * (toInteger s)
            fromMinutes m = 60 * (fromSeconds m)
            fromHours   h = 60 * (fromMinutes h)

-- | Milliseconds to TimeDiff
milliseconds :: Integral a => a -> TimeDiff
milliseconds ms = noTimeDiff { tdPicosec = (toInteger ms) * 1000000000}

-- | Seconds to TimeDiff
seconds :: Integral a => a -> TimeDiff
seconds = milliseconds . (*1000)