-- |
-- Module      : Streamly.Internal.Data.Time
-- Copyright   : (c) 2017 Composewell Technologies
--
-- License     : BSD3
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC
--
-- Time utilities for reactive programming.

module Streamly.Internal.Data.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 :: Int -> IO () -> IO ()
periodic Int
freq IO ()
action = do
    IO ()
action
    Int -> IO ()
threadDelay (Int
1000000 forall a. Integral a => a -> a -> a
`div` Int
freq)
    Int -> IO () -> IO ()
periodic Int
freq IO ()
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 :: IO Int -> Int -> (Int -> IO ()) -> IO ()
withClock IO Int
clock Int
freq Int -> IO ()
action = do
    Int
t <- IO Int
clock
    forall {b}. Int -> Int -> Int -> Int -> Int -> IO b
go Int
t Int
period Int
period Int
t Int
0

    where

    period :: Int
period = Int
1000000 forall a. Integral a => a -> a -> a
`div` Int
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 :: Int -> Int -> Int -> Int -> Int -> IO b
go Int
lastAdj Int
delay Int
tick Int
localTime Int
n = do
        Int -> IO ()
action Int
localTime
        forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
delay forall a. Ord a => a -> a -> Bool
> Int
0) forall a b. (a -> b) -> a -> b
$ Int -> IO ()
threadDelay Int
delay

        if Int
n forall a. Eq a => a -> a -> Bool
== Int
freq
        then do
            (Int
t, Int
newTick, Int
newDelay) <- Int -> Int -> Int -> IO (Int, Int, Int)
adjustClock Int
lastAdj Int
localTime Int
delay
            Int -> Int -> Int -> Int -> Int -> IO b
go Int
t Int
newDelay Int
newTick (Int
localTime forall a. Num a => a -> a -> a
+ Int
newTick) Int
0
        else Int -> Int -> Int -> Int -> Int -> IO b
go Int
lastAdj Int
delay Int
tick (Int
localTime forall a. Num a => a -> a -> a
+ Int
tick) (Int
n forall a. Num a => a -> a -> a
+ Int
1)

    -- Adjust the tick size rather than the clock to avoid abrupt changes
    -- resulting in jittery behavior at the end of every interval.
    adjustClock :: Int -> Int -> Int -> IO (Int, Int, Int)
adjustClock Int
lastAdj Int
localTime Int
delay = do
        Int
baseTime <- IO Int
clock
        let newTick :: Int
newTick    = Int
period forall a. Num a => a -> a -> a
+ (Int
baseTime forall a. Num a => a -> a -> a
- Int
localTime) forall a. Integral a => a -> a -> a
`div` Int
freq
            lastPeriod :: Int
lastPeriod = (Int
baseTime forall a. Num a => a -> a -> a
- Int
lastAdj) forall a. Integral a => a -> a -> a
`div` Int
freq
            newDelay :: Int
newDelay   = forall a. Ord a => a -> a -> a
max Int
0 (Int
delay forall a. Num a => a -> a -> a
+ Int
period forall a. Num a => a -> a -> a
- Int
lastPeriod)
        forall (m :: * -> *) a. Monad m => a -> m a
return (Int
baseTime, Int
newTick, Int
newDelay)