{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Utils.Commands where

import Control.Monad.Error
import System.Directory
import System.Exit
import System.FilePath
import System.IO
import System.Process

yesOrNo :: IO Bool
yesOrNo = do
  hFlush stdout
  input <- getLine
  case input of
    "y" -> return True
    "n" -> return False
    _   -> do putStr "Must type 'y' for yes or 'n' for no: "
              yesOrNo

inDir :: (MonadError String m, MonadIO m) => FilePath -> m a -> m a
inDir dir doStuff = do
  here <- liftIO $ getCurrentDirectory
  liftIO $ createDirectoryIfMissing True dir
  liftIO $ setCurrentDirectory dir
  result <- doStuff
  liftIO $ setCurrentDirectory here
  return result

copyDir :: (MonadError String m, MonadIO m) => FilePath -> FilePath -> m ()
copyDir src dst = do
  exists <- liftIO $ doesDirectoryExist src
  if exists
    then liftIO $ copyDir' src dst
    else throwError $ "Directory " ++ src ++ " does not exist"

copyDir' ::  FilePath -> FilePath -> IO ()
copyDir' src dst = do
  createDirectoryIfMissing True dst
  content <- getDirectoryContents src
  let paths = filter (`notElem` [".", "..",".git",".gitignore"]) content
  forM_ paths $ \name -> do
    let srcPath = src </> name
    let dstPath = dst </> name
    isDirectory <- doesDirectoryExist srcPath
    (if isDirectory then copyDir' else copyFile) srcPath dstPath

git :: (MonadError String m, MonadIO m) => [String] -> m String
git = run "git"

run :: (MonadError String m, MonadIO m) => String -> [String] -> m String
run command args =
  do result <- liftIO runCommand
     case result of
       Right out -> return out
       Left err -> throwError $
                   "failure when running:" ++ concatMap (' ':) (command:args) ++ "\n" ++ err
  where
    runCommand = do
      (exitCode, stdout, stderr) <- readProcessWithExitCode command args ""
      return $ case exitCode of
                 ExitSuccess -> Right stdout
                 ExitFailure code
                     | code == 127  -> Left missingExe  -- UNIX
                     | code == 9009 -> Left missingExe  -- Windows
                     | otherwise    -> Left stderr

    missingExe =
        "Could not find command '" ++ command ++ "'. Do you have it installed?\n\
        \  Can it be run from anywhere? I.e. is it on your PATH?"


out :: (MonadError String m, MonadIO m) => String -> m ()
out string = liftIO $ hPutStrLn stdout string'
    where
      string' = if not (null string) && last string == '\n' then init string else string