content: {-# LANGUAGE DeriveLift#-}
  module System.Console.Docopt.Types
      where
  
  import           Data.Char (isUpper)
  import           Data.List (nub)
  import           Data.Map (Map)
  import qualified Data.Map as M
  import Language.Haskell.TH.Syntax (Lift)
  
  -- * Usage expression Types
  
  type Name = String
  
  data Pattern a = Sequence [Pattern a]
                 | OneOf [Pattern a]
                 | Unordered [Pattern a]
                 | Optional (Pattern a)
                 | Repeated (Pattern a)
                 | Atom a
                 deriving (Show, Eq, Lift)
  
  atoms :: Eq a => Pattern a -> [a]
  atoms (Sequence ps)  = concatMap atoms ps
  atoms (OneOf ps)     = concatMap atoms $ nub ps
  atoms (Unordered ps) = concatMap atoms $ nub ps
  atoms (Optional p)   = atoms p
  atoms (Repeated p)   = atoms p
  atoms (Atom a)       = [a]
  
  -- | A named leaf node of the usage pattern tree
  data Option = LongOption Name
              | ShortOption Char
              | Command Name
              | Argument Name
              | AnyOption
              deriving (Show, Eq, Ord, Lift)
  
  type OptPattern = Pattern Option
  
  humanize :: Option -> String
  humanize opt = case opt of
    Command name    -> name
    Argument name   -> if all isUpper name
                           then name
                           else "<" ++ name ++ ">"
    LongOption name -> "--"++name
    ShortOption c   -> ['-',c]
    AnyOption       -> "[options]"
  
  -- | Used when parsing through the available option descriptions.
  --   Holds a list of synonymous options, Maybe a default value (if specified),
  --   an expectsVal :: Bool that indicates whether this option is a flag (--flag)
  --   or an option that needs an argument (--opt=arg), and isRepeated :: Bool
  --   that indicates whether this option is always single or needs to be accumulated
  data OptionInfo = OptionInfo
                    { synonyms :: [Option]
                    , defaultVal :: Maybe String
                    , expectsVal :: Bool
                    , isRepeated :: Bool
                    } deriving (Show, Eq, Lift)
  
  fromSynList :: [Option] -> OptionInfo
  fromSynList opts = OptionInfo { synonyms = opts
                                , defaultVal = Nothing
                                , expectsVal = False
                                , isRepeated = False }
  
  -- | Maps each available option to a OptionInfo entry
  --   (each synonymous option gets its own separate entry, for easy lookup)
  type OptInfoMap = Map Option OptionInfo
  
  -- | Contains all the relevant information parsed out of a usage string.
  --   Used to build the actual command-line arg parser.
  type OptFormat = (OptPattern, OptInfoMap)
  
  -- |
  data OptParserState = OptParserState
                        { optInfoMap :: OptInfoMap
                        , parsedArgs :: Arguments
                        , inShortOptStack :: Bool
                        , inTopLevelSequence :: Bool
                        } deriving (Show)
  
  fromOptInfoMap :: OptInfoMap -> OptParserState
  fromOptInfoMap m = OptParserState { optInfoMap = m
                                    , parsedArgs = M.empty
                                    , inShortOptStack = False
                                    , inTopLevelSequence = True }
  
  
  data ArgValue = MultiValue [String]
                | Value String
                | NoValue
                | Counted Int
                | Present
                | NotPresent
                deriving (Show, Eq, Ord)
  
  -- | Maps each Option to all of the valued parsed from the command line
  --   (in order of last to first, if multiple values encountered)
  type Arguments = Map Option ArgValue
  
  -- | An abstract data type which represents Docopt usage patterns.
  data Docopt = Docopt { optFormat :: OptFormat
                       -- | Retrieve the original usage string.
                       , usage :: String
                       }
  