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

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

Collection of functions which are basically shortcuts
of "System.Console.EasyConsole.Params" versions. If you
cannot find a parameter fitting your needs, you should check this
module.

Values provided to 'parsedBy' and 'andBy' should be created with
the following functions. The types are inferred. ArgParser will use
@readMaybe@ to convert the arguments to haskell values, except for strings
which will be passed unmodified.

Flags can be passed in long form (@--foo@) or short form (@-f@)
You may also provide a prefix form such as @--fo@.

Mandatory parameters will fail if the argument is absent or invalid.
Optional parameters only fail if the argument is invalid (ie @foo@ passed
as @Int@)

Note that single arg parameters need exactly one arg, and that multiple args
parameters can have any number of args (0 included).

-}

module System.Console.ArgParser.QuickParams (
  -- * Parameters without args
    boolFlag
  -- * Parameters with one arg
  -- ** Flags
  , reqFlag
  , optFlag
  -- ** Positional
  , reqPos
  , optPos
  -- * Parameters with multiple args
  -- ** Flags
  , reqFlagArgs
  , optFlagArgs
  -- ** Positionnal
  , posArgs
  -- ** RawRead class
  , RawRead
  ) where

import           Data.Either                       (partitionEithers)
import           Data.List                       (unfoldr)
import           Control.Applicative
import           System.Console.ArgParser.BaseType
import           System.Console.ArgParser.Params

-- | Same function as Text.Read.readMaybe. It is 
--   redefined here for compatibility purpose
readMaybe :: (Read a) => String -> Maybe a
readMaybe s = case reads s of
              [(x, "")] -> Just x
              _ -> Nothing

-- | A typeclass used to define a way a converting
--   string to specific types. It is similar to read.
--   The main difference is that strings are parsed
--   without quotes.
--
-- > rawRead "foo" :: Maybe String == Just "foo"
class RawRead a where
  rawParse :: String -> Maybe (a, String)

instance RawRead Char where
  rawParse s = case s of
    [] -> Nothing
    c:s' -> Just (c, s')

instance RawRead a => RawRead [a] where
  rawParse s = Just (unfoldr rawParse s, [])

instance RawRead Float where
  rawParse = defaultRawParse

instance RawRead Int where
  rawParse = defaultRawParse

defaultRawParse :: Read t => String -> Maybe (t, String)
defaultRawParse s = (\val -> (val , [])) <$> readMaybe s

rawRead :: RawRead a => String -> Maybe a
rawRead s = fst <$> rawParse s

readArg
  :: RawRead a
  => Key
  -> Arg
  -> ParseResult a
readArg key arg = case rawRead arg of
  Just val -> Right val
  Nothing -> Left $ "Could not parse parameter " ++ key ++ "."
    ++ "Unable to convert " ++ arg


-- | A simple command line flag.
--   The parsing function will return True
--   if the flag is present, if the flag is provided to
--   the command line, and False otherwise.
--   For a key @foo@, the flag can either be @--foo@ or @-f@
boolFlag
  :: Key            -- ^ flag key
  -> FlagParam Bool
boolFlag key = FlagParam Short key id

-- | A mandatory positional argument parameter
reqPos
  :: RawRead a
  => Key         -- ^ Param name
  -> StdArgParam a
reqPos key = StdArgParam Mandatory Pos key (SingleArgParser $ readArg key)

-- | An optional positional argument parameter
optPos
  :: RawRead a
  => a                  -- ^ Default value
  -> Key                -- ^ Param name
  -> StdArgParam a
optPos val key = StdArgParam (Optional val) Pos key (SingleArgParser $ readArg key)

-- | A mandatory flag argument parameter
reqFlag
  :: RawRead a
  => Key         -- ^ Flag name
  -> StdArgParam a
reqFlag key = StdArgParam Mandatory Flag key (SingleArgParser $ readArg key)

-- | An optional flag argument parameter
optFlag
  :: RawRead a
  => a                  -- ^ Default value
  -> Key                -- ^ Flag name
  -> StdArgParam a
optFlag val key = StdArgParam (Optional val) Flag key (SingleArgParser $ readArg key)

readArgs
  :: RawRead a
  => Key
  -> b
  -> (b -> a -> b)
  -> Args
  -> ParseResult b
readArgs key initval accum args = case errors of
  [] -> Right $ foldl accum initval values
  _  -> Left $ unlines errors
 where
  (errors, values) = partitionEithers $ map (readArg key) args

-- | A parameter consuming all the remaining positional parameters
posArgs
  :: RawRead a
  => Key                -- ^ Param name
  -> b                  -- ^ Initial value
  -> (b -> a -> b)      -- ^ Accumulation function
  -> StdArgParam b
posArgs key initval accum = StdArgParam
  Mandatory Pos key (MulipleArgParser $ readArgs key initval accum)

-- | A mandatory flag argument parameter taking multiple arguments
reqFlagArgs
  :: RawRead a
  => Key                -- ^ Flag name
  -> b                  -- ^ Initial value
  -> (b -> a -> b)      -- ^ Accumulation function
  -> StdArgParam b
reqFlagArgs key initval accum = StdArgParam
  Mandatory Flag key (MulipleArgParser $ readArgs key initval accum)

-- | An optional flag argument parameter taking multiple arguments
optFlagArgs
  :: RawRead a
  => b                  -- ^ Default value
  -> Key                -- ^ Flag name
  -> b                  -- ^ Initial value
  -> (b -> a -> b)      -- ^ Accumulation function
  -> StdArgParam b
optFlagArgs val key initval accum = StdArgParam
  (Optional val) Flag key (MulipleArgParser $ readArgs key initval accum)
