-- Copyright 2010 Google Inc.
-- 
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
-- 
--      http://www.apache.org/licenses/LICENSE-2.0
-- 
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

module Barley.Project
    ( ProjectDir(..)
    , enter
    , init
    ) where

import Control.Monad (unless, when)
import Paths_barley -- generated by cabal
import Prelude hiding (init)
import System.Directory
import System.Exit
import System.FilePath

-- | A specification of a project directory
data ProjectDir = CurrentDir | ProjectDir FilePath

projectPath :: ProjectDir -> FilePath
projectPath CurrentDir = "."  -- FIXME: The use of "." is probably not right
projectPath (ProjectDir fp) = fp
    
-- | The presence of this file indicates a directory is a barley project.
-- In the future we might store information in it.
markerFile :: FilePath
markerFile = ".barley-project"

-- | Change into the project directory.
-- Fails if the directory can't be entered.
enter :: ProjectDir -> IO ()
enter projectDir = do
    exists <- case projectDir of
        CurrentDir -> return True
        ProjectDir fp -> doesDirectoryExist fp
    unless exists $
        putStrLn ("Project directory doesn't exist: " ++ pdPath) >>
        exitFailure
    hasMarker <- doesFileExist (pdPath </> markerFile)
    unless hasMarker $
        putStrLn "Directory doesn't appear to be a Barely project." >>
        putStrLn ("Missing .barley-project file in directory: " ++ pdPath) >>
        exitFailure
    case projectDir of
        CurrentDir -> return ()
        ProjectDir fp -> setCurrentDirectory fp
  where
    pdPath = projectPath projectDir

-- | Create a project directory structure.
init :: Bool -> ProjectDir -> IO ()
init warnIfNotEmpty projectDir = nothingHere >>= \b -> if b
    then copyInitialProject projectDir
    else when warnIfNotEmpty $
        putStrLn "** This directory is not empty. Not initializing."
  where
    nothingHere = whatsHere >>= return . null . filter notDot
    whatsHere = case projectDir of
        CurrentDir -> getCurrentDirectory >>= getDirectoryContents 
        ProjectDir fp -> do
            exists <- doesDirectoryExist fp
            if exists
                then getDirectoryContents fp
                else return []
    notDot ('.':_) = False
    notDot _ = True

-- | Copy the initial project skeleton to the project directory.
copyInitialProject :: ProjectDir -> IO ()
copyInitialProject projectDir = do
    fromDir <- getSeedDir
    let toDir = projectPath projectDir
    putStrLn "Creating default project files..."
    copyTree fromDir toDir
    writeFile (toDir </> markerFile) ""
    putStrLn "...done."

-- | Locate seed dir in current dir, or data dir, or if neither, fail.
getSeedDir :: IO FilePath
getSeedDir = findFirstSeed [ getCurrentDirectory, getDataDir ]
  where
    findFirstSeed (g:gs) = do
        s <- g >>= return . (</> "seed")
        exists <- doesDirectoryExist s
        if exists
            then return s
            else findFirstSeed gs
    findFirstSeed [] = do
        putStrLn "** No seed directory found."
        putStrLn "** You should try reinstalling Barley."
        exitFailure

-- | Copy a directory tree from one place to another. The destination, or
-- the subtrees needn't exist. If they do, existing files with the same names
-- as the source will be overwritten. Other files will be left alone.
copyTree :: FilePath -> FilePath -> IO ()
copyTree from to = pick
    [(doesFileExist,        doFile),
     (doesDirectoryExist,   doDir)]
  where
    pick ((test, act):rest) = do
        bool <- test from
        if bool
            then putStrLn ("   " ++ to) >> act
            else pick rest
    pick [] =
        putStrLn $ "** Skipping funny thing in skeleton tree: " ++ from

    doFile = copyFile from to 
    doDir = do
        createDirectoryIfMissing False to
        getDirectoryContents from >>= mapM_ dive . filter notSpecial
        
    dive item = copyTree (from </> item) (to </> item)
    notSpecial item = item /= "." && item /= ".."
