-----------------------------------------------------------------------------
-- |
-- Module      :  Static.Resources.Checked
-- Copyright   :  (c) Scrive 2012
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  mariusz@scrive.com
-- Stability   :  development
-- Portability :  portable
--
-- Basic consistency checker.


module Static.Resources.Checker (
                               -- * Checker
                                 check,
                                 isStaticResourceFile
                        ) where
import System.IO.HVFS.Utils
import Data.List
import Control.Monad
import Static.Resources.Types
import Data.Functor
import Control.Monad.Error
import System.Directory

-- | Performs check. Left is error with some description.
-- * Fails with css and js files avaible but not listed in spec.
-- * Fails with css and js files that are in spec, but not avaible.

check :: String -> ResourceSpec -> IO (Either String ())
check dir spec = do
    sysFiles <- mapM canonicalizePath =<< filter isStaticResourceFile <$> recurseDir SystemFS dir
    specFiles <- mapM (\r -> canonicalizePath $ dir ++ "/" ++ path r)  $ (join $ resources <$> sets spec)
    ignoredFiles <- mapM (\p -> canonicalizePath $ dir ++ "/" ++ p) $  ignored spec
    let diff a b = fileDiff (sort $ nub a) (sort $ nub b)
    runErrorT $ do
       when (not $ null $ diff sysFiles (specFiles ++ ignoredFiles)) $
        throwError $ "Files from some sets are not availble in FS: \n" ++ (unlines $ diff sysFiles (specFiles ++ ignoredFiles)) ++ "\n\n"
       when (not $ null $ diff (specFiles ++ ignoredFiles) sysFiles) $
        throwError $ "Some files on filesystem are not listed in any set or ignored: \n" ++ (unlines $ diff (specFiles ++ ignoredFiles) sysFiles) ++ "\n\n"

-- | Difference between two sets. Simillar to -, but with some respect to dirs.
fileDiff:: [FilePath] -> [FilePath] -> [FilePath]
fileDiff (f1:f1s) (f2:f2s) 
    | (isDir f1 && f1 `isPrefixOf` f2) = fileDiff (f1:f1s) f2s
    | (isDir f2 && f2 `isPrefixOf` f1) = fileDiff f1s (f2 : f2s)
    | (f1 == f2)                     = fileDiff f1s f2s
    | (f1 <= f2) && (isDir f1)       = fileDiff f1s (f2:f2s)
    | (f1 <= f2)                     = f1 : (fileDiff f1s (f2:f2s))
    | otherwise 	             = fileDiff (f1:f1s) f2s

fileDiff [] fs                       = filter (not . isDir) fs
fileDiff _ _                         = []

isDir :: String -> Bool
isDir = not . isStaticResourceFile

isStaticResourceFile :: String -> Bool 
isStaticResourceFile fn = any (`isSuffixOf` fn) [".css",".js",".less"]