
module DepsBuilder (nonExistentMtime, build) where

import Control.Applicative
import Control.Monad
import Data.ConfigFile
import Data.List.Utils
import System.Directory
import System.FilePath.Find hiding (FileInfo, FileType)
import System.IO
import Text.JSON
import Text.Read

import AppDeps
import AppDepsUtility
import FolderJsDeps
import FolderJsDepsUtility
import CompilationUtility
import qualified ConfigFile
import Templates
import Utility


build :: ConfigParser -> EitherT String IO (AppDeps, Maybe AppDeps)
build cp = do
  forceRecomp <- hoistEither $ ConfigFile.getBool cp "DEFAULT" "force_recomp"
  if forceRecomp then checkFolderJsDeps cp Nothing else do
    cacheName <- ConfigFile.getMaybeFile cp "DEFAULT" "cache.path"
    cacheExists <- catchIO $ doesFileExist cacheName
    if not cacheExists then checkFolderJsDeps cp Nothing else do
      cacheMaybe <- fmap readMaybe $ catchIO $ readFile cacheName
      case cacheMaybe of
        Nothing -> checkFolderJsDeps cp Nothing
        Just cache -> do
          modified <- mapAppDeps calcModified cache
          if safeJsSoy modified
            then return (cache, Just modified)
            else checkFolderJsDeps cp (Just (cache, modified))
 where
  calcModified :: FileInfo -> EitherT String IO (Maybe FileInfo)
  calcModified fileInfo = do
    exists <- catchIO $ doesFileExist $ fi_path fileInfo
    if exists then do
        mtime <- catchIO $ getModificationTime $ fi_path fileInfo
        if mtime == fi_mtime fileInfo
          -- unmodified
          then return Nothing
          -- modified
          else return $ Just $ fileInfo{fi_mtime = mtime}
      -- deleted
      else return $ Just $ fileInfo{fi_mtime = nonExistentMtime}


checkFolderJsDeps :: ConfigParser -> Maybe (AppDeps, AppDeps) -> EitherT String IO (AppDeps, Maybe AppDeps)
checkFolderJsDeps cp cacheMaybe = do
  changed <- updateFolderJsDeps cp False
  case cacheMaybe of
    Just (cache, modified) -> if changed
      then closureBuilder cp cacheMaybe
      else return (cache, Just modified)
    Nothing -> closureBuilder cp cacheMaybe


closureBuilder :: ConfigParser -> Maybe (AppDeps, AppDeps) -> EitherT String IO (AppDeps, Maybe AppDeps)
closureBuilder cp cacheMaybe = do
  prefillSoyNamespaces cp
  calc <- runCU =<< appDepsSettings cp
  case cacheMaybe of
    Nothing -> return (calc, Nothing)
    Just (cache, modified) -> return (calc, Just $ mergeAppDeps calc cache modified)


prefillSoyNamespaces :: ConfigParser -> EitherT String IO ()
prefillSoyNamespaces cp = do
  public <- hoistEither $ ConfigFile.get cp "DEFAULT" "public"
  filePaths <- catchIO $ find (return True) (extension ==? ".soy") public
  forM_ filePaths $ \soyPath -> do
    soyJsPath <- hoistEither $ do
      srcPath <- ConfigFile.get cp "DEFAULT" "src.path"
      appPath <- ConfigFile.get cp "DEFAULT" "app.path"
      soyToSoyJs srcPath appPath soyPath
    exists <- catchIO $ doesFileExist soyJsPath
    if exists then return () else do
      namespace <- hoistEither $ ConfigFile.get cp "DEFAULT" "utility.templates.namespace"
      catchIO $ withFile soyJsPath WriteMode $ \h -> do
        hPutStrLn h $ "goog.provide('" ++ namespace ++ "." ++ (soyJsToNamespace soyJsPath) ++"')"
