{-# OPTIONS_GHC -fno-cse #-}
module Internal.CSeg (

    CSeg,
    currentValCapsule,
    producer

) where

    -- Control
    import Control.Applicative     as Applicative
    import Control.Arrow           as Arrow
    import Control.Compose         as Compose
    import Control.Concurrent.MVar as MVar

    -- Data
    import Data.Unique as Unique

    -- System
    import System.IO.Unsafe as UnsafeIO

    -- Internal
    import Internal.Capsule as Capsule
    import Internal.Circuit as Circuit

    -- FRP.Grapefruit
    import FRP.Grapefruit.Circuit as Circuit

    newtype CSeg val = CSeg (((->) Unique :. Capsule) val) deriving (Functor, Applicative)

    currentValCapsule :: Unique -> CSeg val -> Capsule val
    currentValCapsule currentTimeID (CSeg capsuleGen) = unO capsuleGen currentTimeID

    producer :: IO val -> Circuit era () (CSeg val)
    producer readVal = proc _ -> do
                           maybeValVar <- act -< newMVar Nothing
                           addECFinalizer <- getECFinalizerAdd -< ()
                           returnA -< CSeg $
                                      O (unsafeCurrentValCapsule readVal maybeValVar addECFinalizer)

    {-# NOINLINE unsafeCurrentValCapsule #-}
    unsafeCurrentValCapsule :: IO val
                            -> MVar (Maybe val)
                            -> (IO () -> IO ())
                            -> Unique
                            -> Capsule val
    unsafeCurrentValCapsule readVal maybeValVar addECFinalizer timeID = unsafePerformIO $
                                                                        seq timeID $
                                                                        getCurrentValCapsule where

        getCurrentValCapsule = do
                                   maybeVal <- readMVar maybeValVar
                                   case maybeVal of
                                       Nothing            -> do
                                                                 val <- readVal
                                                                 putMVar maybeValVar (Just val)
                                                                 addECFinalizer resetMaybeValVar
                                                                 return (Applicative.pure val)
                                       justVal@(Just val) -> do
                                                                 putMVar maybeValVar justVal
                                                                 return (Applicative.pure val)

        resetMaybeValVar     = do
                                   readMVar maybeValVar
                                   putMVar maybeValVar Nothing