local ML = require("design.MayaLuaTablesLibrary")
local timers = require("level.timer")
local thisObj, thisLevel, player, son
local isEnabled = true
local navObstacle
local cubbyHoles = {}
local volumeTransition, lastHoleVolume, currentZone, activeCubby
local STATE_NONE, STATE_APPROACH, STATE_TRANSITION, STATE_IDLE = 0, 1, 2, 3
local sonState = STATE_NONE
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  isEnabled = thisObj:FindLuaTableAttribute("startEnabled")
  if thisObj:FindLuaTableAttribute("navObstacle") ~= nil and thisObj:FindLuaTableAttribute("navObstacle") ~= "" then
    navObstacle = thisLevel:GetGameObject(thisObj:GetLuaTableAttribute("navObstacle"))
  end
  volumeTransition = thisLevel:FindSingleGameObject(thisObj:GetLuaTableAttribute("volumeTransition"))
  game.SubObject.SetEntityZoneHandler(thisObj, volumeTransition)
  local volume
  local hole = thisObj:FindSingleGOByName("Hole_1")
  if hole ~= nil then
    volume = thisLevel:FindSingleGameObject(thisObj:GetLuaTableAttribute("volumeHole1"))
    table.insert(cubbyHoles, {
      hole = hole.Child,
      volume = volume
    })
    game.SubObject.SetEntityZoneHandler(thisObj, volume)
    hole = thisObj:FindSingleGOByName("Hole_2")
    volume = thisLevel:FindSingleGameObject(thisObj:GetLuaTableAttribute("volumeHole2"))
    table.insert(cubbyHoles, {
      hole = hole.Child,
      volume = volume
    })
    game.SubObject.SetEntityZoneHandler(thisObj, volume)
  end
  local additionalHoles = thisObj:FindLuaTableAttribute("additionalCubbyHoles")
  local additionalVolumes = thisObj:FindLuaTableAttribute("additionalCubbyVolumes")
  if additionalHoles and additionalHoles ~= "" and additionalVolumes and additionalVolumes ~= "" then
    additionalHoles = ML.ConvertStringListToTable(additionalHoles)
    additionalVolumes = ML.ConvertStringListToTable(additionalVolumes)
    if #additionalHoles and #additionalVolumes then
      local tempHole, tempVolume
      for i = 1, #additionalHoles do
        tempHole = thisLevel:GetGameObject(additionalHoles[i]).Child
        tempVolume = thisLevel:GetGameObject(additionalVolumes[i])
        table.insert(cubbyHoles, {hole = tempHole, volume = tempVolume})
        game.SubObject.SetEntityZoneHandler(thisObj, tempVolume)
      end
    end
  end
  game.SubObject.Sleep(obj)
end
function OnStart()
  if isEnabled then
    Enable()
  else
    Disable()
  end
end
function OnSaveCheckpoint(level, obj)
  return {isEnabled = isEnabled}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  isEnabled = savedInfo.isEnabled
end
function Enable()
  isEnabled = true
  if volumeTransition then
    volumeTransition:ShowEntityVolume()
  end
  for _, cubbyHole in ipairs(cubbyHoles) do
    cubbyHole.volume:ShowEntityVolume()
  end
  if navObstacle then
    navObstacle:ShowNavObstacle()
  end
end
function Disable()
  isEnabled = false
  if volumeTransition then
    volumeTransition:HideEntityVolume()
  end
  for _, cubbyHole in ipairs(cubbyHoles) do
    cubbyHole.volume:HideEntityVolume()
  end
  if navObstacle then
    navObstacle:HideNavObstacle()
  end
  Emerge()
end
function InstantExit(pos, dir)
  if activeCubby and (sonState == STATE_TRANSITION or sonState == STATE_IDLE or sonState == STATE_APPROACH) then
    local disableObject = activeCubby.hole.LuaObjectScript
    ClearActiveHole()
    disableObject.Interrupt()
    if pos == nil or dir == nil then
      local exitHole
      for _, cubbyHole in ipairs(cubbyHoles) do
        if lastHoleVolume == cubbyHole.volume then
          exitHole = cubbyHole.hole
          break
        end
      end
      print("EXIT HOLE" .. tostring(exitHole:GetWorldPosition()))
      son:Warp(pos or exitHole:GetWorldPosition(), dir or -exitHole:GetWorldForward())
    else
      son:Warp(pos, dir)
    end
  end
end
function OnMarkerEnterZone(level, scriptObj, volumeGameObject, markerGameObject, markerId)
  if currentZone == volumeGameObject or markerGameObject ~= player or game.LargeInteger.HashString(3) ~= markerId and game.LargeInteger.HashString(3) ~= game.LargeInteger.HashString(tostring(markerId)) then
    return
  end
  if volumeGameObject == volumeTransition and currentZone ~= volumeTransition then
    EnterHole()
  elseif volumeGameObject ~= volumeTransition then
    lastHoleVolume = volumeGameObject
    if currentZone == volumeTransition then
      Emerge()
    end
  end
  currentZone = volumeGameObject
end
function OnMarkerExitZone(level, scriptObj, volumeGameObject, markerGameObject, markerId)
  if markerGameObject ~= player or game.LargeInteger.HashString(3) ~= markerId and game.LargeInteger.HashString(3) ~= game.LargeInteger.HashString(tostring(markerId)) then
    return
  end
  currentZone = volumeGameObject
  if volumeGameObject == volumeTransition then
    return
  end
  if player:InsideZone(volumeTransition) then
    lastHoleVolume = volumeGameObject
    EnterHole()
    currentZone = volumeTransition
    return
  end
  for i = 1, #cubbyHoles do
    if player:InsideZone(cubbyHoles[i].volume) then
      return
    end
  end
  lastHoleVolume = volumeGameObject
  Emerge()
  currentZone = nil
end
function EnterHole()
  if activeCubby then
    return
  end
  for i = 1, #cubbyHoles do
    if currentZone == cubbyHoles[i].volume then
      activeCubby = cubbyHoles[i]
      activeCubby.hole.LuaObjectScript.Enable()
      son:RequestInteract(thisObj)
      sonState = STATE_APPROACH
      break
    end
  end
end
function Emerge()
  if activeCubby == nil or sonState == STATE_TRANSITION then
    return
  end
  if sonState == STATE_APPROACH and lastHoleVolume == activeCubby.volume then
    activeCubby.hole.LuaObjectScript.Disable()
    ClearActiveHole()
  elseif sonState == STATE_IDLE then
    for i = 1, #cubbyHoles do
      if lastHoleVolume == cubbyHoles[i].volume then
        local holeToDisable = activeCubby.hole
        activeCubby = cubbyHoles[i]
        sonState = STATE_TRANSITION
        local position = activeCubby.hole:GetWorldForward():Normalized() * 4
        position = position + activeCubby.hole:GetWorldPosition()
        son:Warp(position, activeCubby.hole:GetWorldForward() * -1)
        timers.StartLevelTimer(0.1, function()
          holeToDisable.LuaObjectScript.Disable()
        end)
        break
      end
    end
  end
end
function ClearActiveHole()
  activeCubby = nil
  sonState = STATE_NONE
  son:AbortInteract()
end
function LuaHook_EnterStart()
  sonState = STATE_TRANSITION
  activeCubby.hole.LuaObjectScript.FireEnterCallbacks()
end
function OutsideOfCubbyHoles()
  for i = 1, #cubbyHoles do
    if player:InsideZone(cubbyHoles[i].volume) then
      return false
    end
  end
  if player:InsideZone(volumeTransition) then
    return false
  end
  return true
end
function LuaHook_OnEnterIdle()
  if not activeCubby then
    return
  end
  sonState = STATE_IDLE
  if currentZone == nil or lastHoleVolume ~= activeCubby.volume and player:InsideZone(lastHoleVolume) or OutsideOfCubbyHoles() then
    Emerge()
  end
end
function LuaHook_ExitDone()
  if not activeCubby then
    return
  end
  activeCubby.hole.LuaObjectScript.FireExitCallbacks()
  if lastHoleVolume == activeCubby.volume and (player:InsideZone(activeCubby.volume) or currentZone == nil or OutsideOfCubbyHoles()) then
    ClearActiveHole()
  else
    activeCubby = nil
    currentZone = lastHoleVolume
    EnterHole()
    currentZone = volumeTransition
  end
end
