-- | Utilities to run 'ClSF's at the speed of combined clocks
--   when they are defined only for a constituent clock.

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}

module FRP.Rhine.ClSF.Upsample where

-- base
import Data.Semigroup

-- dunai
import Control.Monad.Trans.MSF.Reader
--import Data.MonadicStreamFunction

-- rhine
import FRP.Rhine.ClSF.Core
import FRP.Rhine.Schedule

-- | An 'MSF' can be given arbitrary other arguments
--   that cause it to tick without doing anything
--   and replicating the last output.
upsampleMSF :: Monad m => b -> MSF m a b -> MSF m (Either arbitrary a) b
upsampleMSF b msf = right msf >>> accumulateWith (<>) (Right b) >>> arr fromRight
  where
    fromRight (Right b') = b'
    fromRight (Left  _ ) = error "fromRight: This case never occurs in upsampleMSF."
-- Note that the Semigroup instance of Either a arbitrary
-- updates when the first argument is Right.


-- | Upsample a 'ClSF' to a parallel clock.
--   The given 'ClSF' is only called when @clR@ ticks,
--   otherwise the last output is replicated
--   (with the given @b@ as initialisation).
upsampleR
  :: (Monad m, Time clL ~ Time clR)
  => b -> ClSF m clR a b -> ClSF m (ParallelClock m clL clR) a b
upsampleR b clsf = readerS $ arr remap >>> upsampleMSF b (runReaderS clsf)
  where
    remap (TimeInfo { tag = Left  tag     }, _) = Left tag
    remap (TimeInfo { tag = Right tag, .. }, a) = Right (TimeInfo { .. }, a)


-- | Upsample a 'ClSF' to a parallel clock.
--   The given 'ClSF' is only called when @clL@ ticks,
--   otherwise the last output is replicated
--   (with the given @b@ as initialisation).
upsampleL
  :: (Monad m, Time clL ~ Time clR)
  => b -> ClSF m clL a b -> ClSF m (ParallelClock m clL clR) a b
upsampleL b clsf = readerS $ arr remap >>> upsampleMSF b (runReaderS clsf)
  where
    remap (TimeInfo { tag = Right tag     }, _) = Left tag
    remap (TimeInfo { tag = Left  tag, .. }, a) = Right (TimeInfo { .. }, a)