--------------------------------------------------------------------------------
--- Minimalistic Lua submodule system, with and without require
-- @module lua-nucleo.import
-- This file is a part of lua-nucleo library
-- @copyright lua-nucleo authors (see file `COPYRIGHT` for the license)
--------------------------------------------------------------------------------

local type, assert, loadfile, tostring, error, unpack, require,
      setmetatable, select
    = type, assert, loadfile, tostring, error, unpack or table.unpack, require,
      setmetatable, select

local create_import = function(init_with_require, code_root)
  local get_path
  local cache
  local import_in_progress_tag

  if init_with_require then
    cache = setmetatable(
        { },
        {
          __metatable = "name_cache";
          __index = function(t, k)
            local v = k:gsub("%.lua$", ""):gsub("/", "."):gsub("\\", ".")
            t[k] = v
            return v
          end
        }
      )
  else
    import_in_progress_tag = function() end
    local base_path = code_root or ""
    local base_path_type = type(base_path)
    if base_path_type == "function" then
      get_path = base_path
    elseif base_path_type == "string" then
      get_path = function(filename)
        if not filename:find("^/") then
          return base_path .. filename
        else
          return filename
        end
      end
      cache = { }
    else
      error("import: bad base path type")
    end
  end

  return function(filename)
    local t
    local fn_type = type(filename)
    if fn_type == "table" then
      t = filename
    elseif fn_type == "string" then
      if init_with_require then
        t = assert(
            require(cache[filename]),
            "import: bad implementation",
            2
          )
        if t == true then
          -- This means that module did not return anything.
          error("import: bad implementation", 2)
        end
      else
        local full_path = get_path(filename)
        t = cache[filename]
        if t == nil then
          cache[filename] = import_in_progress_tag
          t = assert(
              assert(loadfile(full_path))(), "import: bad implementation", 2
            )
          cache[filename] = t
        elseif t == import_in_progress_tag then
          error(
              "import: cyclic dependency detected while loading: " .. filename,
              2
            )
        end
      end
    else
      error("import: bad filename type: " .. fn_type, 2)
    end

    return function(symbols)
      local result = { }
      local sym_type = type(symbols)

      if sym_type ~= "nil" then
        if sym_type == "table" then
          for i = 1, #symbols do
            local name = symbols[i]
            local v = t[name]
            if v == nil then
              error(
                  "import: key `" .. tostring(name) .. "' not found in `"
                  .. (fn_type == "string" and filename or "(table)") .. "'",
                  2
                )
            end
            result[i] = v
          end
        elseif sym_type == "string" then
          local v = t[symbols]
          if v == nil then
            error(
                "import: key `" .. symbols .. "' not found in `"
                .. (fn_type == "string" and filename or "(table)") .. "'",
                2
              )
          end
          result[1] = v
        else
          error("import: bad symbols type: " .. sym_type, 2)
        end

      end
      result[#result + 1] = t

      return unpack(result)
    end
  end
end

if declare then declare 'import' end

-- when module is loaded by require the first chunk parameter is the module name
local chunk_param = select(1, ...)
local init_with_require = chunk_param == "lua-nucleo.import"

import = create_import(init_with_require, chunk_param)
