    module Derive.Flags(Flag(..), getFlags, addFlags, flagInfo) where

    import System.Environment
    import System.Console.GetOpt
    import System.Directory
    import Language.Haskell
    import System.Exit
    import System.IO
    import Data.Maybe

    data Flag = Version | Help | Output String | Import String | Modu String
              | Append | Derive [String] | NoOpts | Preprocessor | Test | Generate
                deriving (Eq, Show)

    options :: [OptDescr Flag]
    options =
        [Option "v"  ["version"]  (NoArg Version)          "show version number"
        ,Option "h?" ["help"]     (NoArg Help)             "show help message"
        ,Option "o"  ["output"]   (ReqArg Output "FILE")   "output FILE"
        ,Option "i"  ["import"]   (OptArg (Import . fromMaybe "") "MODULE") "add an import statement"
        ,Option "m"  ["module"]   (ReqArg Modu "MODULE")   "add a module MODULE where statement"
        ,Option "a"  ["append"]   (NoArg Append)           "append the result to the file"
        ,Option "d"  ["derive"]   (ReqArg splt "DERIVES")  "things to derive for all types"
        ,Option "n"  ["no-opts"]  (NoArg NoOpts)           "ignore the file options"
        ,Option "F"  ["preprocessor"] (NoArg Preprocessor) "operate as a GHC preprocessor with -pgmF"
        ,Option ""   ["test"]     (NoArg Test)             "run the test suite"
        ,Option ""   ["generate"] (NoArg Generate)         "perform code generation"
        ]
        where splt = Derive . words . map (\x -> if x == ',' then ' ' else x)

    flagInfo = usageInfo "Usage: derive [OPTION...] files..." options

    getFlags :: IO ([Flag], [String])
    getFlags = do
        args <- getArgs
        case getOpt Permute options args of
            (o,n,[]  ) | Version `elem` o -> putStrLn "Derive 2.5.* (C) Neil Mitchell 2006-2013" >> exitSuccess
                       | Help `elem` o    -> putStr flagInfo >> exitSuccess
                       | Preprocessor `elem` o -> return (o,n)
                       | otherwise        -> do files <- mapM pickFile n; return (o, files)
            (_,_,errs) -> hPutStr stderr (concat errs ++ flagInfo) >> exitFailure
        where
            exitSuccess = exitWith ExitSuccess

    pickFile :: FilePath -> IO FilePath
    pickFile orig = f [orig, orig ++ ".hs", orig ++ ".lhs"]
        where
            f [] = error $ "File not found: " ++ orig
            f (x:xs) = do
                b <- doesFileExist x
                if b then return x else f xs

    addFlags :: [Flag] -> (SrcLoc, [String]) -> [Flag]
    addFlags flags (sl,xs)
        | NoOpts `elem` flags = flags
        | errs /= [] = error $ prettyPrint sl ++ "\n" ++ concat errs
        | otherwise = flags ++ a
        where (a,_,errs) = getOpt Permute options xs