{-# LANGUAGE DeriveDataTypeable #-}

module Distribution.Client.SavedFlags
       ( readCommandFlags, writeCommandFlags
       , readSavedArgs, writeSavedArgs
       ) where

import Distribution.Simple.Command
import Distribution.Simple.UserHooks ( Args )
import Distribution.Simple.Utils
       ( createDirectoryIfMissingVerbose, unintersperse )
import Distribution.Verbosity

import Control.Exception ( Exception, throwIO )
import Control.Monad ( liftM )
import Data.List ( intercalate )
import Data.Maybe ( fromMaybe )
import Data.Typeable
import System.Directory ( doesFileExist )
import System.FilePath ( takeDirectory )


writeSavedArgs :: Verbosity -> FilePath -> [String] -> IO ()
writeSavedArgs verbosity path args = do
  createDirectoryIfMissingVerbose
    (lessVerbose verbosity) True (takeDirectory path)
  writeFile path (intercalate "\0" args)


-- | Write command-line flags to a file, separated by null characters. This
-- format is also suitable for the @xargs -0@ command. Using the null
-- character also avoids the problem of escaping newlines or spaces,
-- because unlike other whitespace characters, the null character is
-- not valid in command-line arguments.
writeCommandFlags :: Verbosity -> FilePath -> CommandUI flags -> flags -> IO ()
writeCommandFlags verbosity path command flags =
  writeSavedArgs verbosity path (commandShowOptions command flags)


readSavedArgs :: FilePath -> IO (Maybe [String])
readSavedArgs path = do
  exists <- doesFileExist path
  if exists
     then liftM (Just . unintersperse '\0') (readFile path)
    else return Nothing


-- | Read command-line arguments, separated by null characters, from a file.
-- Returns the default flags if the file does not exist.
readCommandFlags :: FilePath -> CommandUI flags -> IO flags
readCommandFlags path command = do
  savedArgs <- liftM (fromMaybe []) (readSavedArgs path)
  case (commandParseArgs command True savedArgs) of
    CommandHelp _ -> throwIO (SavedArgsErrorHelp savedArgs)
    CommandList _ -> throwIO (SavedArgsErrorList savedArgs)
    CommandErrors errs -> throwIO (SavedArgsErrorOther savedArgs errs)
    CommandReadyToGo (mkFlags, _) ->
      return (mkFlags (commandDefaultFlags command))

-- -----------------------------------------------------------------------------
-- * Exceptions
-- -----------------------------------------------------------------------------

data SavedArgsError
    = SavedArgsErrorHelp Args
    | SavedArgsErrorList Args
    | SavedArgsErrorOther Args [String]
  deriving (Typeable)

instance Show SavedArgsError where
  show (SavedArgsErrorHelp args) =
    "unexpected flag '--help', saved command line was:\n"
    ++ intercalate " " args
  show (SavedArgsErrorList args) =
    "unexpected flag '--list-options', saved command line was:\n"
    ++ intercalate " " args
  show (SavedArgsErrorOther args errs) =
    "saved command line was:\n"
    ++ intercalate " " args ++ "\n"
    ++ "encountered errors:\n"
    ++ intercalate "\n" errs

instance Exception SavedArgsError