{-|
    This module provides context connector records.

    A context connector record is a record of connectors (consumers or producers) which depend on
    some data, called the context.
-}
module Data.Record.Signal.Context (

    -- * Context connector records
    ContextConsumerRecord,
    ContextProducerRecord,
    ContextConnectorRecord,
    ContextConnectorStyle,
    consume,
    produce

) where

    -- Control
    import Control.Arrow.Operations         as ArrowOperations
    import Control.Arrow.Transformer.Reader as ReaderArrow

    -- Data
    import           Data.Record         as Record
    import           Data.Record.Context as ContextRecord
    import           Data.Record.Signal  as SignalRecord  hiding (consume, produce)
    import qualified Data.Record.Signal  as SignalRecord

    -- FRP.Grapefruit
    import FRP.Grapefruit.Circuit as Circuit
    import FRP.Grapefruit.Signal  as Signal  hiding (consume, produce)

    -- |Records which contain functions from contexts to consumers as values.
    type ContextConsumerRecord context record = ContextConnectorRecord context Consumer record

    -- |Records which contain functions from contexts to producers as values.
    type ContextProducerRecord context record = ContextConnectorRecord context Producer record

    {-|
        Records which contain functions from contexts to connectors (consumers or producers) as
        values.
    -}
    type ContextConnectorRecord context connector record = record (ContextConnectorStyle context
                                                                                         connector)

    type ContextConnectorStyle context connector = ContextStyle context (ConnectorStyle connector)

    {-|
        Converts a record of context consumers into a reader arrow which consumes a corresponding
        record of signals. The concrete context has to be provided as the environment of the reader
        arrow.
    -}
    consume :: (Record SignalKind record)
            => ContextConsumerRecord context record
            -> ReaderArrow context (Circuit era) (SignalRecord era record) ()
    consume = connect SignalRecord.consume

    {-|
        Converts a record of context producers into a reader arrow which produces a corresponding
        record of signals. The concrete context has to be provided as the environment of the reader
        arrow.
    -}
    produce :: (Record SignalKind record)
            => ContextProducerRecord context record
            -> ReaderArrow context (Circuit era) () (SignalRecord era record)
    produce = connect SignalRecord.produce

    connect :: (Record SignalKind record)
            => (ConnectorRecord connector record -> Circuit era i o)
            -> ContextConnectorRecord context connector record
            -> ReaderArrow context (Circuit era) i o
    connect signalRecordConnect contextConnectorRecord = arrow' where

        arrow' = proc i -> do
                     context <- readState -< ()
                     liftReader $ signalRecordConnect (contextConnectorRecord `app` context) -<< i