{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

{- |
Periodic clocks are defined by a stream of ticks with periodic time differences.
They model subclocks of a fixed reference clock.
The time differences are supplied at the type level.
-}
module FRP.Rhine.Clock.Periodic (Periodic (Periodic)) where

-- base
import Data.List.NonEmpty hiding (unfold)
import Data.Maybe (fromMaybe)
import GHC.TypeLits (KnownNat, Nat, natVal)

-- dunai
import Data.MonadicStreamFunction

-- monad-schedule
import Control.Monad.Schedule.Trans

-- rhine
import FRP.Rhine.Clock
import FRP.Rhine.Clock.Proxy

-- * The 'Periodic' clock

{- | A clock whose tick lengths cycle through
   a (nonempty) list of type-level natural numbers.
   E.g. @Periodic '[1, 2]@ ticks at times 1, 3, 4, 5, 7, 8, etc.

   The waiting side effect is formal, in 'ScheduleT'.
   You can use e.g. 'runScheduleIO' to produce an actual delay.
-}
data Periodic (v :: [Nat]) where
  Periodic :: Periodic (n : ns)

instance
  (Monad m, NonemptyNatList v) =>
  Clock (ScheduleT Integer m) (Periodic v)
  where
  type Time (Periodic v) = Integer
  type Tag (Periodic v) = ()
  initClock :: Periodic v
-> RunningClockInit
     (ScheduleT Integer m) (Time (Periodic v)) (Tag (Periodic v))
initClock Periodic v
cl =
    forall (m :: Type -> Type) a. Monad m => a -> m a
return
      ( forall (m :: Type -> Type) a. Monad m => NonEmpty a -> MSF m () a
cycleS (forall (v :: [Nat]).
NonemptyNatList v =>
Periodic v -> NonEmpty Integer
theList Periodic v
cl) forall {k} (cat :: k -> k -> Type) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> MSF m a a
withSideEffect forall (m :: Type -> Type) diff.
Monad m =>
diff -> ScheduleT diff m ()
wait forall {k} (cat :: k -> k -> Type) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall (m :: Type -> Type) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith forall a. Num a => a -> a -> a
(+) Integer
0 forall (a :: Type -> Type -> Type) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& forall (a :: Type -> Type -> Type) b c.
Arrow a =>
(b -> c) -> a b c
arr (forall a b. a -> b -> a
const ())
      , Integer
0
      )

instance GetClockProxy (Periodic v)

-- * Type-level trickery to extract the type value from the singleton

data HeadClProxy (n :: Nat) where
  HeadClProxy :: Periodic (n : ns) -> HeadClProxy n

headCl :: KnownNat n => Periodic (n : ns) -> Integer
headCl :: forall (n :: Nat) (ns :: [Nat]).
KnownNat n =>
Periodic (n : ns) -> Integer
headCl Periodic (n : ns)
cl = forall (n :: Nat) (proxy :: Nat -> Type).
KnownNat n =>
proxy n -> Integer
natVal forall a b. (a -> b) -> a -> b
$ forall (n :: Nat) (n :: [Nat]). Periodic (n : n) -> HeadClProxy n
HeadClProxy Periodic (n : ns)
cl

tailCl :: Periodic (n1 : n2 : ns) -> Periodic (n2 : ns)
tailCl :: forall (n1 :: Nat) (n2 :: Nat) (ns :: [Nat]).
Periodic (n1 : n2 : ns) -> Periodic (n2 : ns)
tailCl Periodic (n1 : n2 : ns)
Periodic = forall (n :: Nat) (ns :: [Nat]). Periodic (n : ns)
Periodic

class NonemptyNatList (v :: [Nat]) where
  theList :: Periodic v -> NonEmpty Integer

instance KnownNat n => NonemptyNatList '[n] where
  theList :: Periodic '[n] -> NonEmpty Integer
theList Periodic '[n]
cl = forall (n :: Nat) (ns :: [Nat]).
KnownNat n =>
Periodic (n : ns) -> Integer
headCl Periodic '[n]
cl forall a. a -> [a] -> NonEmpty a
:| []

instance
  (KnownNat n1, KnownNat n2, NonemptyNatList (n2 : ns)) =>
  NonemptyNatList (n1 : n2 : ns)
  where
  theList :: Periodic (n1 : n2 : ns) -> NonEmpty Integer
theList Periodic (n1 : n2 : ns)
cl = forall (n :: Nat) (ns :: [Nat]).
KnownNat n =>
Periodic (n : ns) -> Integer
headCl Periodic (n1 : n2 : ns)
cl forall a. a -> NonEmpty a -> NonEmpty a
<| forall (v :: [Nat]).
NonemptyNatList v =>
Periodic v -> NonEmpty Integer
theList (forall (n1 :: Nat) (n2 :: Nat) (ns :: [Nat]).
Periodic (n1 : n2 : ns) -> Periodic (n2 : ns)
tailCl Periodic (n1 : n2 : ns)
cl)

-- * Utilities

-- TODO Port back to dunai when naming issues are resolved

-- | Repeatedly outputs the values of a given list, in order.
cycleS :: Monad m => NonEmpty a -> MSF m () a
cycleS :: forall (m :: Type -> Type) a. Monad m => NonEmpty a -> MSF m () a
cycleS NonEmpty a
as = forall (m :: Type -> Type) a b.
Monad m =>
(a -> (b, a)) -> a -> MSF m () b
unfold (forall (a :: Type -> Type -> Type) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second (forall a. a -> Maybe a -> a
fromMaybe NonEmpty a
as) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. NonEmpty a -> (a, Maybe (NonEmpty a))
uncons) NonEmpty a
as

{-
-- TODO Port back to dunai when naming issues are resolved
delayList :: [a] -> MSF a a
delayList [] = id
delayList (a : as) = delayList as >>> delay a
-}