{-# LANGUAGE ViewPatterns #-}
-- | This module provides a basic framework to render LaTeX formulae inside Pandoc documents
--   for Hakyll pages.
--
--   See the latex-formulae page on GitHub for more information.
--
--      https://github.com/liamoc/latex-formulae#readme
--
module Hakyll.Contrib.LaTeX
       ( initFormulaCompilerDataURI
       , CacheSize
       , compileFormulaeDataURI
       ) where

import Image.LaTeX.Render
import Image.LaTeX.Render.Pandoc
import Text.Pandoc.Definition
import Hakyll.Core.Item
import Hakyll.Core.Compiler
import Data.Char
import qualified Data.Cache.LRU.IO as LRU
import Control.Applicative
import Prelude

-- | Number of formula images to keep in memory during a @watch@ session.
type CacheSize = Integer

-- | Creates a formula compiler with caching. Can be used as in the following minimal example:
--
-- >    main = do
-- >       renderFormulae <- initFormulaCompilerDataURI 1000 defaultEnv
-- >       hakyll $
-- >         match "posts/*.markdown" $ do
-- >           route $ setExtension "html"
-- >           compile $ pandocCompilerWithTransformM (renderFormulae defaultPandocFormulaOptions)
--
initFormulaCompilerDataURI :: CacheSize -> EnvironmentOptions
                           -> IO (PandocFormulaOptions -> Pandoc -> Compiler Pandoc)
initFormulaCompilerDataURI cs eo = do
    mImageForFormula <- curry <$> memoizeLru (Just cs) (uncurry drawFormula)
    let eachFormula x y = do
          putStrLn $ "    formula (" ++ environment x ++ ") \"" ++ equationPreview y ++ "\""
          mImageForFormula x y
    return $ \fo -> unsafeCompiler . convertAllFormulaeDataURIWith eachFormula fo
  where
    drawFormula x y = do
      putStrLn "      drawing..."
      imageForFormula eo x y

-- | A formula compiler that does not use caching, which works in a more drop-in fashion, as in:
--
-- > compile $ pandocCompilerWithTransformM (compileFormulaeDataURI defaultEnv defaultPandocFormulaOptions)
--
compileFormulaeDataURI :: EnvironmentOptions
                       -> PandocFormulaOptions
                       -> Pandoc -> Compiler Pandoc
compileFormulaeDataURI eo po =
    let eachFormula x y = do
          putStrLn $ "    formula (" ++ environment x ++ ") \"" ++ equationPreview y ++ "\""
          putStrLn   "      drawing..."
          imageForFormula eo x y
    in unsafeCompiler . convertAllFormulaeDataURIWith eachFormula po

equationPreview :: String -> String
equationPreview (dropWhile isSpace -> x)
      | length x <= 16 = x
      | otherwise      = take 16 $ filter (/= '\n') x ++ "..."

memoizeLru :: Ord a => Maybe Integer -> (a -> IO b) -> IO (a -> IO b)
memoizeLru msize action = do
    lru <- LRU.newAtomicLRU msize
    return $ \arg -> do
        mret <- LRU.lookup arg lru
        case mret of
            Just ret -> return ret
            Nothing -> do
                ret <- action arg
                LRU.insert arg ret lru
                return ret