-- |
-- 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 Int -> Int -> Int
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
    Int -> Int -> Int -> Int -> Int -> IO ()
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 Int -> Int -> Int
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
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
delay Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
threadDelay Int
delay

        if Int
n Int -> Int -> Bool
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 Int -> Int -> Int
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 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
tick) (Int
n Int -> Int -> Int
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 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
baseTime Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
localTime) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
freq
            lastPeriod :: Int
lastPeriod = (Int
baseTime Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lastAdj) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
freq
            newDelay :: Int
newDelay   = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
delay Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
period Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lastPeriod)
        (Int, Int, Int) -> IO (Int, Int, Int)
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
baseTime, Int
newTick, Int
newDelay)