{-# LANGUAGE NamedFieldPuns #-}

module Examples.ParserGroup.Nested (opts, main) where

import Data.Semigroup ((<>))
import Options.Applicative

-- Nested groups. Demonstrates that group can nest.

data LogGroup = LogGroup
  { logPath :: Maybe String,
    systemGroup :: SystemGroup,
    logVerbosity :: Maybe Int
  }
  deriving (Show)

data SystemGroup = SystemGroup
  { poll :: Bool,
    deepNested :: Nested2,
    timeout :: Int
  }
  deriving (Show)

data Nested2 = Nested2
  { nested2Str :: String,
    nested3 :: Nested3
  }
  deriving (Show)

newtype Nested3 = Nested3
  { nested3Str :: String
  }
  deriving (Show)

data Sample = Sample
  { hello :: String,
    logGroup :: LogGroup,
    quiet :: Bool,
    verbosity :: Int,
    group2 :: (Int, Int),
    cmd :: String
  }
  deriving (Show)

sample :: Parser Sample
sample =
  Sample
    <$> parseHello
    <*> parseLogGroup
    <*> parseQuiet
    <*> parseVerbosity
    <*> parseGroup2
    <*> parseCmd

  where
    parseHello =
      strOption
        ( long "hello"
            <> metavar "TARGET"
            <> help "Target for the greeting"
        )

    parseLogGroup =
      parserOptionGroup "First group" $
      parserOptionGroup "Second group" $
      parserOptionGroup "Logging" $
        LogGroup
          <$> parseLogPath
          <*> parseSystemGroup
          <*> parseLogVerbosity

      where
        parseLogPath =
          optional
            ( strOption
                ( long "file-log-path"
                    <> metavar "PATH"
                    <> help "Log file path"
                )
            )
        parseLogVerbosity =
          optional
            ( option
                auto
                ( long "file-log-verbosity"
                    <> metavar "INT"
                    <> help "File log verbosity"
                )
            )

    parseQuiet =
      switch
        ( long "quiet"
            <> short 'q'
            <> help "Whether to be quiet"
        )

    parseSystemGroup =
      parserOptionGroup "System Options" $
        SystemGroup
          <$> switch (long "poll" <> help "Whether to poll")
          <*> parseNested2
          <*> option auto (long "timeout" <> metavar "INT" <> help "Whether to time out")

    parseNested2 =
      parserOptionGroup "Nested2" $
        Nested2
          <$> option auto (long "double-nested" <> metavar "STR" <> help "Some nested option")
          <*> parseNested3

    parseNested3 =
      parserOptionGroup "Nested3" $
        Nested3 <$> option auto (long "triple-nested" <> metavar "STR" <> help "Another option")

    parseGroup2 :: Parser (Int, Int)
    parseGroup2 = parserOptionGroup "Group 2" $
      (,)
        <$> parserOptionGroup "G 2.1" (option auto (long "one" <> help "Option 1"))
        <*> parserOptionGroup "G 2.2" (option auto (long "two" <> help "Option 2"))

    parseVerbosity =
      option auto (long "verbosity" <> short 'v' <> help "Console verbosity")

    parseCmd =
      argument str (metavar "Command")

opts :: ParserInfo Sample
opts =
  info
    (sample <**> helper)
    ( fullDesc
        <> progDesc "Nested parser groups"
        <> header "parser_group.nested - a test for optparse-applicative"
    )

main :: IO ()
main = do
  r <- customExecParser (prefs helpShowGlobals) opts
  print r