{-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification #-}

module Rasa.Internal.Action where

import Control.Monad.State
import Control.Monad.Reader
import Data.Dynamic
import Data.Map

import Rasa.Internal.Buffer
import Rasa.Internal.Editor

-- | This is a monad-transformer stack for performing actions against the editor.
-- You register Actions to be run in response to events using 'Rasa.Internal.Scheduler.eventListener'
--
-- Within an Action you can:
--
--      * Use liftIO for IO
--      * Access/edit extensions that are stored globally, see 'ext'
--      * Embed any 'Action's exported other extensions
--      * Embed buffer actions using 'Rasa.Internal.Ext.Directive.bufDo' and 'Rasa.Internal.Ext.Directive.focusDo'
--      * Add\/Edit\/Focus buffers and a few other Editor-level things, see the 'Rasa.Internal.Ext.Directive' module.

newtype Action a = Action
  { runAct :: StateT Editor (ReaderT Hooks IO) a
  } deriving (Functor, Applicative, Monad, MonadState Editor, MonadReader Hooks, MonadIO)

-- | Unwrap and execute an Action (returning the editor state)
execAction :: Editor -> Hooks -> Action () -> IO Editor
execAction editor hooks action  = flip runReaderT hooks $ execStateT (runAct action) editor

-- | Unwrap and evaluate an Action (returning the value)
evalAction :: Editor -> Hooks -> Action a ->IO a
evalAction editor hooks action  = flip runReaderT hooks $ evalStateT (runAct action) editor

-- | This is a monad-transformer stack for performing actions on a specific buffer.
-- You register BufActions to be run by embedding them in a scheduled 'Action' via 'bufferDo' or 'focusDo'
--
-- Within a BufAction you can:
--
--      * Use liftIO for IO
--      * Access/edit buffer extensions; see 'bufExt'
--      * Embed and sequence any 'BufAction's from other extensions
--      * Access/Edit the buffer's 'text'
--
newtype BufAction a = BufAction
  { getBufAction::StateT Buffer (ReaderT Hooks IO) a
  } deriving (Functor, Applicative, Monad, MonadState Buffer, MonadReader Hooks, MonadIO)


-- | A wrapper around event listeners so they can be stored in 'Hooks'.
data Hook = forall a. Hook a

-- | A map of Event types to a list of listeners for that event
type Hooks = Map TypeRep [Hook]