--  Copyright (C) 2003 David Roundy
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2, or (at your option)
--  any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; see the file COPYING.  If not, write to
--  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
--  Boston, MA 02110-1301, USA.

module Darcs.ArgumentDefaults ( getDefaultFlags ) where
import Data.Maybe ( listToMaybe, mapMaybe )

import Darcs.Arguments ( DarcsFlag,
                         atomicOptions, DarcsAtomicOption( .. ), DarcsOption ( .. ),
                         applyDefaults,
                         arein )
import Darcs.Commands ( CommandControl( CommandData ),
                        commandAlloptions )
import Darcs.Commands.Help ( commandControlList )
import Darcs.Repository.Prefs ( getGlobal, getPreflist )

getDefaultFlags :: String -> [DarcsOption] -> [DarcsFlag] -> IO [DarcsFlag]
getDefaultFlags com com_opts already = do
    repo_defs   <- defaultContent $ getPreflist "defaults"
    global_defs <- defaultContent $ getGlobal   "defaults"
    let repo_flags = getFlagsFrom com com_opts already repo_defs
        global_flags = getFlagsFrom com com_opts
                                          (already++repo_flags) global_defs
    return $ applyDefaults com_opts     -- hard-coded defaults (respects user preferences)
           $ repo_flags ++ global_flags -- user preferences

getFlagsFrom :: String -> [DarcsOption] -> [DarcsFlag] -> [(String,String,String)] -> [DarcsFlag]
getFlagsFrom com com_opts already defs =
    options_for com_defs com_opts com_opts ++
    options_for all_defs com_opts all_opts
    where com_defs = filter (\ (c,_,_) -> c == com) defs
          all_defs = filter (\ (c,_,_) -> c == "ALL") defs
          options_for d o ao = concatMap (findOption o ao already) d
          all_opts = concatMap get_opts commandControlList
          get_opts (CommandData c) = let (o1, o2) = commandAlloptions c
                                      in o1 ++ o2
          get_opts _                = []

findOption :: [DarcsOption] -> [DarcsOption] -> [DarcsFlag] -> (String,String,String) -> [DarcsFlag]
findOption opts all_opts already (c, f, d) =
    if null $ mapMaybe choose_option all_opts
    then error $ "Bad default option: command '"++c++"' has no option '"++f++"'."
    else concat $ mapMaybe choose_option opts
    where choose_atomic_option (DarcsNoArgOption _ fls o _)
              | f `elem` fls = if null d
                               then Just [o]
                               else error $ "Bad default option: '"++f
                                        ++"' takes no argument, but '"++d
                                        ++"' argument given."
          choose_atomic_option (DarcsArgOption _ fls o _ _)
              | f `elem` fls = if null d
                               then error $ "Bad default option: '"++f
                                        ++"' requires an argument, but no "
                                        ++"argument given."
                               else Just [o d]
          choose_atomic_option _ = Nothing
          choose_option o
              | o `arein` already = Just []
              | otherwise = listToMaybe $ mapMaybe choose_atomic_option $ atomicOptions o

defaultContent :: IO [String] -> IO [(String,String,String)]
defaultContent = fmap (mapMaybe (doline . words))
    where doline (c:a:r) = Just (c, drop_dashdash a, unwords r)
          doline _ = Nothing
          drop_dashdash ('-':'-':a) = a
          drop_dashdash a = a