local profile = require("core.profile")
local ForwardUserFunctionToObject = function(sourceObj, targetObj)
  assert(sourceObj.LuaObjectScript, "Source object " .. tostring(sourceObj) .. " does not have child \"LuaObjectScript\"")
  assert(targetObj.LuaObjectScript, "Target object " .. tostring(targetObj) .. " does not have child \"LuaObjectScript\"")
  local builtInHooks = {}
  for _, v in pairs({
    "OnInit",
    "OnLevelInit",
    "FirstInitBool",
    "Pickle",
    "Unpickle",
    "RegisterPostSubObjectInit",
    "OnAICreateLuaState",
    "OnAIPreSpawn",
    "OnAIPostSpawn",
    "OnAIUpdate",
    "OnAIShutdown",
    "OnCreatureUpdate",
    "OnStartGameFromThisLevel",
    "OnLevelUpdate",
    "OnLevelShutdown",
    "OnInit",
    "OnUpdate",
    "OnTeardown",
    "OnSaveCheckpoint",
    "OnRestoreCheckpoint",
    "OnBeforeRestart",
    "OnGOCreateLuaState",
    "IsDeathAllowed",
    "OnBreakableBroken",
    "OnDeath",
    "OnHitGameObject",
    "OnWeaponHitGameObject",
    "OnHitByWeapon",
    "OnMarkerEnterZone",
    "OnMarkerExitZone",
    "OnSequenceSkipped",
    "OnTokenDropped",
    "OnTokenLoseOwnership",
    "OnTokenTakeOwnership",
    "OnWadLoaded",
    "OnUseWorld",
    "OnWeaponApproaching",
    "OnWeaponEmbed",
    "OnWeaponLockedOn",
    "OnWeaponUnembed",
    "OnInteractStart",
    "OnInteractFinish",
    "OnInteractAbort",
    "OnInteractDone",
    "OnWeaponEmbed",
    "OnHitByArrow",
    "OnInteractWithObjectStart",
    "OnInteractWithObjectFinish",
    "OnInteractWithObjectAbort",
    "OnInteractWithObjectDone",
    "OnScriptLoaded",
    "OnFirstStart",
    "OnFirstPreStart",
    "OnStart",
    "OnPreStart"
  }) do
    builtInHooks[v] = true
  end
  local sourceMetatable = getmetatable(sourceObj.LuaObjectScript)
  assert(not sourceMetatable.__targetObject, "Source object already has forwarding initialized")
  sourceMetatable.__targetObject = targetObj.LuaObjectScript
  if type(sourceMetatable.__index) == "table" then
    sourceMetatable.__indexTable = sourceMetatable.__index
    sourceMetatable.__index = nil
    function sourceMetatable.__index(sourceLuaObject, key)
      if sourceMetatable.__indexTable[key] == nil and getmetatable(sourceLuaObject).__targetObject then
        return getmetatable(sourceLuaObject).__targetObject[key]
      else
        return sourceMetatable.__indexTable[key]
      end
    end
  end
end
local Map = function(t, fn, ...)
  local nt = {}
  for _, v in ipairs(t) do
    local rv = table.pack(fn(v, ...))
    if #rv == 1 then
      table.insert(nt, rv[1])
    elseif 1 < #rv then
      table.insert(nt, rv)
    else
      table.insert(nt, nil)
    end
  end
  return nt
end
local IMap = function(t, fn, ...)
  local nt = {}
  for i, v in ipairs(t) do
    local rv = table.pack(fn(v, i, ...))
    if #rv == 1 then
      table.insert(nt, rv[1])
    elseif 1 < #rv then
      table.insert(nt, rv)
    else
      table.insert(nt, nil)
    end
  end
  return nt
end
local KeyMap = function(t, fn, ...)
  local nt = {}
  for k, v in pairs(t) do
    local rv = table.pack(fn(v, k, ...))
    if #rv == 1 then
      table.insert(nt, rv[1])
    elseif 1 < #rv then
      table.insert(nt, rv)
    else
      table.insert(nt, nil)
    end
  end
  return nt
end
return profile.WrapLibrary({
  ForwardUserFunctionToObject = ForwardUserFunctionToObject,
  Map = Map,
  IMap = IMap,
  KeyMap = KeyMap
})
