-- |This module defines the MOO command interface, the commnad line options
-- parser, and helpers to manipulate the Command data structure.
module Moo.CommandInterface
    ( commands
    , commandOptionUsage
    , findCommand
    , getCommandArgs
    , usageString
    ) where

import Data.Maybe
import Moo.CommandHandlers
import Moo.Core
import System.Console.GetOpt

-- |The available commands; used to dispatch from the command line and
-- used to generate usage output.
-- |The available commands; used to dispatch from the command line and
-- used to generate usage output.
commands :: [Command]
commands = [ Command "new" [migrationName]
                           []
                           ["no-ask", configFile]
                           "Create a new empty migration"
                           newCommand

           , Command "apply" [migrationName]
                             []
                             [testOption, configFile]
                             "Apply the specified migration and its \
                             \dependencies"
                             applyCommand

           , Command "revert" [migrationName]
                              []
                              [testOption, configFile]
                              "Revert the specified migration and those \
                              \that depend on it"
                              revertCommand

           , Command "test" [migrationName]
                            []
                            [configFile]
                            "Test the specified migration by applying \
                            \and reverting it in a transaction, then \
                            \roll back"
                            testCommand

           , Command "upgrade" []
                               []
                               [testOption, configFile]
                               "Install all migrations that have not yet \
                               \been installed"

                               upgradeCommand

           , Command "upgrade-list" []
                                    []
                                    []
                                    "Show the list of migrations not yet \
                                    \installed"
                                    upgradeListCommand

           , Command "reinstall" [migrationName]
                                 []
                                 [testOption, configFile]
                                 "Reinstall a migration by reverting, then \
                                 \reapplying it"
                                 reinstallCommand

           , Command "list" []
                            []
                            [configFile]
                            "List migrations already installed in the backend"
                            listCommand
           ]
    where migrationName = "migrationName"
          testOption    = "test"
          configFile    = "config-file"


findCommand :: String -> Maybe Command
findCommand name = listToMaybe [ c | c <- commands, _cName c == name ]

commandOptions :: [ OptDescr (CommandOptions -> IO CommandOptions) ]
commandOptions =  [ optionConfigFile
                  , optionTest
                  , optionNoAsk
                  ]

optionConfigFile :: OptDescr (CommandOptions -> IO CommandOptions)
optionConfigFile = Option "c" ["config-file"]
                   (ReqArg (\arg opt ->
                             return opt { _configFilePath = Just arg }) "FILE")
                   "Specify location of configuration file"

optionTest :: OptDescr (CommandOptions -> IO CommandOptions)
optionTest = Option "t" ["test"]
             (NoArg (\opt -> return opt { _test = True }))
             "Perform the action then rollback when finished"

optionNoAsk :: OptDescr (CommandOptions -> IO CommandOptions)
optionNoAsk = Option "n" ["no-ask"]
              (NoArg (\opt -> return opt { _noAsk = True }))
              "Do not interactively ask any questions, just do it"

getCommandArgs :: [String] -> IO ( CommandOptions, [String] )
getCommandArgs args = do
  let (actions, required, _) =  getOpt RequireOrder commandOptions args
  opts <- foldl (>>=) defaultOptions actions
  return ( opts, required )

defaultOptions :: IO CommandOptions
defaultOptions = return $ CommandOptions Nothing False False

commandOptionUsage :: String
commandOptionUsage = usageInfo "Options:" commandOptions

usageString :: Command -> String
usageString command =
    unwords (_cName command:optionalArgs ++ options ++ requiredArgs)
    where
      requiredArgs = map (\s -> "<" ++ s ++ ">") $ _cRequired command
      optionalArgs = map (\s -> "[" ++ s ++ "]") $ _cOptional command
      options = map (\s -> "["++ "--" ++ s ++ "]")  optionStrings
      optionStrings = _cAllowedOptions command