local LD = require("design.LevelDesignLibrary")
local thisObj, thisLevel, player, elevatorObj, sandbowlObj
local onEnterCallbacks = {}
local onIdleCallbacks = {}
local onExitCallbacks = {}
bDebugEnabled = true
DEBUG_GoToNextElevatorState = nil
DebugStateTable = {}
local elevatorAnims = {
  CollapseEnter = "envElevator_BowlCollapseEnter",
  CollapseIdle = "envElevator_BowlCollapseIdle",
  Idle = "envElevator_Idle",
  Shake = "envElevator_State0Shake",
  Reform = "envElevator_Reform",
  State1Enter = "envElevator_State1Enter",
  State1Idle = "envElevator_State1Idle",
  State1Reform = "envElevator_State1Reform",
  State2Enter = "envElevator_State2Enter",
  State2Idle = "envElevator_State2Idle",
  State2Reform = "envElevator_State2Reform",
  State3Enter = "envElevator_State3Enter",
  State3Idle = "envElevator_State3Idle",
  State3Reform = "envElevator_State3Reform",
  State3Collapse = "envElevator_State3Collapse"
}
local elevatorStates = {}
local currentElevatorState, nextElevatorState
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  elevatorObj = thisObj:FindSingleGOByName("elevatorObj").Child
  sandbowlObj = elevatorObj:FindSingleGOByName("soninteract_sandbowl_continuous")
  DEBUG_GoToNextElevatorState = engine.VFSBool.New("DEBUG: GoToNextElevatorState")
  elevatorStates = {
    CollapseStart = {
      Exit = elevatorAnims.Reform
    },
    BowlCollapse = {
      Enter = elevatorAnims.CollapseEnter,
      Idle = elevatorAnims.State3Idle
    },
    Idle = {
      Idle = elevatorAnims.Idle
    },
    Shake = {
      Enter = elevatorAnims.Shake,
      Idle = elevatorAnims.Idle
    },
    State1 = {
      Enter = elevatorAnims.State1Enter,
      Idle = elevatorAnims.State1Idle
    },
    State2 = {
      Enter = elevatorAnims.State2Enter,
      Idle = elevatorAnims.State2Idle
    },
    State3 = {
      Enter = elevatorAnims.State3Enter,
      Idle = elevatorAnims.State3Idle
    },
    State1Reform = {
      Enter = elevatorAnims.State1Reform
    },
    State2Reform = {
      Enter = elevatorAnims.State2Reform
    },
    State3Reform = {
      Enter = elevatorAnims.State3Reform
    },
    Collapse = {
      Enter = elevatorAnims.State3Collapse
    }
  }
end
function OnFirstStart(level, obj)
  ResetElevator()
end
function OnStart(level, obj)
  DebugStateTable = {
    "Idle",
    "State1",
    "Idle",
    "State1",
    "State2",
    "Idle",
    "State1",
    "State2",
    "State3",
    "Idle",
    "State1",
    "State2",
    "State3",
    "Collapse",
    "Idle",
    "BowlCollapse",
    "State1",
    "State2",
    "State3",
    "Collapse"
  }
  if not currentElevatorState then
    currentElevatorState = "CollapseStart"
  end
  SetElevatorState(currentElevatorState)
end
function OnSaveCheckpoint(level, obj)
  return {currentElevatorState = currentElevatorState}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  currentElevatorState = savedInfo.currentElevatorState
end
function ResetElevator()
  currentElevatorState = "CollapseStart"
  elevatorObj:StartAnim(elevatorStates[currentElevatorState].Exit)
  elevatorObj:JumpAnimToFrame(0)
  elevatorObj:PauseAnim()
  elevatorObj:ShowJoint(elevatorObj:GetJointIndex("JOPiece071"))
  GameObjects.DMC_Wall:JumpAnimToFrame(0)
  GameObjects.DMC_Col:JumpAnimToFrame(0)
  GameObjects.DMC_Col:HideCollision()
  GameObjects.DMC_Wall:Hide()
  elevatorObj:FindSingleGOByName("IC"):Hide()
  sandbowlObj.LuaObjectScript:ShowSandBowl()
end
function SetElevatorState(state)
  if state then
    if state == "CollapseStart" then
      ResetElevator()
      return
    end
    if state == "Collapse" then
      elevatorObj:FindSingleGOByName("IC"):Hide()
    end
    if state == "Idle" and currentElevatorState ~= nil and string.match(currentElevatorState, "State") then
      state = currentElevatorState .. "Reform"
    end
    nextElevatorState = state
    if currentElevatorState and elevatorStates[currentElevatorState] and elevatorStates[currentElevatorState].Exit then
      ExitState()
    elseif nextElevatorState and elevatorStates[nextElevatorState] then
      if elevatorStates[nextElevatorState].Enter then
        EnterState()
      elseif elevatorStates[nextElevatorState].Idle then
        EnterIdle()
      end
    end
  end
end
function ExitState()
  elevatorObj:ClearAllAnimCallbacks()
  elevatorObj:StartAnim(elevatorStates[currentElevatorState].Exit)
  elevatorObj:OnAnimDone(thisObj, "ExitStateComplete")
  ExecuteCallbackOnExit()
  if currentElevatorState == "CollapseStart" then
    elevatorObj:FindSingleGOByName("IC"):Show()
  end
end
function ExitStateComplete()
  if elevatorStates[nextElevatorState] then
    if elevatorStates[nextElevatorState].Enter then
      EnterState()
    elseif elevatorStates[nextElevatorState].Idle then
      EnterIdle()
    end
  end
end
function EnterState()
  currentElevatorState = nextElevatorState
  elevatorObj:ClearAllAnimCallbacks()
  elevatorObj:StartAnim(elevatorStates[currentElevatorState].Enter)
  elevatorObj:OnAnimDone(thisObj, "EnterStateComplete")
  ExecuteCallbackOnEnter()
end
function EnterStateComplete()
  if elevatorStates[currentElevatorState].Idle then
    EnterIdle()
  elseif string.match(currentElevatorState, "Reform") then
    nextElevatorState = "Idle"
    EnterIdle()
  end
end
function EnterIdle()
  if currentElevatorState ~= nextElevatorState then
    currentElevatorState = nextElevatorState
  end
  if currentElevatorState == "BowlCollapse" then
    elevatorObj:HideJoint(elevatorObj:GetJointIndex("JOPiece071"))
    sandbowlObj.LuaObjectScript:HideSandBowl()
  end
  ExecuteCallbackOnIdle()
  elevatorObj:PlayAnimationCycle({
    Animation = elevatorStates[currentElevatorState].Idle
  })
end
function GetElevatorState()
  return currentElevatorState
end
local RegisterCallbackInTable = function(fn)
  return LD.ExtractCallbacksForEvent(thisLevel, thisObj, fn)
end
function RegisterCallbackOnEnter(fn)
  onEnterCallbacks = RegisterCallbackInTable(fn)
end
function RegisterCallbackOnIdle(fn)
  onIdleCallbacks = RegisterCallbackInTable(fn)
end
function RegisterCallbackOnExit(fn)
  onExitCallbacks = RegisterCallbackInTable(fn)
end
function ExecuteCallbackOnEnter()
  if onEnterCallbacks then
    print("Executing Callbacks for ", onEnterCallbacks)
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onEnterCallbacks)
    onEnterCallbacks = nil
  end
end
function ExecuteCallbackOnIdle()
  if onIdleCallbacks then
    print("Executing Callbacks for ", onIdleCallbacks)
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onIdleCallbacks)
    onIdleCallbacks = nil
  end
end
function ExecuteCallbackOnExit()
  if onExitCallbacks then
    print("Executing Callbacks for ", onExitCallbacks)
    LD.ExecuteCallbacksForEvent(thisLevel, thisObj, onExitCallbacks)
    onExitCallbacks = nil
  end
end
local debugIndex = 1
function GoToNextState()
  if debugIndex > #DebugStateTable then
    ResetElevator()
    debugIndex = 1
    return
  end
  SetElevatorState(DebugStateTable[debugIndex])
  if debugIndex <= #DebugStateTable then
    debugIndex = debugIndex + 1
  end
end
function OnUpdate(level, obj)
  if DEBUG_GoToNextElevatorState.value then
    DEBUG_GoToNextElevatorState.value = false
    GoToNextState()
  end
  ShowDebugTable()
end
function DebugEnable()
  bDebugEnabled = true
end
function DebugDisable()
  bDebugEnabled = false
end
function ShowDebugTable(distance, x, y)
  distance = distance or 20
  if engine.IsDebug() and bDebugEnabled and distance > (player.WorldPosition - thisObj.WorldPosition):Length() then
    local debugTable = {}
    debugTable.Y = y or 5
    debugTable.X = x or 50
    debugTable.Title = thisObj.Parent:GetName()
    debugTable.TitleColor = engine.Vector.New(255, 0, 128)
    table.insert(debugTable, {
      "currentElevatorState: ",
      currentElevatorState
    })
    if currentElevatorState then
      for k, v in pairs(elevatorStates[currentElevatorState]) do
        table.insert(debugTable, {
          k .. ": ",
          v
        })
      end
      if elevatorStates[nextElevatorState] and currentElevatorState ~= nextElevatorState then
        table.insert(debugTable, {
          "nextElevatorState: ",
          nextElevatorState
        })
        for k, v in pairs(elevatorStates[nextElevatorState]) do
          table.insert(debugTable, {
            k .. ": ",
            v
          })
        end
      end
    end
    engine.DrawDebugTable(debugTable)
  end
end
