module App
(
  App(..),
  Action(..),
  Flag(..),
  compileApp,
  usageApp,
  failWithUsage
)
where

import System.Console.GetOpt
import System.IO
import System.Exit
import Control.Monad (when)
import System.Environment
import Data.Char (toUpper)
import Text.Read (readMaybe)

data Flag = Flag {
  verbose :: Bool,
  global  :: Bool,
  version :: Bool,
  help    :: Bool,
  message :: String,
  port    :: String
} deriving (Show, Eq)

defaultFlag :: Flag
defaultFlag = Flag {
  verbose = False,
  global  = False,
  version = False,
  help    = False,
  message = "",
  port    = "6091"
}

data App = App {action :: Action,
                flags :: Flag,
                args :: [String]}
                deriving (Show)

data Action =
    Empty
  | Start
  | Status
  | Commit
  | Config
  | Cluster
  | Init
  | Diff
  | History
  | Rollback
  deriving (Show, Read)

usageApp :: String
usageApp = usageInfo header optionsDesc

-- failWithUsage :: [String] -> IO ()
failWithUsage err = hPutStrLn stderr (concat err ++ usageApp) >> exitFailure

compileApp :: IO (App)
compileApp =
  do  args <- getArgs
      (flgs, arguments) <- compileOpts args

      let isCommand = length arguments /= 0
      when  (not isCommand && flgs == defaultFlag)
            (failWithUsage ["Command missing\n"])
      act <- if isCommand
              then compileAction $ head arguments
              else return Empty
      return $ App act flgs arguments

optionsDesc :: [OptDescr (Flag -> Flag)]
optionsDesc =
  [ Option "V" ["version"]
      (NoArg (\opts -> opts {version = True})) "Display version",
    Option "h" ["help"]
      (NoArg (\opts -> opts {help = True})) "Print help",
    Option "g" ["global"]
      (NoArg (\opts -> opts {global = True})) "Global command",
    Option "m" ["message"]
      (ReqArg (\m opts -> opts {message = m}) "message") "Add a message",
    Option "v" ["verbose"]
      (NoArg (\opts -> opts {verbose = True})) "Enable verbose messages",
    Option "p" ["port"]
      (ReqArg (\m opts -> opts {port = m}) "port") "Port of the server"]

compileOpts :: [String] -> IO (Flag, [String])
compileOpts argv =
  case getOpt Permute optionsDesc argv of
    (o, n, []) -> return (foldl (flip id) defaultFlag o, n)
    (_, _, errs) -> failWithUsage errs

compileAction :: String -> IO (Action)
compileAction cmd = maybe
                      (failWithUsage ["Unknow command\n"])
                      (return)
                      (readMaybe cmd' :: Maybe Action)
  where cmd' = ((toUpper . head) cmd) : (tail cmd)

header :: String
header = "Usage: det COMMAND [--version] [--help]"
