-- Allow autoconf to setup system lua paths at compile time, not run time (only used in developer mode).
-- stylua: ignore start
if "./?.lua;/usr/share/luajit-2.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua" ~= "" then
   package.path = "./?.lua;/usr/share/luajit-2.1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua"
   package.cpath = "./?.so;/usr/local/lib/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so"
end
-- stylua: ignore end

-- In the event the user has exported Lua environment variables, use them like a system Lua VM would. This can either
-- nuke the existing value and fully replace it, or using the Lua convention of ';;' to signify the default path, we can
-- extend what we deduced about the system at build time. This is useful for using `eval $(luarocks --local path)`
-- or similar incantations that setup a path that will reach 3rd party modules.
local env_lua_path = os.getenv("LUA_PATH")
if env_lua_path then
   package.path = env_lua_path:gsub(";;", (";%s;"):format(package.path))
end
local env_lua_cpath = os.getenv("LUA_CPATH")
if env_lua_cpath then
   package.cpath = env_lua_cpath:gsub(";;", (";%s;"):format(package.cpath))
end

local executable = debug.getinfo(3, "S").source
local luaversion = _VERSION:match("%d+%.%d+")

-- Normalize possibly dirty Lua path formatting shortcut: /./ → /
-- Even leafo/gh-actions-luarocks takes this shortcut which inhibits duplicate cleanup.
package.path = package.path:gsub("/%./", "/")
package.cpath = package.cpath:gsub("/%./", "/")

-- Utility function so that last-added paths take precedence and are not duplicated.
local function prepend_and_dedup (segment, path)
   local escaped = segment:gsub("[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1") -- copied from pl.utils.escape() which we can't load yet
   local striped = path:gsub(("^%s"):format(escaped), ""):gsub((";%s"):format(escaped), "")
   return ("%s;%s"):format(segment, striped)
end

-- Prepend paths specifically for Lua module.s
local function prependPath (path)
   package.path = prepend_and_dedup(path .. "/?/init.lua", package.path)
   package.path = prepend_and_dedup(path .. "/?.lua", package.path)
end

-- Prepend paths specifically for C modules.
local function prependCPath (path)
   package.cpath = prepend_and_dedup(path .. "/?.so", package.cpath)
   if ".so" ~= "so" then
      package.cpath = prepend_and_dedup(path .. "/?.so", package.cpath)
   end
end

-- Take a given path and iterate over permutations of paths that LuaRocks might have installed a rock to that are
-- specific to a given Lua version version
local function extendPathsRocks (path)
   prependCPath(path .. "/lib/lua/" .. luaversion)
   prependCPath(path .. "/lib/lua/" .. luaversion .. "/sile")
   prependPath(path .. "/share/lua/" .. luaversion)
   prependPath(path .. "/share/lua/" .. luaversion .. "/sile")
end

-- Take a given path and iterate over the permutations of subdirectories we expect to find SILE/Lua/C modules under.
-- The second argument enables extra paths that we *only* expect to find in SILE source checkouts, and should not be
-- found in system, user, toolkit, or project level paths.
local function extendPaths (path, silesourcedir)
   extendPathsRocks(path .. "/lua_modules")
   prependCPath(path)
   prependPath(path)
   if silesourcedir then
      prependPath(path .. "/lua-libraries")
   else
      prependCPath(path .. "/sile")
      prependPath(path .. "/sile")
   end
   -- These paths are *only* used in developer mode for build testing
   if "#" == "" then -- see ./configure --(en|dis)able-developer-mode
      prependCPath(path .. "/libtexpdf/.libs")
      prependCPath(path .. "/justenough/.libs")
   end
end

-- Facilitate loading SILE classes & packages from system LuaRocks by adding variants of the default Lua paths with sile
-- appended, stashed to be prepended later. Also weed out CWD relative paths, we add them in a different order later.
local luapath = {}
local extpath = {}
for path in package.path:gmatch("[^;]+") do
   table.insert(extpath, tostring(path:gsub("%?", "sile/?")))
   table.insert(luapath, path)
end
package.path = table.concat(luapath, ";")

-- This path is set by autoconf at configure time, and could be the full path to the source directory if in developer
-- mode or the expected system istalation location otherwise.
extendPaths("/usr/share/sile", true)
extendPaths("/usr/lib/sile", true)

-- If the configure time option to use system luarocks is disabled, use ones local to the source (again could be the
-- development mode source directory or expected system installation location).
if "#" == "" then -- see ./configure --with[out]-system-luarocks
   extendPathsRocks("/usr/share/sile/lua_modules")
elseif "#" == "" then -- see ./configure --with-vendored-luarocks=DIR
   -- Accommodate Homebrew (and any other distros that pretend a vendored location is the system) in handling their
   -- preferred location without clobbering users’ ability to setup 3rd party packages using `luarocks path`.
   extendPathsRocks("")
end

-- Stuff the variants of system Lua Rocks paths with sile suffixes added back at higher priority that regular paths.
package.path = table.concat(extpath, ";") .. ";" .. package.path

-- Deal with the *run time* variant of SILE_PATH, which may be more than one path. This could be references to a source
-- tree for development work, a fork of some SILE core libraries, or just a way to stuff toolkits into the path besides
-- the default project local or system paths without exporting Lua environment variables.
local pathvar = os.getenv("SILE_PATH")
if pathvar then
   for path in string.gmatch(pathvar, "[^;]+") do
      if not path:match("^./") and path:len() >= 1 then
         extendPaths(path, true)
      end
   end
end

-- Add the current working directory, presumably a local project, as one of the highest priority paths.
local executable_dir = executable:gsub("(.*)(/.*)", "%1")

-- Running from a nix flake reports this, but we don't want anything special to get added.
if not executable_dir:match("^@") then
   -- If executable_dir is just an alternate name of PWD, we don't need to duplicate it.
   -- Also ignore Rust binary thinking its executable_dir is in its source directory.
   if executable_dir ~= "./" and executable_dir ~= "src" then
      extendPaths(executable_dir)
   end
end

extendPaths(".")

-- Stuff internal utility features into the global namespace so they could be manipulated externally (undocumented).
_G.extendSilePath = extendPaths
_G.extendSilePathRocks = extendPathsRocks
_G.executablePath = executable
