content: {-# Language CPP, TemplateHaskell, MultiWayIf #-}
  
  #ifndef TOOL_VERSION_alex
  #define TOOL_VERSION_alex "none"
  #endif
  
  #ifndef TOOL_VERSION_happy
  #define TOOL_VERSION_happy "none"
  #endif
  
  {-|
  Module      : Client.Options
  Description : Processing of command-line options
  Copyright   : (c) Eric Mertens, 2016
  License     : ISC
  Maintainer  : emertens@gmail.com
  
  This module process command-line options provided when launching the client.
  
  -}
  module Client.Options
    (
    -- * Command-line options
      Options(..)
  
    -- * Lenses
    , optConfigFile
    , optInitialNetworks
    , optNoConnect
  
    -- * Options loader
    , getOptions
    ) where
  
  import Build_glirc (deps)
  import Client.Configuration (configurationSpec, getConfigPath)
  import Config.Schema.Docs (generateDocs)
  import Control.Lens (view, (<>~), set, makeLenses)
  import Data.Foldable (Foldable(foldl'), traverse_)
  import Data.List (intercalate, sort)
  import Data.Text (Text)
  import Data.Text qualified as Text
  import Data.Version (showVersion)
  import GitHash (giHash, giDirty, tGitInfoCwdTry)
  import Paths_glirc (version)
  import System.Console.GetOpt
  import System.Environment (getArgs)
  import System.Exit (exitFailure, exitSuccess)
  import System.Info (arch, compilerName, compilerVersion, os)
  import System.IO (hPutStr, hPutStrLn, stderr)
  
  -- | Command-line options
  data Options = Options
    { _optConfigFile      :: Maybe FilePath -- ^ configuration file path
    , _optInitialNetworks :: [Text]         -- ^ initial networks
    , _optNoConnect       :: Bool           -- ^ disable autoconnect
    , _optShowHelp        :: Bool           -- ^ show help message
    , _optShowVersion     :: Bool           -- ^ show version message
    , _optShowFullVersion :: Bool           -- ^ show version of ALL transitive dependencies
    , _optShowConfigFormat:: Bool           -- ^ show configuration file format
    }
  
  makeLenses ''Options
  
  -- | Default values for 'Options'
  defaultOptions :: Options
  defaultOptions = Options
    { _optConfigFile      = Nothing
    , _optInitialNetworks = []
    , _optShowHelp        = False
    , _optShowVersion     = False
    , _optShowFullVersion = False
    , _optNoConnect       = False
    , _optShowConfigFormat= False
    }
  
  -- | Option descriptions
  options :: [OptDescr (Options -> Options)]
  options =
    [ Option "c" ["config"]  (ReqArg (set optConfigFile . Just) "PATH")
      "Configuration file path"
    , Option "!" ["noconnect"] (NoArg (set optNoConnect True))
      "Disable autoconnecting"
    , Option "h" ["help"]    (NoArg (set optShowHelp True))
      "Show help"
    , Option "" ["config-format"] (NoArg (set optShowConfigFormat True))
      "Show configuration file format"
    , Option "v" ["version"] (NoArg (set optShowVersion True))
      "Show version"
    , Option "" ["full-version"] (NoArg (set optShowFullVersion True))
      "Show version and versions of all linked Haskell libraries"
    ]
  
  optOrder :: ArgOrder (Options -> Options)
  optOrder = ReturnInOrder (\x -> optInitialNetworks <>~ [Text.pack x])
  
  -- | Load command line options. This action will terminate early
  -- in the case of the version flag, help flag, or an error.
  getOptions :: IO Options
  getOptions =
    do (flags, _, errors) <- getOpt optOrder options <$> getArgs
  
       let opts = foldl' (\acc f -> f acc) defaultOptions flags
  
           bullet x = "• " ++ x
  
           reportErrors =
             do hPutStrLn stderr "Errors processing command-line options:"
                traverse_ (hPutStr stderr) (map bullet errors)
                hPutStrLn stderr tryHelpTxt
  
       if | view optShowHelp         opts -> putStr helpTxt        >> exitSuccess
          | view optShowFullVersion  opts -> putStr fullVersionTxt >> exitSuccess
          | view optShowVersion      opts -> putStr versionTxt     >> exitSuccess
          | view optShowConfigFormat opts -> printConfigFormat     >> exitSuccess
          | null errors                   -> return opts
          | otherwise                     -> reportErrors          >> exitFailure
  
  printConfigFormat :: IO ()
  printConfigFormat =
    do path <- getConfigPath
       putStrLn ""
       putStrLn ("Default configuration file path: " ++ path)
       putStrLn ""
       print (generateDocs configurationSpec)
  
  helpTxt :: String
  helpTxt = usageInfo "glirc [FLAGS] INITIAL_NETWORKS..." options
  
  tryHelpTxt :: String
  tryHelpTxt =
    "Run 'glirc --help' to see a list of available command line options."
  
  -- version information ---------------------------------------------
  
  versionTxt :: String
  versionTxt = unlines
    [ "glirc-" ++ showVersion version ++ gitHashTxt
    , "Copyright 2016-2020 Eric Mertens"
    ]
    where
      gitHashTxt =
        case $$tGitInfoCwdTry of
          Left{}   -> ""
          Right gi -> giHash gi ++ if giDirty gi then "-dirty" else ""
  
  fullVersionTxt :: String
  fullVersionTxt =
    versionTxt ++
    unlines
    (""
    :("OS          : " ++ os)
    :("Architecture: " ++ arch)
    :("Compiler    : " ++ compilerName ++ "-" ++ showVersion compilerVersion)
    :""
    :("ghc         : " ++ TOOL_VERSION_ghc)
    :("ghc-pkg     : " ++ TOOL_VERSION_ghc_pkg)
    :("alex        : " ++ TOOL_VERSION_alex)
    :("happy       : " ++ TOOL_VERSION_happy)
    :("hsc2hs      : " ++ TOOL_VERSION_hsc2hs)
    :""
    :"Transitive dependencies:"
    : [ name ++ "-" ++ intercalate "." (map show ver) | (name,ver) <- sort deps ]
    )
  