{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, GADTs, FlexibleInstances, ScopedTypeVariables, DeriveDataTypeable, TypeSynonymInstances, NamedFieldPuns #-}

{-|
This module defines a 'Neuron' which generates values based on a given sequence at a given interval.
If it reaches the end of a sequence it initiates 'dissolving'.
You 'grow' default version of it, which gives you an infinite source of random 'Int's at random interval of maximum length of 1
second, in 'Incubation' by using something like:

> nerveRandom <- (growNeuron :: NerveOnlyFrom (SequenceNeuron Int)) defaultOptions

or for an infinite source of ones with same random interval:

> nerveOnes <- (growNeuron :: NerveOnlyFrom (SequenceNeuron Int)) (\o -> o { valueSource = repeat 1 })

This 'Neuron' is an example of a 'Neuron' with a parametrized type. Check also "Control.Etage.Function" for a 'Neuron' with both receiving
and sending 'Impulse's types parametrized. It does not process any (receiving) 'Impulse's.
-}

module Control.Etage.Sequence (
  SequenceNeuron,
  SequenceFromImpulse,
  SequenceForImpulse,
  SequenceOptions,
  NeuronOptions(..)
) where

import Control.Concurrent
import Control.Monad
import Data.Data
import System.Random

import Control.Etage

defaultMaxInterval :: Int
defaultMaxInterval = 1000000 -- microseconds, 1 second

data SequenceNeuron v = SequenceNeuron (SequenceOptions v) deriving (Typeable, Data)

instance Typeable v => Show (SequenceNeuron v) where
  show = show . typeOf

{-|
'Impulse's from 'SequenceNeuron', @'IValue' v@.
-}
type SequenceFromImpulse v = NeuronFromImpulse (SequenceNeuron v)
-- | 'Impulse's for 'SequenceNeuron'. This 'Neuron' does not define any 'Impulse's it would receive, 'NoImpulse'.
type SequenceForImpulse v = NeuronForImpulse (SequenceNeuron v)
{-|
Options for 'SequenceNeuron'. Those options are defined:

[@valueSource :: \[v\]@] The list of values to send. If the end of the list is reached, 'Neuron' initiates 'dissolving'. Default
is an infinite list of values of type @v@ generated by the 'StdGen' random generator.

[@intervalSource :: \['Int'\]@] The list of intervals between values. It is defined as a delay in microseconds before the next value
is send. If the end of the list is reached, 'Neuron' initiates 'dissolving'. Default is a list of random delays with maximum
length of 1 second generated by the 'StdGen' random generator.
-}
type SequenceOptions v = NeuronOptions (SequenceNeuron v)

-- | A 'Neuron' which generates values based on a given sequence at a given interval.
instance (Real v, Random v, Show v, Typeable v) => Neuron (SequenceNeuron v) where
  type NeuronFromImpulse (SequenceNeuron v) = IValue v
  type NeuronForImpulse (SequenceNeuron v) = NoImpulse
  data NeuronOptions (SequenceNeuron v) = SequenceOptions {
      valueSource :: [v],
      intervalSource :: [Int] -- microseconds
    } deriving (Eq, Ord, Read, Show, Data)
  
  mkDefaultOptions = do
    generator <- newStdGen
    generator' <- newStdGen
    return SequenceOptions {
        valueSource = randoms generator,
        intervalSource = randomRs (0, defaultMaxInterval) generator'
      }
  
  grow options = return $ SequenceNeuron options
  
  live nerve n@(SequenceNeuron SequenceOptions { valueSource, intervalSource }) = do
    forM_ (zip valueSource intervalSource) $ \(v, i) -> do
      threadDelay i
      time <- getCurrentImpulseTime
      sendFromNeuron nerve $ IValue time v
    dissolving n