{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Text.Pandoc.Lua.Packages
  ( LuaPackageParams (..)
  , installPandocPackageSearcher
  ) where
import Prelude
import Control.Monad (forM_)
import Data.ByteString (ByteString)
import Data.IORef (IORef)
import Foreign.Lua (Lua, NumResults, liftIO)
import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir)
import Text.Pandoc.MediaBag (MediaBag)
import qualified Foreign.Lua as Lua
import Text.Pandoc.Lua.Module.Pandoc as Pandoc
import Text.Pandoc.Lua.Module.MediaBag as MediaBag
import Text.Pandoc.Lua.Module.Utils as Utils
data LuaPackageParams = LuaPackageParams
  { luaPkgCommonState :: CommonState
  , luaPkgDataDir :: Maybe FilePath
  , luaPkgMediaBag :: IORef MediaBag
  }
installPandocPackageSearcher :: LuaPackageParams -> Lua ()
installPandocPackageSearcher luaPkgParams = do
  Lua.getglobal' "package.searchers"
  shiftArray
  Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams)
  Lua.rawseti (Lua.nthFromTop 2) 1
  Lua.pop 1           
 where
  shiftArray = forM_ [4, 3, 2, 1] $ \i -> do
    Lua.rawgeti (-1) i
    Lua.rawseti (-2) (i + 1)
pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults
pandocPackageSearcher luaPkgParams pkgName =
  case pkgName of
    "pandoc"          -> let datadir = luaPkgDataDir luaPkgParams
                         in pushWrappedHsFun (Pandoc.pushModule datadir)
    "pandoc.mediabag" -> let st    = luaPkgCommonState luaPkgParams
                             mbRef = luaPkgMediaBag luaPkgParams
                         in pushWrappedHsFun (MediaBag.pushModule st mbRef)
    "pandoc.utils"    -> let datadirMb = luaPkgDataDir luaPkgParams
                         in pushWrappedHsFun (Utils.pushModule datadirMb)
    _ -> searchPureLuaLoader
 where
  pushWrappedHsFun f = do
    Lua.pushHaskellFunction f
    return 1
  searchPureLuaLoader = do
    let filename = pkgName ++ ".lua"
    modScript <- liftIO (dataDirScript (luaPkgDataDir luaPkgParams) filename)
    case modScript of
      Just script -> pushWrappedHsFun (loadStringAsPackage pkgName script)
      Nothing -> do
        Lua.push ("no file '" ++ filename ++ "' in pandoc's datadir")
        return 1
loadStringAsPackage :: String -> ByteString -> Lua NumResults
loadStringAsPackage pkgName script = do
  status <- Lua.dostring script
  if status == Lua.OK
    then return (1 :: NumResults)
    else do
      msg <- Lua.popValue
      Lua.raiseError ("Error while loading `" <> pkgName <> "`.\n" <> msg)
dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe ByteString)
dataDirScript datadir moduleFile = do
  res <- runIO $ setUserDataDir datadir >> readDataFile moduleFile
  return $ case res of
    Left _ -> Nothing
    Right s -> Just s