module Language.Haskell.Tools.Daemon.Options
         (DaemonOptions(..), parseDaemonCLI, SharedDaemonOptions(..), sharedOptionsParser) where

import Control.Monad.Reader (MonadReader(..))
import Data.List.Split (splitOn)
import Data.Semigroup ((<>))
import Language.Haskell.Tools.Daemon.PackageDB (PackageDB(..))
import Options.Applicative
import Options.Applicative.Types (ReadM(..), Parser, readerAsk)

-- | Command line options for the daemon process.
data DaemonOptions = DaemonOptions { daemonVersion :: Bool
                                   , portNumber :: Int
                                   , silentMode :: Bool
                                   , sharedOptions :: SharedDaemonOptions
                                   } deriving Show

-- | Command line options shared by CLI and daemon.
data SharedDaemonOptions = SharedDaemonOptions { noWatch :: Bool
                                               , watchExe :: Maybe FilePath
                                               , generateCode :: Bool
                                               , disableHistory :: Bool
                                               , ghcFlags :: Maybe [String]
                                               , projectType :: Maybe PackageDB
                                               } deriving Show

parseDaemonCLI = execParser daemonCLI

daemonCLI = info (daemonOptionsParser <**> helper)
                 (fullDesc
                   <> progDesc "Start the Haskell-tools daemon process"
                   <> header "ht-daemon: a background process for Haskell development tools.")

daemonOptionsParser :: Parser DaemonOptions
daemonOptionsParser = DaemonOptions <$> version <*> port <*> silent <*> sharedOptionsParser
  where version = switch (long "version"
                            <> short 'v'
                            <> help "Show the version of this software")
        port = option auto
                 (long "port"
                   <> short 'p'
                   <> value 4123
                   <> showDefault
                   <> help "Set the number of port where the daemon will wait for clients."
                   <> metavar "PORT_NUMBER")
        silent = switch (long "silent"
                          <> short 's'
                          <> help "Set to disable messages from daemon.")

sharedOptionsParser :: Parser SharedDaemonOptions
sharedOptionsParser = SharedDaemonOptions <$> noWatch <*> watch <*> generateCode <*> noHistory <*> ghcFlags <*> pkgDBFlags
  where noWatch = switch (long "no-watch"
                            <> help "Disables file system watching.")
        watch = optional . strOption
                  $ long "watch-exe"
                      <> short 'w'
                      <> help "The file path of the watch executable that is used to monitor file system changes."
                      <> metavar "WATH_PATH"
        generateCode = switch (long "generate-code"
                                 <> help "Always generate code for the modules of the loaded project.")
        noHistory = switch (long "no-history"
                              <> help "Disables saving the performed refactorings.")
        ghcFlags
          = optional $ option ghcFlagsParser
                         (long "ghc-options"
                           <> short 'g'
                           <> metavar "GHC_OPTIONS"
                           <> help "Flags passed to GHC when loading the packages, separated by spaces.")
          where ghcFlagsParser :: ReadM [String]
                ghcFlagsParser
                  = ReadM $ do str <- ask
                               let str' = case str of '=':'"':rest -> init rest
                                                      '=':rest     -> rest
                                                      '"':rest     -> init rest
                                                      other        -> other
                               return ( splitOn " " str')
        pkgDBFlags = optional $ option typeParser
                       (long "project-type"
                         <> metavar "PROJECT_TYPE"
                         <> help "Set the project type (cabal, cabal-sandbox, stack, or explicit package database pathes separated by commas).")
          where typeParser = do str <- readerAsk
                                case str of "cabal" -> return DefaultDB
                                            "cabal-sandbox" ->  return CabalSandboxDB
                                            "stack" -> return StackDB
                                            pathes -> return $ ExplicitDB (splitOn "," pathes)