-- | Manage lambdabot's state files. There are three relevant directories:
--
-- * local: @./State/@ (configurable, see `outputDir`)
-- * home:  @~/.lambdabot/State/@
-- * data:  relative to the data directory of the @lambdabot@ package.
--
-- Files are stored locally if the directory exists; otherwise, in the home
-- directory. When reading a state file, and the file exists in the data
-- directory but nowhere else, then it is picked up from the data directory.

module Lambdabot.File
    ( stateDir
    , findLBFileForReading
    , findLBFileForWriting
    , findOrCreateLBFile
    , findLBFile -- deprecated
    , outputDir
    ) where

import Lambdabot.Config
import Lambdabot.Config.Core
import Lambdabot.Monad
import Lambdabot.Util

import Control.Applicative
import Control.Monad
import System.Directory
import System.FilePath

lambdabot :: FilePath
lambdabot = ".lambdabot"

-- | Locate state directory. Returns the local directory if it exists,
-- and the home directory otherwise.
stateDir :: LB FilePath
stateDir = do
    -- look locally
    output <- getConfig outputDir
    b <- io $ doesDirectoryExist output
    if b then return output else homeDir

homeDir :: LB FilePath
homeDir = do
    output <- getConfig outputDir
    home <- io getHomeDirectory
    return $ home </> lambdabot </> output

-- | Look for the file in the local, home, and data directories.
findLBFileForReading :: FilePath -> LB (Maybe FilePath)
findLBFileForReading f = do
    state <- stateDir
    home  <- homeDir
    output <- getConfig outputDir
    rodir <- getConfig dataDir
    findFirstFile [state </> f, home </> f, rodir </> output </> f]

-- | Return file name for writing state. The file will reside in the
-- state directory (`stateDir`), and `findLBFileForWriting` ensures that
-- the state directory exists.
findLBFileForWriting :: FilePath -> LB FilePath
findLBFileForWriting f = do
    state <- stateDir
    -- ensure that the directory exists
    io $ createDirectoryIfMissing True state
    success <- io $ doesDirectoryExist state
    when (not success) $ fail $ concat ["Unable to create directory ", state]
    return $ state </> f

findFirstFile :: [FilePath] -> LB (Maybe FilePath)
findFirstFile [] = return Nothing
findFirstFile (path:ps) = do
    b <- io $ doesFileExist path
    if b then return (Just path) else findFirstFile ps

{-# DEPRECATED findLBFile
 "Use `findLBFileForReading` or `findLBFileForWriting` instead" #-}
-- | Try to find a pre-existing file, searching first in the local or home
-- directory (but not in the data directory)
findLBFile :: FilePath -> LB (Maybe String)
findLBFile f = do
    state <- stateDir
    home  <- homeDir
    findFirstFile [state </> f, home </> f]

-- | This returns the same file name as `findLBFileForWriting`.
-- If the file does not exist, it is either copied from the data (or home)
-- directory, if a copy is found there; otherwise, an empty file is
-- created instead.
findOrCreateLBFile :: FilePath -> LB String
findOrCreateLBFile f = do
    outFile <- findLBFileForWriting f
    b <- io $ doesFileExist outFile
    when (not b) $ do
        -- the file does not exist; populate it from home or data directory
        b <- findLBFileForReading f
        case b of
            Nothing      -> io $ writeFile outFile ""
            Just roFile  -> io $ copyFile roFile outFile
    return outFile