-- |
-- Module      : Streamly.Time
-- Copyright   : (c) 2017 Harendra Kumar
--
-- License     : BSD3
-- Maintainer  : harendra.kumar@gmail.com
-- Stability   : experimental
-- Portability : GHC
--
-- Time utilities for reactive programming.

module Streamly.Time
{-# DEPRECATED
   "Please use the \"rate\" combinator instead of the functions in this module"
  #-}
    ( periodic
    , withClock
    )
where

import Control.Monad (when)
import Control.Concurrent (threadDelay)

-- | Run an action forever periodically at the given frequency specified in per
-- second (Hz).
--
-- @since 0.1.0
{-# DEPRECATED periodic "Please use the \"rate\" combinator instead" #-}
periodic :: Int -> IO () -> IO ()
periodic freq action = do
    action
    threadDelay (1000000 `div` freq)
    periodic freq action

-- | Run a computation on every clock tick, the clock runs at the specified
-- frequency. It allows running a computation at high frequency efficiently by
-- maintaining a local clock and adjusting it with the provided base clock at
-- longer intervals.  The first argument is a base clock returning some notion
-- of time in microseconds. The second argument is the frequency in per second
-- (Hz). The third argument is the action to run, the action is provided the
-- local time as an argument.
--
-- @since 0.1.0
{-# DEPRECATED withClock "Please use the \"rate\" combinator instead" #-}
withClock :: IO Int -> Int -> (Int -> IO ()) -> IO ()
withClock clock freq action = do
    t <- clock
    go t period period t 0

    where

    period = 1000000 `div` freq

    -- Note that localTime is roughly but not exactly equal to (lastAdj + tick
    -- * n).  That is because we do not abruptly adjust the clock skew instead
    -- we adjust the tick size.
    go lastAdj delay tick localTime n = do
        action localTime
        when (delay > 0) $ threadDelay delay

        if n == freq
        then do
            (t, newTick, newDelay) <- adjustClock lastAdj localTime delay
            go t newDelay newTick (localTime + newTick) 0
        else go lastAdj delay tick (localTime + tick) (n + 1)

    -- Adjust the tick size rather than the clock to avoid abrupt changes
    -- resulting in jittery behavior at the end of every interval.
    adjustClock lastAdj localTime delay = do
        baseTime <- clock
        let newTick    = period + (baseTime - localTime) `div` freq
            lastPeriod = (baseTime - lastAdj) `div` freq
            newDelay   = max 0 (delay + period - lastPeriod)
        return (baseTime, newTick, newDelay)