{-# LANGUAGE DeriveFunctor #-}

module Options.Harg.Types
  ( Opt (..),
    OptionOpt (..),
    FlagOpt (..),
    ArgumentOpt (..),
    OptAttr (..),
    OptType (..),
    SomeOpt (..),
    OptReader,
    HargCtx (..),
    Environment,
    getCtx,
    ctxFromArgs,
    ctxFromEnv,
    pureCtx,
  )
where

import System.Environment (getArgs, getEnvironment)

type OptReader a = String -> Either String a

-- | The basic option type
data Opt a = Opt
  { -- | Modifier for long options (e.g. @--user@)
    _optLong :: Maybe String,
    -- | Modifier for short options (e.g. @-u@)
    _optShort :: Maybe Char,
    -- | Option help to be shown when invoked
    --   with @--help/-h@ or in case of error
    _optHelp :: Maybe String,
    -- | Metavar to be shown in the help description
    _optMetavar :: Maybe String,
    -- | Environment variable for use with 'EnvSource'
    _optEnvVar :: Maybe String,
    -- | Default value
    _optDefaultVal :: Maybe a,
    -- | Default value as string (unparsed)
    _optDefaultStr :: Maybe String,
    -- | Option parser
    _optReader :: OptReader a,
    -- | Option type
    _optType :: OptType a
  }
  deriving (Functor)

-- | Option types
data OptType a
  = OptionOptType
  | -- | @a@ is the active value for the flag parser
    FlagOptType a
  | ArgumentOptType
  deriving (Functor)

data OptAttr
  = OptDefault
  | OptOptional

-- * Intermediate option types

-- | Option for flags with arguments. Corresponds to 'Options.Applicative.option'.
data OptionOpt (attr :: [OptAttr]) a = OptionOpt
  { _oLong :: Maybe String,
    _oShort :: Maybe Char,
    _oHelp :: Maybe String,
    _oMetavar :: Maybe String,
    _oEnvVar :: Maybe String,
    _oDefaultVal :: Maybe a,
    _oDefaultStr :: Maybe String,
    _oReader :: OptReader a
  }

-- | Option for flags that act like switches between a default and an active
-- value. Corresponds to 'Options.Applicative.flag'.
data FlagOpt (attr :: [OptAttr]) a = FlagOpt
  { _fLong :: Maybe String,
    _fShort :: Maybe Char,
    _fHelp :: Maybe String,
    _fEnvVar :: Maybe String,
    _fDefaultVal :: a,
    _fReader :: OptReader a,
    _fActive :: a
  }

-- | Option for arguments (no long/short specifiers). Corresponds to
-- 'Options.Applicative.argument'.
data ArgumentOpt (attr :: [OptAttr]) a = ArgumentOpt
  { _aHelp :: Maybe String,
    _aMetavar :: Maybe String,
    _aEnvVar :: Maybe String,
    _aDefaultVal :: Maybe a,
    _aDefaultStr :: Maybe String,
    _aReader :: OptReader a
  }

-- | Existential wrapper for 'Opt', so that many options can be carried in
-- a list.
data SomeOpt where
  SomeOpt :: Opt a -> SomeOpt

-- | Environment variable pairs, can be retrieved with 'getEnvironment'.
type Environment =
  [(String, String)]

-- | Command line arguments, can be retrieved with 'getArgs'.
type Args =
  [String]

-- | Context to carry around, that contains environment variables and
-- command line arguments.
data HargCtx = HargCtx
  { _hcEnv :: Environment,
    _hcArgs :: Args
  }

getCtx :: IO HargCtx
getCtx =
  HargCtx <$> getEnvironment <*> getArgs

ctxFromArgs :: Args -> IO HargCtx
ctxFromArgs args =
  HargCtx <$> getEnvironment <*> pure args

ctxFromEnv :: Environment -> IO HargCtx
ctxFromEnv env =
  HargCtx env <$> getArgs

pureCtx :: Environment -> Args -> HargCtx
pureCtx =
  HargCtx