-- | This module provides some functions that are useful in the
-- processing of command line options, and that are shared between
-- several algorithms.

module CommandLine where

-- import other stuff
import System.Exit
import System.IO
import Data.List
import Data.Char

-- ----------------------------------------------------------------------
-- * Formatting of lists and strings

-- | A general list-to-string function. Example:
-- 
-- > string_of_list "{" ", " "}" "{}" show [1,2,3] = "{1, 2, 3}"
string_of_list :: String -> String -> String -> String -> (t -> String) -> [t] -> String
string_of_list lpar comma rpar nil string_of_elt lst =
  let string_of_tail lst =
        case lst of
          [] -> ""
          h:t -> comma ++ string_of_elt h ++ string_of_tail t
  in
  case lst of
    [] -> nil
    h:t -> lpar ++ string_of_elt h ++ string_of_tail t ++ rpar

-- ----------------------------------------------------------------------
-- * Option processing
      
-- | Exit with an error message after a command line error. This also
-- outputs information on where to find command line help.
optfail :: String -> IO a
optfail msg = do
  hPutStr stderr msg
  hPutStrLn stderr "Try --help for more info."
  exitFailure

-- | Parse a string to an integer, or return 'Nothing' on failure.
parse_int :: (Integral r) => String -> Maybe r
parse_int s = case reads s of
  [(n, "")] -> Just (fromInteger n)
  _ -> Nothing

-- | Parse a string to a list of integers, or return 'Nothing' on failure.
parse_list_int :: String -> Maybe [Int]      
parse_list_int s = case reads s of
  [(ns, "")] -> Just ns
  _ -> Nothing

-- | Parse a string to a 'Double', or return 'Nothing' on failure.
parse_double :: String -> Maybe Double
parse_double s = case reads s of
  [(n, "")] -> Just n
  _ -> Nothing

-- | In an association list, find the key that best matches the given
-- string. If one key matches exactly, return the corresponding
-- key-value pair. Otherwise, return a list of all key-value pairs
-- whose key have the given string as a prefix. This list could be of
-- length 0 (no match), 1 (unique match), or greater (ambiguous key).
-- Note: the keys in the association list must be lower case. The
-- input string is converted to lower case as well, resulting in
-- case-insensitive matching.
match_enum :: [(String, a)] -> String -> [(String, a)]
match_enum list key =
  case lookup s list of
    Just v -> [(s,v)]
    Nothing -> filter (\(k,v) -> isPrefixOf s k) list
  where
    s = map toLower key
    
-- | Pretty-print a list of possible values for a parameter. The
-- first argument is the name of the parameter, and the second
-- argument is its enumeration.
show_enum :: String -> [(String, a)] -> String    
show_enum param list =
  "Possible values for " ++ param ++ " are: " ++
  string_of_list "" ", " "" "no possible values" fst list ++ ".\n"
