-----------------------------------------------------------------------------
-- |
-- 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
import System.Time

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

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

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

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

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

-- | Change 'ResourceSet' to 'ResourceSetForImport'. Will generate files if needed.
generateResourcesForSet ::  ImportType -> String -> ResourceSet -> IO ResourceSetForImport
generateResourcesForSet Development pathPrefix rs = do
  lessF <- generateSeparateLESSFiles 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 pathPrefix rs = do
  cssF <- generateAgregatedCSSFile rs
  lessF <- generateAgregatedLESSFile rs
  jsF <- generateAgregatedJSFile 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 (=='/')