content: {-# language FlexibleInstances #-}
  {-# language MultiParamTypeClasses #-}
  {-# language ExistentialQuantification #-}
  {-# language ScopedTypeVariables #-}
  
  module Eve.Internal.Async
    ( dispatchActionAsync
    , asyncActionProvider
    ) where
  
  import Eve.Internal.Actions
  import Eve.Internal.AppState
  import Eve.Internal.States
  
  import Control.Concurrent
  import Control.Monad
  import Control.Monad.State
  import Control.Lens
  import Data.Typeable
  
  -- | Dispatch an action which is generated by some IO. Note that state of the application may have changed
  -- between calling 'dispatchActionAsync' and running the resulting 'Action'
  dispatchActionAsync
    :: (MonadIO m, HasStates base, Typeable m, Typeable base) => IO (AppT base m ()) -> ActionT base zoomed m ()
  dispatchActionAsync asyncAction = runApp $ do
    mQueue <- use asyncQueue
    case mQueue of
      Nothing -> return ()
      Just queue -> liftIO . void . forkIO $ asyncAction >>= writeChan queue
  
  -- | This allows long-running IO processes to provide 'Action's to the application asyncronously.
  -- 
  -- 'asyncEventProvider' is simpler to use, however 'asyncActionProvider' provides
  -- more power and expressivity. When in doubt, 'asyncEventProvider' probably meets
  -- your needs.
  --
  -- Don't let the type signature confuse you; it's much simpler than it seems.
  --
  -- Let's break it down:
  --
  -- When you call 'asyncActionProvider' you pass it a function which accepts a @dispatch@ function as an argument
  -- and then calls it with various 'Action's within the resulting 'IO'. The
  -- @dispatch@ function it is passed will have type @(App () -> IO ())@
  --
  -- Note that this function calls forkIO internally, so there's no need to do that yourself.
  --
  -- Here's an example:
  --
  -- > data Timer = Timer
  -- > myTimer :: (App () -> IO ()) -> IO ()
  -- > myTimer dispatch = forever $ dispatch (myInt += 1) >> threadDelay 1000000
  -- >
  -- > myInit :: App ()
  -- > myInit = asyncActionProvider myTimer
  asyncActionProvider :: (MonadIO m, HasStates base, Typeable m, Typeable base) => ((AppT base m () -> IO ()) -> IO ()) -> ActionT base zoomed m ()
  asyncActionProvider provider = runApp $ do
    mQueue <- use asyncQueue
    case mQueue of
      Nothing -> return ()
      Just queue -> liftIO . void . forkIO $ provider (writeChan queue)  where
  