-----------------------------------------------------------------------------
-- |
-- Module      :  Static.Resources.Generation
-- Copyright   :  (c) Scrive 2012
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  mariusz@scrive.com
-- Stability   :  development
-- Portability :  portable
--
-- Module for generaing joined js and css files from spec
--

module Static.Resources.Generation
  (generateResources)
  where

import Control.Monad
import Data.Functor
import Data.Hash.MD5
import Data.Time
import Static.Resources.Types
import System.Process

-- | Concatenate a the files of a given type together, from the resource set.
generateAgregatedResourceTypeContent :: String -> ResourceType -> ResourceSet -> IO String
generateAgregatedResourceTypeContent dir rt rs = do
  let files = map path $ filterByType (== rt) rs
  s <- forM files $ \p -> readFile $ dir ++ "/" ++ p
  return $ (concat s)

-- | Generate one big file from all the CSS Files.
generateAgregatedCSSFile :: String -> ResourceSet -> IO FilePath
generateAgregatedCSSFile dir rs = do
  c <- generateAgregatedResourceTypeContent dir CSS rs
  let fn = (name rs) ++ "-" ++ (md5s $ Str $ c) ++ ".css"
  writeFile (dir ++ "/" ++ fn) c
  return fn

-- | Generate one big file of CSS from all the LESS files. Requires
-- lessc to be installed.
generateAgregatedLESSFile :: String ->  ResourceSet -> IO FilePath
generateAgregatedLESSFile dir rs = do
  let files = map path $ filterByType (== LESS) rs
  c <- fmap concat $ forM files $ \file ->
    readProcess "lessc" [dir ++ "/" ++ file] ""
  let fn = name rs ++ "-" ++ md5s (Str c) ++ ".less.css"
  writeFile (dir ++ "/" ++ fn) c
  return fn

-- | Generate seperate CSS files from the given LESS files:
--
--   The point is for these to be easy to map to in development. So
--   the generated files will be like this:
--
--   /less/foo.less → /foo.less.css
--
generateSeparateLESSFiles :: String -> ResourceSet -> IO [FilePath]
generateSeparateLESSFiles dir rs = do
  forM (map path (filterByType (== LESS) rs)) $ \file -> do
    contents <- readProcess "lessc" [dir ++ "/" ++ file] ""
    let fn = file ++ ".css"
    writeFile (dir ++ "/" ++ fn) contents
    return fn

-- | Aggregate the JS files in the given resource set.
generateAgregatedJSFile :: String -> ResourceSet -> IO FilePath
generateAgregatedJSFile dir rs = do
  c <- generateAgregatedResourceTypeContent dir JS rs
  let fn = (name rs) ++ "-" ++ (md5s $ Str $ c) ++ ".js"
  writeFile (dir ++ "/" ++ fn) c
  return fn

-- | Generate each set of resources.
generateResources ::  ImportType -> String -> ResourceSpec -> FilePath -> IO ResourceSetsForImport
generateResources it dir spec pathPrefix =
  liftM2 ResourceSetsForImport (forM (sets spec) (generateResourcesForSet it dir pathPrefix)) getCurrentTime

-- | Change 'ResourceSet' to 'ResourceSetForImport'. Will generate files if needed.
generateResourcesForSet ::  ImportType -> String -> String -> ResourceSet -> IO ResourceSetForImport
generateResourcesForSet Development dir pathPrefix rs = do
  lessF <- generateSeparateLESSFiles dir rs
  return $ ResourceSetForImport {
       set = rs
     , cssFiles = prefixize (filterByType (== CSS) rs)
     , jsFiles  = prefixize (filterByType (\t -> t == JS || t == JSX) rs)
     , lessFiles = map (appendPath pathPrefix) lessF
   }

  where prefixize = map (appendPath pathPrefix . path)

generateResourcesForSet Production dir pathPrefix rs = do
  cssF <- generateAgregatedCSSFile dir rs
  lessF <- generateAgregatedLESSFile dir rs
  jsF <- generateAgregatedJSFile dir rs
  return $ ResourceSetForImport {
       set = rs
     , cssFiles = prefixize [cssF]
     , jsFiles  = prefixize (jsF : (path <$> filterByType (== JSX) rs))
     , lessFiles = prefixize [lessF]
   }

  where prefixize = map (appendPath pathPrefix)

-- | Append to path parts with a single /.
appendPath :: String -> String -> String
appendPath left right = reverse (strip (reverse left)) ++ "/" ++ strip right

  where strip = dropWhile (=='/')