-- | ReactHandle

-- Sometimes it is beneficial to give control to an external main loop,
-- for example OpenGL or a hardware-clocked audio server like JACK.
-- This module makes Dunai compatible with external main loops.

module Data.MonadicStreamFunction.ReactHandle where

-- External
import Control.Monad.IO.Class
import Data.IORef

-- Internal
import Data.MonadicStreamFunction


-- | A storage for the current state of an MSF
type ReactHandle m = IORef (MSF m () ())


-- | Needs to be called before the external main loop is dispatched
reactInit :: MonadIO m => MSF m () () -> m (ReactHandle m)
reactInit = liftIO . newIORef


-- | The callback that needs to be called by the main loop at every cycle
react :: MonadIO m => ReactHandle m -> m ()
react handle = do
  msf <- liftIO $ readIORef handle
  (_, msf') <- unMSF msf ()
  liftIO $ writeIORef handle msf'


-- | Creates two ends of a synchronisation wormhole

-- Often, the external framework may have several parallel loops,
-- for example, OpenGL with a display callback, an idle callback and a keyboard callback.
-- In such cases, one would like to let the different parts communicate.
-- This is done through a wormhole, which is a shared mutable variable
-- that can be written from one part and read from the other.

createWormhole :: MonadIO m => a -> m (MSF m a (), MSF m () a)
createWormhole a = liftIO $ do
  ref <- newIORef a
  return (arrM $ liftIO . writeIORef ref, arrM_ $ liftIO $ readIORef ref)