-- | Parser for build arguments.
buildOptsParser :: BuildCommand
                -> Parser BuildOpts
buildOptsParser cmd =
            transform <$> trace <*> profile <*> options
  where transform tracing profiling = enable
          where enable opts
                  | tracing || profiling =
                      opts {boptsLibProfile = True
                           ,boptsExeProfile = True
                           ,boptsGhcOptions = ["-auto-all","-caf-all"]
                           ,boptsBenchmarkOpts =
                                bopts {beoAdditionalArgs =
                                           beoAdditionalArgs bopts <>
                                           Just (" " <> unwords additionalArgs)}
                           ,boptsTestOpts =
                                topts {toAdditionalArgs =
                                           (toAdditionalArgs topts) <>
                                           additionalArgs}}
                  | otherwise = opts
                  where bopts = boptsBenchmarkOpts opts
                        topts = boptsTestOpts opts
                        additionalArgs = "+RTS" : catMaybes [trac, prof, Just "-RTS"]
                        trac = if tracing
                                  then Just "-xc"
                                  else Nothing
                        prof = if profiling
                                  then Just "-p"
                                  else Nothing
        profile =
            flag False True
             ( long "profile"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a profiling report\
                    \ in exec or benchmarks")
        trace =
            flag False True
             ( long "trace"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a backtrace on \
                    \exception")
        options =
            BuildOpts <$> target <*> libProfiling <*> exeProfiling <*>
            haddock <*> haddockDeps <*> dryRun <*> ghcOpts <*>
            flags <*> copyBins <*> preFetch <*> buildSubset <*>
            fileWatch' <*> keepGoing <*> forceDirty <*> tests <*>
            testOptsParser <*> benches <*> benchOptsParser <*>
            many exec <*> onlyConfigure <*> reconfigure <*> cabalVerbose
        target =
           many (textArgument
                   (metavar "TARGET" <>
                    help "If none specified, use all packages"))
        libProfiling =
          boolFlags False
                    "library-profiling"
                    "library profiling for TARGETs and all its dependencies"
                    idm
        exeProfiling =
          boolFlags False
                    "executable-profiling"
                    "executable profiling for TARGETs and all its dependencies"
                    idm
        haddock =
          boolFlags (cmd == Haddock)
                    "haddock"
                    "generating Haddocks the package(s) in this directory/configuration"
                    idm
        haddockDeps =
             maybeBoolFlags
                       "haddock-deps"
                       "building Haddocks for dependencies"
                       idm
        copyBins = boolFlags (cmd == Install)
            "copy-bins"
            "copying binaries to the local-bin-path (see 'stack path')"
            idm

        dryRun = switch (long "dry-run" <>
                         help "Don't build anything, just prepare to")
        ghcOpts = (\x y z -> concat [x, y, z])
          <$> flag [] ["-Wall", "-Werror"]
              ( long "pedantic"
             <> help "Turn on -Wall and -Werror"
              )
          <*> flag [] ["-O0"]
              ( long "fast"
             <> help "Turn off optimizations (-O0)"
              )
          <*> many (textOption (long "ghc-options" <>
                                metavar "OPTION" <>
                                help "Additional options passed to GHC"))

        flags = Map.unionsWith Map.union <$> many
                  (option readFlag
                      (long "flag" <>
                       metavar "PACKAGE:[-]FLAG" <>
                       help ("Override flags set in stack.yaml " <>
                             "(applies to local packages and extra-deps)")))

        preFetch = switch
            (long "prefetch" <>
             help "Fetch packages necessary for the build immediately, useful with --dry-run")

        buildSubset =
            flag' BSOnlyDependencies
                (long "dependencies-only" <>
                 help "A synonym for --only-dependencies")
            <|> flag' BSOnlySnapshot
                (long "only-snapshot" <>
                 help "Only build packages for the snapshot database, not the local database")
            <|> flag' BSOnlyDependencies
                (long "only-dependencies" <>
                 help "Only build packages that are dependencies of targets on the command line")
            <|> pure BSAll

        fileWatch' =
            flag' FileWatch
                (long "file-watch" <>
                 help "Watch for changes in local files and automatically rebuild. Ignores files in VCS boring/ignore file")
            <|> flag' FileWatchPoll
                (long "file-watch-poll" <>
                 help "Like --file-watch, but polling the filesystem instead of using events")
            <|> pure NoFileWatch

        keepGoing = maybeBoolFlags
            "keep-going"
            "continue running after a step fails (default: false for build, true for test/bench)"
            idm

        forceDirty = switch
            (long "force-dirty" <>
             help "Force treating all local packages as having dirty files (useful for cases where stack can't detect a file change)")

        tests = boolFlags (cmd == Test)
            "test"
            "testing the package(s) in this directory/configuration"
            idm

        benches = boolFlags (cmd == Bench)
            "bench"
            "benchmarking the package(s) in this directory/configuration"
            idm

        exec = cmdOption
            ( long "exec" <>
              metavar "CMD [ARGS]" <>
              help "Command and arguments to run after a successful build" )

        onlyConfigure = switch
            (long "only-configure" <>
             help "Only perform the configure step, not any builds. Intended for tool usage, may break when used on multiple packages at once!")

        reconfigure = switch
            (long "reconfigure" <>
             help "Perform the configure step even if unnecessary. Useful in some corner cases with custom Setup.hs files")

        cabalVerbose = switch
            (long "cabal-verbose" <>
             help "Ask Cabal to be verbose in its output")

-- | Parser for package:[-]flag
buildOptsParser :: BuildCommand
                -> Parser BuildOpts
buildOptsParser cmd =
            transform <$> trace <*> profile <*> options
  where transform tracing profiling = enable
          where enable opts
                  | tracing || profiling =
                      opts {boptsLibProfile = True
                           ,boptsExeProfile = True
                           ,boptsGhcOptions = ["-auto-all","-caf-all"]
                           ,boptsBenchmarkOpts =
                                bopts {beoAdditionalArgs =
                                           beoAdditionalArgs bopts <>
                                           Just (" " <> unwords additionalArgs)}
                           ,boptsTestOpts =
                                topts {toAdditionalArgs =
                                           (toAdditionalArgs topts) <>
                                           additionalArgs}}
                  | otherwise = opts
                  where bopts = boptsBenchmarkOpts opts
                        topts = boptsTestOpts opts
                        additionalArgs = "+RTS" : catMaybes [trac, prof, Just "-RTS"]
                        trac = if tracing
                                  then Just "-xc"
                                  else Nothing
                        prof = if profiling
                                  then Just "-p"
                                  else Nothing
        profile =
            flag False True
             ( long "profile"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a profiling report\
                    \ in exec or benchmarks")
        trace =
            flag False True
             ( long "trace"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a backtrace on \
                    \exception")
        options =
            BuildOpts <$> target <*> libProfiling <*> exeProfiling <*>
            haddock <*> haddockDeps <*> dryRun <*> ghcOpts <*>
            flags <*> copyBins <*> preFetch <*> buildSubset <*>
            fileWatch' <*> keepGoing <*> forceDirty <*> tests <*>
            testOptsParser <*> benches <*> benchOptsParser <*>
            many exec <*> onlyConfigure <*> reconfigure <*> cabalVerbose
        target =
           many (textArgument
                   (metavar "TARGET" <>
                    help "If none specified, use all packages"))
        libProfiling =
          boolFlags False
                    "library-profiling"
                    "library profiling for TARGETs and all its dependencies"
                    idm
        exeProfiling =
          boolFlags False
                    "executable-profiling"
                    "executable profiling for TARGETs and all its dependencies"
                    idm
        haddock =
          boolFlags (cmd == Haddock)
                    "haddock"
                    "generating Haddocks the package(s) in this directory/configuration"
                    idm
        haddockDeps =
             maybeBoolFlags
                       "haddock-deps"
                       "building Haddocks for dependencies"
                       idm
        copyBins = boolFlags (cmd == Install)
            "copy-bins"
            "copying binaries to the local-bin-path (see 'stack path')"
            idm

        dryRun = switch (long "dry-run" <>
                         help "Don't build anything, just prepare to")
        ghcOpts = (\x y z -> concat [x, y, z])
          <$> flag [] ["-Wall", "-Werror"]
              ( long "pedantic"
             <> help "Turn on -Wall and -Werror"
              )
          <*> flag [] ["-O0"]
              ( long "fast"
             <> help "Turn off optimizations (-O0)"
              )
          <*> many (textOption (long "ghc-options" <>
                                metavar "OPTION" <>
                                help "Additional options passed to GHC"))

        flags = Map.unionsWith Map.union <$> many
                  (option readFlag
                      (long "flag" <>
                       metavar "PACKAGE:[-]FLAG" <>
                       help ("Override flags set in stack.yaml " <>
                             "(applies to local packages and extra-deps)")))

        preFetch = switch
            (long "prefetch" <>
             help "Fetch packages necessary for the build immediately, useful with --dry-run")

        buildSubset =
            flag' BSOnlyDependencies
                (long "dependencies-only" <>
                 help "A synonym for --only-dependencies")
            <|> flag' BSOnlySnapshot
                (long "only-snapshot" <>
                 help "Only build packages for the snapshot database, not the local database")
            <|> flag' BSOnlyDependencies
                (long "only-dependencies" <>
                 help "Only build packages that are dependencies of targets on the command line")
            <|> pure BSAll

        fileWatch' =
            flag' FileWatch
                (long "file-watch" <>
                 help "Watch for changes in local files and automatically rebuild. Ignores files in VCS boring/ignore file")
            <|> flag' FileWatchPoll
                (long "file-watch-poll" <>
                 help "Like --file-watch, but polling the filesystem instead of using events")
            <|> pure NoFileWatch

        keepGoing = maybeBoolFlags
            "keep-going"
            "continue running after a step fails (default: false for build, true for test/bench)"
            idm

        forceDirty = switch
            (long "force-dirty" <>
             help "Force treating all local packages as having dirty files (useful for cases where stack can't detect a file change)")

        tests = boolFlags (cmd == Test)
            "test"
            "testing the package(s) in this directory/configuration"
            idm

        benches = boolFlags (cmd == Bench)
            "bench"
            "benchmarking the package(s) in this directory/configuration"
            idm

        exec = cmdOption
            ( long "exec" <>
              metavar "CMD [ARGS]" <>
              help "Command and arguments to run after a successful build" )

        onlyConfigure = switch
            (long "only-configure" <>
             help "Only perform the configure step, not any builds. Intended for tool usage, may break when used on multiple packages at once!")

        reconfigure = switch
            (long "reconfigure" <>
             help "Perform the configure step even if unnecessary. Useful in some corner cases with custom Setup.hs files")

        cabalVerbose = switch
            (long "cabal-verbose" <>
             help "Ask Cabal to be verbose in its output")

-- | Parser for package:[-]flag
buildOptsParser cmd =
            transform <$> trace <*> profile <*> options
  where transform tracing profiling = enable
          where enable opts
                  | tracing || profiling =
                      opts {boptsLibProfile = True
                           ,boptsExeProfile = True
                           ,boptsGhcOptions = ["-auto-all","-caf-all"]
                           ,boptsBenchmarkOpts =
                                bopts {beoAdditionalArgs =
                                           beoAdditionalArgs bopts <>
                                           Just (" " <> unwords additionalArgs)}
                           ,boptsTestOpts =
                                topts {toAdditionalArgs =
                                           (toAdditionalArgs topts) <>
                                           additionalArgs}}
                  | otherwise = opts
                  where bopts = boptsBenchmarkOpts opts
                        topts = boptsTestOpts opts
                        additionalArgs = "+RTS" : catMaybes [trac, prof, Just "-RTS"]
                        trac = if tracing
                                  then Just "-xc"
                                  else Nothing
                        prof = if profiling
                                  then Just "-p"
                                  else Nothing
        profile =
            flag False True
             ( long "profile"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a profiling report\
                    \ in exec or benchmarks")
        trace =
            flag False True
             ( long "trace"
            <> help "Enable profiling in libraries, executables, etc. \
                    \for all expressions and generate a backtrace on \
                    \exception")
        options =
            BuildOpts <$> target <*> libProfiling <*> exeProfiling <*>
            haddock <*> haddockDeps <*> dryRun <*> ghcOpts <*>
            flags <*> copyBins <*> preFetch <*> buildSubset <*>
            fileWatch' <*> keepGoing <*> forceDirty <*> tests <*>
            testOptsParser <*> benches <*> benchOptsParser <*>
            many exec <*> onlyConfigure <*> reconfigure <*> cabalVerbose
        target =
           many (textArgument
                   (metavar "TARGET" <>
                    help "If none specified, use all packages"))
        libProfiling =
          boolFlags False
                    "library-profiling"
                    "library profiling for TARGETs and all its dependencies"
                    idm
        exeProfiling =
          boolFlags False
                    "executable-profiling"
                    "executable profiling for TARGETs and all its dependencies"
                    idm
        haddock =
          boolFlags (cmd == Haddock)
                    "haddock"
                    "generating Haddocks the package(s) in this directory/configuration"
                    idm
        haddockDeps =
             maybeBoolFlags
                       "haddock-deps"
                       "building Haddocks for dependencies"
                       idm
        copyBins = boolFlags (cmd == Install)
            "copy-bins"
            "copying binaries to the local-bin-path (see 'stack path')"
            idm

        dryRun = switch (long "dry-run" <>
                         help "Don't build anything, just prepare to")
        ghcOpts = (\x y z -> concat [x, y, z])
          <$> flag [] ["-Wall", "-Werror"]
              ( long "pedantic"
             <> help "Turn on -Wall and -Werror"
              )
          <*> flag [] ["-O0"]
              ( long "fast"
             <> help "Turn off optimizations (-O0)"
              )
          <*> many (textOption (long "ghc-options" <>
                                metavar "OPTION" <>
                                help "Additional options passed to GHC"))

        flags = Map.unionsWith Map.union <$> many
                  (option readFlag
                      (long "flag" <>
                       metavar "PACKAGE:[-]FLAG" <>
                       help ("Override flags set in stack.yaml " <>
                             "(applies to local packages and extra-deps)")))

        preFetch = switch
            (long "prefetch" <>
             help "Fetch packages necessary for the build immediately, useful with --dry-run")

        buildSubset =
            flag' BSOnlyDependencies
                (long "dependencies-only" <>
                 help "A synonym for --only-dependencies")
            <|> flag' BSOnlySnapshot
                (long "only-snapshot" <>
                 help "Only build packages for the snapshot database, not the local database")
            <|> flag' BSOnlyDependencies
                (long "only-dependencies" <>
                 help "Only build packages that are dependencies of targets on the command line")
            <|> pure BSAll

        fileWatch' =
            flag' FileWatch
                (long "file-watch" <>
                 help "Watch for changes in local files and automatically rebuild. Ignores files in VCS boring/ignore file")
            <|> flag' FileWatchPoll
                (long "file-watch-poll" <>
                 help "Like --file-watch, but polling the filesystem instead of using events")
            <|> pure NoFileWatch

        keepGoing = maybeBoolFlags
            "keep-going"
            "continue running after a step fails (default: false for build, true for test/bench)"
            idm

        forceDirty = switch
            (long "force-dirty" <>
             help "Force treating all local packages as having dirty files (useful for cases where stack can't detect a file change)")

        tests = boolFlags (cmd == Test)
            "test"
            "testing the package(s) in this directory/configuration"
            idm

        benches = boolFlags (cmd == Bench)
            "bench"
            "benchmarking the package(s) in this directory/configuration"
            idm

        exec = cmdOption
            ( long "exec" <>
              metavar "CMD [ARGS]" <>
              help "Command and arguments to run after a successful build" )

        onlyConfigure = switch
            (long "only-configure" <>
             help "Only perform the configure step, not any builds. Intended for tool usage, may break when used on multiple packages at once!")

        reconfigure = switch
            (long "reconfigure" <>
             help "Perform the configure step even if unnecessary. Useful in some corner cases with custom Setup.hs files")

        cabalVerbose = switch
            (long "cabal-verbose" <>
             help "Ask Cabal to be verbose in its output")

-- | Parser for package:[-]flag