{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Text.Pandoc.Lua.Packages
  ( installPandocPackageSearcher
  ) where
import Control.Monad.Catch (try)
import Control.Monad (forM_)
import Data.ByteString (ByteString)
import Foreign.Lua (Lua, NumResults)
import Text.Pandoc.Error (PandocError)
import Text.Pandoc.Class.PandocMonad (readDataFile)
import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua)
import qualified Foreign.Lua as Lua
import qualified Foreign.Lua.Module.Text as Text
import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc
import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag
import qualified Text.Pandoc.Lua.Module.System as System
import qualified Text.Pandoc.Lua.Module.Types as Types
import qualified Text.Pandoc.Lua.Module.Utils as Utils
installPandocPackageSearcher :: PandocLua ()
installPandocPackageSearcher = liftPandocLua $ do
  Lua.getglobal' "package.searchers"
  shiftArray
  Lua.pushHaskellFunction pandocPackageSearcher
  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 :: String -> PandocLua NumResults
pandocPackageSearcher pkgName =
  case pkgName of
    "pandoc"          -> pushWrappedHsFun Pandoc.pushModule
    "pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule
    "pandoc.system"   -> pushWrappedHsFun System.pushModule
    "pandoc.types"    -> pushWrappedHsFun Types.pushModule
    "pandoc.utils"    -> pushWrappedHsFun Utils.pushModule
    "text"            -> pushWrappedHsFun Text.pushModule
    _                 -> searchPureLuaLoader
 where
  pushWrappedHsFun f = liftPandocLua $ do
    Lua.pushHaskellFunction f
    return 1
  searchPureLuaLoader = do
    let filename = pkgName ++ ".lua"
    try (readDataFile filename) >>= \case
      Right script -> pushWrappedHsFun (loadStringAsPackage pkgName script)
      Left (_ :: PandocError) -> liftPandocLua $ do
        Lua.push ("\n\tno file '" ++ filename ++ "' in pandoc's datadir")
        return (1 :: NumResults)
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)