{-# LANGUAGE TemplateHaskell #-}
module Web.Herringbone.Embed where

import Control.Monad (forM, (>=>), when)
import Language.Haskell.TH.Syntax (Q, Exp(..), Lit(..), runIO)
import Data.FileEmbed (embedDir)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B8
import qualified Filesystem.Path.CurrentOS as F
import qualified Data.Text as T

import Web.Herringbone

-- | Precompile and embed all assets into your source code. Call this function
-- in a Template Haskell splice. Any asset compilation failures will result in
-- a compile error.
--
-- For example:
--
-- > assets :: [(LogicalPath, ByteString)]
-- > assets = $(embedAssets
--      (herringbone
--          (setSourceDir "assets" . setDestDir ".compiled_assets"))
--      )
embedAssets :: IO Herringbone -> Q Exp
embedAssets iohb = do
    hb <- runIO iohb
    errs <- runIO (precompile hb)
    when (not . null $ errs) $
        fail ("the following assets failed to compile:\n" ++
            unlines (map show errs))

    SigE expr' _ <- embedDir (F.encodeString $ hbDestDir hb)
    let expr = transformFiles expr'

    type_ <- [t| [(LogicalPath, ByteString)] |]
    return $ SigE expr type_
    where
    transformFiles (ListE tups) =
        let f (TupE [LitE (StringL path), contents]) =
                TupE [logicalPathExp path, contents]
            f _ = error "unexpected Exp in precompileEmbed"
        in ListE $ map f tups
    transformFiles _ = error "unexpected Exp in precompileEmbed"
    logicalPathExp = logicalPathToExp . stringToLogicalPath
    stringToLogicalPath = unsafeMakeLogicalPath . T.splitOn "/" . T.pack
    logicalPathToExp logicalPath =
        AppE
            (VarE 'unsafeMakeLogicalPath)
            (ListE (map (LitE . StringL . T.unpack)
                        (fromLogicalPath logicalPath)))