module Options.Applicative.Types (
  ParseError(..),
  ParserInfo(..),
  ParserPrefs(..),
  Option(..),
  OptName(..),
  OptReader(..),
  OptProperties(..),
  OptVisibility(..),
  ReadM(..),
  readerAbort,
  readerError,
  CReader(..),
  Parser(..),
  ParserM(..),
  Completer(..),
  mkCompleter,
  CompletionResult(..),
  ParserFailure(..),
  ParserResult(..),
  Args,
  ArgPolicy(..),
  OptHelpInfo(..),
  OptTree(..),
  fromM,
  oneM,
  manyM,
  someM,
  optVisibility,
  optMetaVar,
  optHelp,
  optShowDefault
  ) where
import Control.Applicative
  (Applicative(..), Alternative(..), (<$>), optional)
import Control.Monad (ap, liftM, MonadPlus, mzero, mplus)
import Control.Monad.Trans.Error (Error(..))
import Data.Monoid (Monoid(..))
import System.Exit (ExitCode(..))
import Options.Applicative.Help.Pretty
import Options.Applicative.Help.Chunk
data ParseError
  = ErrorMsg String
  | InfoMsg String
  | ShowHelpText
  deriving Show
instance Error ParseError where
  strMsg = ErrorMsg
data ParserInfo a = ParserInfo
  { infoParser :: Parser a    
  , infoFullDesc :: Bool      
                              
  , infoProgDesc :: Chunk Doc 
  , infoHeader :: Chunk Doc   
  , infoFooter :: Chunk Doc   
  , infoFailureCode :: Int    
  , infoIntersperse :: Bool   
                              
  }
instance Functor ParserInfo where
  fmap f i = i { infoParser = fmap f (infoParser i) }
data ParserPrefs = ParserPrefs
  { prefMultiSuffix :: String    
  , prefDisambiguate :: Bool     
                                 
  , prefShowHelpOnError :: Bool  
                                 
  , prefBacktrack :: Bool        
                                 
  , prefColumns :: Int           
                                 
  }
data OptName = OptShort !Char
             | OptLong !String
  deriving (Eq, Ord)
data OptVisibility
  = Internal          
  | Hidden            
  | Visible           
  deriving (Eq, Ord)
data OptProperties = OptProperties
  { propVisibility :: OptVisibility       
  , propHelp :: Chunk Doc                 
  , propMetaVar :: String                 
  , propShowDefault :: Maybe String       
  }
data Option a = Option
  { optMain :: OptReader a               
  , optProps :: OptProperties            
  }
instance Functor Option where
  fmap f (Option m p) = Option (fmap f m) p
data CReader m a = CReader
  { crCompleter :: Completer
  , crReader :: String -> m a }
instance Functor m => Functor (CReader m) where
  fmap f (CReader c r) = CReader c (fmap f . r)
newtype ReadM a = ReadM
  { runReadM :: Either ParseError a }
instance Functor ReadM where
  fmap f (ReadM m) = ReadM (fmap f m)
instance Applicative ReadM where
  pure = ReadM . Right
  ReadM b <*> ReadM a = ReadM (b <*> a)
instance Monad ReadM where
  return = ReadM . Right
  ReadM m >>= f = ReadM $ m >>= runReadM . f
  fail = ReadM . Left . ErrorMsg
instance MonadPlus ReadM where
  mzero = ReadM $ Left (strMsg "")
  mplus m1 m2 = case runReadM m1 of
    Left _ -> m2
    Right r -> return r
readerAbort :: ParseError -> ReadM a
readerAbort = ReadM . Left
readerError :: String -> ReadM a
readerError = readerAbort . ErrorMsg
type OptCReader = CReader ReadM
type ArgCReader = CReader Maybe
data OptReader a
  = OptReader [OptName] (OptCReader a) ParseError       
  | FlagReader [OptName] !a                             
  | ArgReader (ArgCReader a)                            
  | CmdReader [String] (String -> Maybe (ParserInfo a)) 
instance Functor OptReader where
  fmap f (OptReader ns cr e) = OptReader ns (fmap f cr) e
  fmap f (FlagReader ns x) = FlagReader ns (f x)
  fmap f (ArgReader cr) = ArgReader (fmap f cr)
  fmap f (CmdReader cs g) = CmdReader cs ((fmap . fmap) f . g)
data Parser a where
  NilP :: Maybe a -> Parser a
  OptP :: Option a -> Parser a
  MultP :: Parser (a -> b) -> Parser a -> Parser b
  AltP :: Parser a -> Parser a -> Parser a
  BindP :: Parser a -> (a -> Parser b) -> Parser b
instance Functor Parser where
  fmap f (NilP x) = NilP (fmap f x)
  fmap f (OptP opt) = OptP (fmap f opt)
  fmap f (MultP p1 p2) = MultP (fmap (f.) p1) p2
  fmap f (AltP p1 p2) = AltP (fmap f p1) (fmap f p2)
  fmap f (BindP p k) = BindP p (fmap f . k)
instance Applicative Parser where
  pure = NilP . Just
  (<*>) = MultP
newtype ParserM r = ParserM
  { runParserM :: forall x . (r -> Parser x) -> Parser x }
instance Monad ParserM where
  return x = ParserM $ \k -> k x
  ParserM f >>= g = ParserM $ \k -> f (\x -> runParserM (g x) k)
instance Functor ParserM where
  fmap = liftM
instance Applicative ParserM where
  pure = return
  (<*>) = ap
fromM :: ParserM a -> Parser a
fromM (ParserM f) = f pure
oneM :: Parser a -> ParserM a
oneM p = ParserM (BindP p)
manyM :: Parser a -> ParserM [a]
manyM p = do
  mx <- oneM (optional p)
  case mx of
    Nothing -> return []
    Just x -> (x:) <$> manyM p
someM :: Parser a -> ParserM [a]
someM p = (:) <$> oneM p <*> manyM p
instance Alternative Parser where
  empty = NilP Nothing
  (<|>) = AltP
  many p = fromM $ manyM p
  some p = fromM $ (:) <$> oneM p <*> manyM p
newtype Completer = Completer
  { runCompleter :: String -> IO [String] }
mkCompleter :: (String -> IO [String]) -> Completer
mkCompleter = Completer
instance Monoid Completer where
  mempty = Completer $ \_ -> return []
  mappend (Completer c1) (Completer c2) =
    Completer $ \s -> (++) <$> c1 s <*> c2 s
newtype CompletionResult = CompletionResult
  { execCompletion :: String -> IO String }
newtype ParserFailure = ParserFailure
  { execFailure :: String -> (String, ExitCode) }
data ParserResult a
  = Success a
  | Failure ParserFailure
  | CompletionInvoked CompletionResult
type Args = [String]
data ArgPolicy
  = SkipOpts
  | AllowOpts
  deriving Eq
data OptHelpInfo = OptHelpInfo
  { hinfoMulti :: Bool
  , hinfoDefault :: Bool }
data OptTree a
  = Leaf a
  | MultNode [OptTree a]
  | AltNode [OptTree a]
  deriving Show
optVisibility :: Option a -> OptVisibility
optVisibility = propVisibility . optProps
optHelp :: Option a -> Chunk Doc
optHelp  = propHelp . optProps
optMetaVar :: Option a -> String
optMetaVar = propMetaVar . optProps
optShowDefault :: Option a -> Maybe String
optShowDefault = propShowDefault . optProps