{-# LANGUAGE RecordWildCards #-}

module Zifter.OptParse
    ( module Zifter.OptParse
    , Instructions
    , Dispatch(..)
    , Settings(..)
    , OutputMode(..)
    ) where

import Data.Maybe
import Data.Monoid
import Options.Applicative
import System.Environment (getArgs)

import Zifter.OptParse.Types

getInstructions :: IO Instructions
getInstructions = do
    (cmd, flags) <- getArguments
    config <- getConfiguration cmd flags
    combineToInstructions cmd flags config

combineToInstructions :: Command -> Flags -> Configuration -> IO Instructions
combineToInstructions cmd Flags {..} Configuration = pure (d, sets)
  where
    sets =
        Settings
            { setsOutputColor = flagsOutputColor
            , setsOutputMode = fromMaybe OutputLinear flagsOutputMode
            }
    d =
        case cmd of
            CommandRun -> DispatchRun
            CommandInstall r -> DispatchInstall r
            CommandPreProcess -> DispatchPreProcess
            CommandPreCheck -> DispatchPreCheck
            CommandCheck -> DispatchCheck

getConfiguration :: Command -> Flags -> IO Configuration
getConfiguration _ _ = pure Configuration

getArguments :: IO Arguments
getArguments = do
    args <- getArgs
    let result = runArgumentsParser args
    handleParseResult result

runArgumentsParser :: [String] -> ParserResult Arguments
runArgumentsParser = execParserPure pfs argParser
  where
    pfs =
        ParserPrefs
            { prefMultiSuffix = ""
            , prefDisambiguate = True
            , prefShowHelpOnError = True
            , prefShowHelpOnEmpty = True
            , prefBacktrack = True
            , prefColumns = 80
            }

argParser :: ParserInfo Arguments
argParser = info (helper <*> parseArgs) hlp
  where
    hlp = fullDesc <> progDesc description
    description = "Zifter"

parseArgs :: Parser Arguments
parseArgs = (,) <$> parseCommand <*> parseFlags

parseCommand :: Parser Command
parseCommand =
    hsubparser $
    mconcat
        [ command "run" parseCommandRun
        , command "preprocess" parseCommandPreProcess
        , command "precheck" parseCommandPreCheck
        , command "check" parseCommandCheck
        , command "install" parseCommandInstall
        ]

parseCommandRun :: ParserInfo Command
parseCommandRun = info parser modifier
  where
    parser = pure CommandRun
    modifier = fullDesc <> progDesc "Run the zift script."

parseCommandPreProcess :: ParserInfo Command
parseCommandPreProcess = info parser modifier
  where
    parser = pure CommandPreProcess
    modifier = fullDesc <> progDesc "PreProcess according to the zift script."

parseCommandPreCheck :: ParserInfo Command
parseCommandPreCheck = info parser modifier
  where
    parser = pure CommandPreCheck
    modifier = fullDesc <> progDesc "PreCheck according to the zift script."

parseCommandCheck :: ParserInfo Command
parseCommandCheck = info parser modifier
  where
    parser = pure CommandCheck
    modifier = fullDesc <> progDesc "Check according to the zift script."

parseCommandInstall :: ParserInfo Command
parseCommandInstall = info parser modifier
  where
    parser =
        CommandInstall <$> doubleSwitch "recursive" "Install recursively" mempty
    modifier = fullDesc <> progDesc "Install the zift script."

parseFlags :: Parser Flags
parseFlags =
    Flags <$> doubleSwitch "color" "color in output." mempty <*> outputModeFlag

doubleSwitch :: String -> String -> Mod FlagFields Bool -> Parser Bool
doubleSwitch name helpText mods =
    let enabledValue = True
        disabledValue = False
        defaultValue = True
     in (last <$>
         some
             ((flag'
                   enabledValue
                   (hidden <> internal <> long name <> help helpText <> mods) <|>
               flag'
                   disabledValue
                   (hidden <> internal <> long ("no-" ++ name) <> help helpText <>
                    mods)) <|>
              flag'
                  disabledValue
                  (long ("[no-]" ++ name) <>
                   help
                       ("Enable/disable " ++
                        helpText ++ " (default: " ++ show defaultValue ++ ")") <>
                   mods))) <|>
        pure defaultValue

outputModeFlag :: Parser (Maybe OutputMode)
outputModeFlag =
    (flag'
         (Just OutputLinear)
         (mconcat [long "linear", help "output linearly, reorder as necessary."]) <|>
     flag'
         (Just OutputFast)
         (mconcat
              [ long "fast"
              , help "output as soon as possible, this is likely faster"
              ])) <|>
    pure Nothing