-- Copyright: 2009 Dino Morelli
-- License: BSD3 (see LICENSE)
-- Author: Dino Morelli <dino@ui3.info>

{-# LANGUAGE FlexibleContexts #-}

module Cargs.Opts 
   ( parseOpts )
   where

import Control.Monad.Error
import System.Console.GetOpt
import System.Directory ( doesFileExist )
import Text.Printf ( printf )

import Cargs.Common ( Options (..) )
import Cargs.Conf ( confToCommand )
import Cargs.Log ( logMsg, setLoggingVerbose )
import Paths_cargs


defaultOptions :: Options
defaultOptions = Options
   { optHelp = False
   , optNoAction = False
   , optVerbose = False
   }


options :: [OptDescr (Options -> Options)]
options =
   [ Option ['h'] ["help"] 
      (NoArg (\opts -> opts { optHelp = True } ))
      "This help text"
   , Option ['n'] ["no-action"] 
      (NoArg (\opts -> opts { optNoAction = True } ))
      "Show command that would be executed, but do nothing"
   , Option ['v'] ["verbose"] 
      (NoArg (\opts -> opts { optVerbose = True } ))
      "Log events with timestamp such as conf file load, command execution and completion."
   ]


{- Parsing function to deal with both the args and config file loading
   and parsing.
-}
parseOpts :: (MonadIO m, MonadError String m) =>
   [String] ->
   m (Options, String)
parseOpts args = do
   examplePath <- liftIO $ getDataFileName "example"

   (opts, nonOpts) <- case getOpt Permute options args of
      (o, ns, []) -> return ((foldl (flip id) defaultOptions o), ns)
      (_,_,errs) -> throwError (concat errs ++ (usageText examplePath))

   when ( optVerbose opts ) $ liftIO setLoggingVerbose

   when ( optHelp opts ) $ throwError $ usageText examplePath

   config <- case nonOpts of
      (p:_) -> do
         confExists <- liftIO $ doesFileExist p
         unless (confExists) $ throwError noConfErrMsg

         confContents <- liftIO $ readFile p
         let config = confToCommand confContents

         liftIO $ logMsg $ printf "Config file loaded: %s" p

         return config

      _     -> throwError noConfErrMsg

   return (opts, config)

   where
      noConfErrMsg = "No config file specified"


usageText :: FilePath -> String
usageText examplePath =
   (usageInfo header options) ++ "\n" ++ footer
   where
      header = init $ unlines
         [ "cargs - Config file to args"
         , ""
         , "Usage:"
         , "  cargs [OPTIONS] [CONFPATH]"
         , ""
         , "CONFPATH  Path to a config file"
         , ""
         , "Options:"
         ]
      footer = init $ unlines
         [ "Example config files can be found in " ++ examplePath
         , ""
         , "This program turns the contents of a config file into a shell command. And then executes it with timestamped log output."
         , ""
         , "Take care with wildly sending files to this thing. It sounds special when we say 'config file', but the reality is this program will de-comment, de-blankline and de-newline any file you give it and try to run the resulting string as a shell command. That's it, no magic."
         , ""
         , "The exit code will be that of the shell command that was executed. If this program fails before it gets that far (say, if you give it a bad option or no config path at all) it will exit with 255."
         , ""
         , "I have to admit that this software is of questionable utility. You can achieve something very similar by writing a shell script using backslash line continuation."
         , ""
         , "In its defense, my motivations were: Ability to mark-up the conf file with comments and whitespace. Handy no-action capability so you can confirm that your command line looks good. Timestamped log output of excution. And, it's just plain fun to write Haskell software."
         , ""
         , "Version 1.0.1  2009-May-09  Dino Morelli <dino@ui3.info>"
         ]

