{- |
Module      :  $Header$
Copyright   :  (c) Simon Bergot
License     :  BSD3

Maintainer  :  simon.bergot@gmail.com
Stability   :  unstable
Portability :  portable

Base types shared by several EasyConsole modules.
-}

module System.Console.ArgParser.BaseType where

import           Control.Applicative     ((<*>), Applicative, pure)
import qualified Data.Map as M (Map)

-- | Simple command line arg
type Arg   = String
-- | List of args provided
type Args  = [Arg]
-- | Flag collection with corresponding args
type Flags = M.Map Arg Args
-- | Structured args to be parsed.
--   Pair of (positionnal arguments, flag arguments)
type NiceArgs = (Args, Flags)
-- | Type representing the result of the parse.
--   Right val in case of success or
--   Left msg if there was an error.
type ParseResult a = Either String a

-- | Data structure describing a parameter
data ParamDescr = ParamDescr
  { argUsageFmt :: String -> String -- ^ Short description of the parameter format
  , argCategory :: String -- ^ Category of parameter (optional/mandatory)
  , argFormat   :: String -> String -- ^ Format of the parameter to provide
  , argDescr    :: String -- ^ Description of the parameter
  , argMetaVar  :: String -- ^ Description of the parameter in the usage
  }

-- | Returns a short description of the input format
--   of a parameter.
argUsage :: ParamDescr -> String
argUsage d = argUsageFmt d $ argMetaVar d

-- | Returns a long description of the input format
--   of a parameter.
getArgFormat :: ParamDescr -> String
getArgFormat d = argFormat d $ argMetaVar d

-- | A parser actual function
data Parser a = Parser (NiceArgs -> (ParseResult a, NiceArgs))

-- | Represent a full parameter spec
data ParserSpec a = ParserSpec
  { getParserParams :: [ParamDescr]
  , getParserFun    :: Parser a
  }

instance Functor ParserSpec where
  fmap f p = p {getParserFun = fmap f $ getParserFun p}

instance Applicative ParserSpec where
  pure val = ParserSpec [] $ pure val
  ParserSpec d1 p1 <*> ParserSpec d2 p2 =
    ParserSpec (d1 ++ d2) (p1 <*> p2)

-- | A special action with more possibilities.
--   The full arg list will be provided,
--   with the command line spec itself.
type SpecialAction a =
  CmdLnInterface a
  -> NiceArgs
  -> ParseResult a

-- | A special parser allowing to
--   perform standard actions.
--   Used for version/help/subparsers.
type SpecialFlag a = (ParserSpec Bool, SpecialAction a)

-- | A command line application, with a parser and a description
data CmdLnInterface a = CmdLnInterface
  { cmdArgParser  :: ParserSpec a    -- ^ The argument parser
  , specialFlags  :: [SpecialFlag a] -- ^ The special flags
  , getAppName    :: String          -- ^ The application name.
                                     --   Usally the binary name.
  , getAppVersion :: Maybe String    -- ^ Optional application version
  , getAppDescr   :: Maybe String    -- ^ Optional description
  , getAppEpilog  :: Maybe String    -- ^ Optional epilog
  }

instance Functor Parser where
  fmap f (Parser g) = Parser (\args ->
    let (res, newargs) = g args
    in (fmap f res, newargs))

instance Applicative Parser where
  pure val = Parser parser where
    parser args = (Right val, args)
  (Parser f) <*> (Parser g) = Parser (\args ->
    let (h, newargs) = f args
        (res, lastargs) = g newargs
    in (h <*> res, lastargs))