    {-# LANGUAGE DeriveDataTypeable, DeriveFunctor, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeOperators, GeneralizedNewtypeDeriving, Trustworthy #-}
    module System.Console.Wizard.Haskeline
            ( UnexpectedEOF (..)
            , Haskeline
            , haskeline
            , withSettings
            , WithSettings(..)
            ) where
    import System.Console.Wizard
    import System.Console.Wizard.Internal
    import System.Console.Haskeline
    import Control.Monad.Trans
    import Control.Monad.Trans.Maybe
    import Control.Exception
    import Data.Typeable

    -- | The Haskeline back-end will throw this exception if EOF is encountered
    --   when it is not expected. Specifically, when actions such as 'getInputLine' return 'Nothing'.
    data UnexpectedEOF = UnexpectedEOF deriving (Show, Typeable)
    instance Exception UnexpectedEOF

    -- | Haskeline supports all the following features completely.
    newtype Haskeline a = Haskeline (( Output
                                   :+: OutputLn
                                   :+: Line
                                   :+: Character
                                   :+: LinePrewritten
                                   :+: Password
                                   :+: ArbitraryIO
                                   :+: WithSettings) a)
                      deriving ( (:<:) Output
                               , (:<:) OutputLn
                               , (:<:) Line
                               , (:<:) Character
                               , (:<:) LinePrewritten
                               , (:<:) Password
                               , (:<:) ArbitraryIO
                               , (:<:) WithSettings
                               , Functor
                               , Run (InputT IO)
                               )

    -- | Modifies a wizard so that it will run with different Haskeline 'Settings' to the top level input monad.
    withSettings :: (WithSettings :<: b) => Settings IO -> Wizard b a -> Wizard b a
    withSettings sets (Wizard (MaybeT v)) = Wizard $ MaybeT $ inject (WithSettings sets v)

    data WithSettings w = WithSettings (Settings IO) w deriving (Functor)

    instance Run (InputT IO) Output          where runAlgebra (Output s w)               = outputStr s                       >> w
    instance Run (InputT IO) OutputLn        where runAlgebra (OutputLn s w)             = outputStrLn s                     >> w
    instance Run (InputT IO) Line            where runAlgebra (Line s w)                 = getInputLine s                    >>= mEof w
    instance Run (InputT IO) Character       where runAlgebra (Character s w)            = getInputChar s                    >>= mEof w
    instance Run (InputT IO) LinePrewritten  where runAlgebra (LinePrewritten p s1 s2 w) = getInputLineWithInitial p (s1,s2) >>= mEof w
    instance Run (InputT IO) Password        where runAlgebra (Password p mc w)          = getPassword mc p                  >>= mEof w
    instance Run (InputT IO) ArbitraryIO     where runAlgebra (ArbitraryIO iov f)        = liftIO iov                        >>= f
    instance Run (InputT IO) WithSettings    where runAlgebra (WithSettings sets w)      = liftIO (runInputT sets w)

    mEof = maybe (throw UnexpectedEOF)

    -- | A simple identity function, used to restrict types if the type inferred by GHC is too general.
    --   You could achieve the same effect with a type signature, but this is slightly less typing.
    haskeline :: Wizard Haskeline a -> Wizard Haskeline a
    haskeline = id