{-# LANGUAGE GADTs, KindSignatures #-}
module System.Console.Wizard.Internal ( WizardAction (..)
                                      , PromptString (..)
                                      -- $backend
                                      ) where

type PromptString = String 

-- | Internally, a 'Wizard' is essentially a prompt monad with a 'WizardAction'. A constructor exists for each primitive action, as well
--   as a special \"escape hatch\" constructor ('Backend') used for writing backend-specific primitives and modifiers.
--   Each back-end has a corresponding data type, used as a type parameter for 'Wizard'. This data type is usually opaque, but internally
--   specifies additional primitive actions that are specific to the back-end.
--   'WizardAction' is parameterised by this data type (for use in the 'Backend' constructor), the prompt monad itself (so that modifiers
--   can be made as well as primitives) and the return type of the action.
data WizardAction :: ((* -> *) -> * -> *) -> (* -> *) -> * -> * where
    Line       :: PromptString -> WizardAction b m String
    LinePreset :: PromptString -> String -> String -> WizardAction b m String
    Password   :: PromptString -> Maybe Char -> WizardAction b m String
    Character  :: PromptString -> WizardAction b m Char
    Output     :: String       -> WizardAction b m ()
    OutputLn   :: String       -> WizardAction b m ()    
    Backend    :: b m a        -> WizardAction b m a
-- $backend
--   A short tutorial on writing backends.
--
--   Backends consist of two main components:
--   
--      1. A back-end data type (the type parameter to 'Wizard'), which includes constructors
--         for any primitive actions or modifiers that are specific to the back-end.
--
--      2. An interpreter function, of type @Wizard DataType a -> B (Maybe a)@ for some type @B@ (depending on the backend).
--         Typically this function will provide semantics for each 'WizardAction' using 'runRecPromptM' or similar.
--   
--   The 'Backend' constructor can be used to add back-end specific primitives and modifiers.
-- 
--   As an example, suppose I am writing a back-end to @IO@, like "System.Console.Wizard.BasicIO".
--   One additional primitive action that I might want to include is the ability to run arbitrary @IO@ actions while a wizard is running.
--   So, my backend data type will be:
--
-- @
-- data MyBackend (m :: * -> *) r = ArbitraryIO (IO r) -- kind signature to avoid defaulting to *
-- @
-- 
--   And my interpreter function will be:
--
-- @
--   runWizardMyBackend :: Wizard MyBackend a -> IO a
--   runWizardMyBackend (Wizard (MaybeT c)) = runRecPromptM f c
--         where f :: WizardAction MyBackend (RecPrompt (WizardAction MyBackend)) a -> IO a  
--               f (Output s) = putStr s
--               f (...     ) = ...
--               f (Backend (ArbitraryIO io)) = io
-- @
-- 
-- And then the action can be easily defined:
--
-- @
--   runIO :: IO a -> Wizard MyBackend a
--   runIO = prompt . Backend . ArbitraryIO 
-- @
--
-- I might also want to include a /modifier/, which say, colours any output text green. Assuming I have a function
-- @
--    withGreenText :: IO a -> IO a
-- @
-- which causes any output produced by the input action to be coloured green, we can use the 'Backend' constructor to transform
-- this into a wizard modifier.
-- 
-- @
--data MyBackend m r = ArbitraryIO (IO r)
--                   | GreenText (m r)
--
--runWizardMyBackend :: Wizard MyBackend
--runWizardMyBackend (Wizard (MaybeT c)) = runRecPromptM f c
--      where f :: WizardAction MyBackend (RecPrompt (WizardAction MyBackend)) a -> IO a  
--            f (Output s) = putStr s
--            f (...     ) = ...
--            f (Backend (ArbitraryIO io)) = io
--            f (Backend (GreenText a)) = withGreenText $ runRecPromptM f a
--
--greenText :: Wizard MyBackend a -> Wizard MyBackend a
--greenText (Wizard (MaybeT a)) = prompt (Backend (GreenText a))
-- @
--
--