-- | This module deals with locating assets on the disk, and determining which
-- assets needs preprocessing.
--
-- In development mode:
-- * At startup, build a mapping of source files to destination files together
--   with any preprocessors that should be run on them (based on extension)
-- * watch for filesystem changes, and rebuild relevant parts of this mapping
--   when necessary
-- * listen for HTTP requests and serve relevant files, performing
--   preprocessing where necessary.
-- (well, eventually do all that. For now just rebuild the BuildMapping for
-- each request).
--
-- In production mode:
-- * build the mapping
-- * preprocess all the files and output them to a particular directory.
--
-- This architecture should ensure that the file mapping is identical in each
-- mode.
--
module Web.Herringbone.Internal.GetBuildMapping where

import Control.Monad
import Control.Applicative ((<$>))
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Filesystem.Path.CurrentOS (FilePath, (</>))
import qualified Filesystem.Path.CurrentOS as F
import qualified Filesystem as F
import Prelude hiding (FilePath)

import Web.Herringbone.Internal.Types
import Web.Herringbone.Internal.Utils

getBuildMapping :: Herringbone -> IO BuildMapping
getBuildMapping hb = do
    files <- getFilesRecursiveRelative $ hbSourceDir hb
    return $ map (getBuildSpec hb) files


-- | Given a FilePath of a source file, construct a BuildSpec for the file.
getBuildSpec :: Herringbone -> FilePath -> BuildSpec
getBuildSpec hb sourcePath = BuildSpec sourcePath destPath pp
    where
    (destPath, pp) =
        fromMaybe (sourcePath, Nothing) $ do
            extension <- F.extension sourcePath
            pp'       <- lookupPP extension (hbPPs hb)
            destPath' <- swapExtension pp' sourcePath
            return (destPath', Just pp')

-- | Make the destination path of a BuildSpec absolute, using the destination
-- directory of the given Herringbone.
makeDestAbsolute :: Herringbone
                 -> BuildSpec
                 -> IO BuildSpec
makeDestAbsolute hb (BuildSpec sourcePath destPath pp) = do
    fullDestDir <- F.canonicalizePath $ hbDestDir hb
    let fullDestPath = fullDestDir </> destPath
    return $ BuildSpec sourcePath fullDestPath pp

swapExtension :: PP -> FilePath -> Maybe FilePath
swapExtension pp =
    swapExtension' (ppConsumes pp) (ppProduces pp)

swapExtension' :: Text -> Text -> FilePath -> Maybe FilePath
swapExtension' fromExt toExt path = do
    guard $ F.hasExtension path fromExt
    return $ F.replaceExtension path toExt

-- | Search for a file in a list of search paths. For example, if
-- @assets/test.txt@ exists, then
-- @searchForFile ["assets", "other_assets"] "test.txt"@ will return
-- @Just "assets/text.txt"@
searchForFile :: [FilePath]     -- ^ List of search paths
              -> FilePath       -- ^ File to search for
              -> IO (Maybe FilePath)
searchForFile searchPath path = do
    let fullPaths = map (</> path) searchPath
    matches <- filterM F.isFile fullPaths
    case matches of
        []    -> return Nothing
        (x:_) -> Just <$> F.canonicalizePath x