local class = require("core.class")
local thunk = require("core.thunk")
local bossState = require("boss.state")
local saveLib = require("core.save")
local BossStateMachine = class.Class("BossStateMachine")
local player
function BossStateMachine:init()
  player = game.Player.FindPlayer()
  self.currentState = "init"
  self.states = {}
  self.activeState = nil
  self.previousState = nil
  self.activeIndex = 1
  self.previousIndex = nil
  self.spawnerTable = {}
  self.bossTable = {}
  self:AddStage("Init")
  self.customTable = {}
  self.delaybuffer = 0
  self.debugInputBuffer = 0
  self.debugSkip = false
  self.bVFS_ShowDebug = engine.VFSBool.New("Enable Boss State Machine Debug Input")
  thunk.Install("OnUpdate", function(...)
    self:Update(...)
  end)
  thunk.Install("OnScriptLoaded", function(...)
    self:OnScriptLoaded(...)
  end)
  thunk.Install("OnStart", function(...)
    self:OnStart(...)
  end)
  thunk.Install("OnFirstStart", function(...)
    self:OnFirstStart(...)
  end)
end
function BossStateMachine:OnScriptLoaded(level)
  self.level = level
  saveLib.AddSaveObjectCallback(self.level, self, OnBossStateSaveCallback)
  saveLib.AddLoadObjectCallback(self.level, self, OnBossStateLoadCallback)
  saveLib.CreateSaveState(self.level, "__bossState")
end
function BossStateMachine:OnFirstStart(level)
  self:SpawnBoss()
  self.currentState = "firstInit"
end
function BossStateMachine:OnStart(level)
  if self.currentState == "init" then
    self:SpawnBoss()
  end
  self:GotoState(self.activeIndex)
end
function OnBossStateSaveCallback(level, bossState)
  local saveInfo = saveLib.GetSaveState(level, "__bossState")
  saveInfo.activeIndex = bossState.activeIndex
  saveInfo.previousIndex = bossState.previousIndex
end
function OnBossStateLoadCallback(level, bossState)
  local saveInfo = saveLib.GetSaveState(level, "__bossState")
  bossState.activeIndex = saveInfo.activeIndex
  bossState.previousIndex = saveInfo.previousIndex
end
function BossStateMachine:Update(level)
  if engine.IsDebug() then
    self:UpdateDebug()
  end
  if self.bVFS_ShowDebug.value then
    self:UpdateDebugInput()
  end
  if self.activeState == nil then
    engine.Error("no valid active state in boss statemachine!")
    return
  end
  if self.currentState == "advanceState" then
    self.delaybuffer = 1
    self.currentState = "onEnter"
  elseif self.currentState == "onEnter" then
    if self.delaybuffer > 0 then
      self.delaybuffer = self.delaybuffer - 1
      return
    end
    if self.activeState.Enter ~= nil then
      self.activeState:Enter(self.level, self.debugSkip)
    end
    if self.OnEnter ~= nil then
      self.OnEnter(self, level)
    end
    if self.debugSkip then
      self.debugSkip = false
    end
    self.currentState = "onUpdate"
  elseif self.currentState == "onUpdate" and self.activeState.Update ~= nil then
    self.activeState:Update(self.level)
  end
end
function BossStateMachine:AddCreature(bossSpawnerName)
  table.insert(self.spawnerTable, bossSpawnerName)
end
function BossStateMachine:AddStage(name, savepoint)
  local newState = bossState.New(name, savepoint)
  table.insert(self.states, newState)
  if self.activeState == nil then
    self.activeState = newState
  end
  return newState
end
function BossStateMachine:SpawnBoss(charConfig)
  for key, v in ipairs(self.spawnerTable) do
    local spawner = self.level:FindSingleGameObject(v)
    if spawner then
      local boss = spawner.LuaObjectScript.SpawnEnemy({
        spawnWad = self.level,
        bossConfigOverride = charConfig
      })
      if boss ~= nil then
        self.bossTable[key] = boss
      end
    else
      engine.Error("Cannot find boss spawner!")
    end
  end
end
function BossStateMachine:GotoState(stateIndex, hackInstant)
  if self.activeState.Exit ~= nil then
    self.activeState:Exit(self.level)
  end
  if self.states[stateIndex] ~= nil then
    self.previousState = self.activeState
    self.activeState = self.states[stateIndex]
  end
  if self.OnExit ~= nil and stateIndex ~= 1 then
    self.OnExit(self, self.level)
  end
  if hackInstant ~= nil and hackInstant then
    self.currentState = "onEnter"
  else
    self.currentState = "advanceState"
  end
end
function BossStateMachine:SetActiveState(stateName)
  for index, v in ipairs(self.states) do
    if v:GetStateName() == stateName then
      self.previousIndex = self.activeIndex
      self.activeIndex = index
      self:GotoState(index)
    end
  end
end
function BossStateMachine:BackwardState(hackInstant)
  self.previousIndex = self.activeIndex
  self.activeIndex = self.activeIndex - 1
  if self.activeIndex < 1 then
    self.activeIndex = 1
  end
  self:GotoState(self.activeIndex, hackInstant)
end
function BossStateMachine:ForwardState(hackInstant)
  self.previousIndex = self.activeIndex
  self.activeIndex = self.activeIndex + 1
  if self.activeIndex > #self.states then
    self.activeIndex = #self.states
  end
  self:GotoState(self.activeIndex, hackInstant)
end
function BossStateMachine:RestartState()
  self:GotoState(self.activeIndex)
end
function BossStateMachine:GetBoss()
  if #self.bossTable > 1 then
    return self.bossTable
  end
  return self.bossTable[1]
end
function BossStateMachine:AddBoss(pBoss)
  table.insert(self.bossTable, pBoss)
end
function BossStateMachine:GetActiveIndex()
  return self.activeIndex
end
function BossStateMachine:GetActiveStateName()
  return self.activeState:GetStateName()
end
function BossStateMachine:GetPreviousIndex()
  return self.previousIndex
end
function BossStateMachine:GetPreviousStateName()
  if self.previousState == nil then
    return nil
  end
  return self.previousState:GetStateName()
end
function BossStateMachine:GetIndexByStage(stageName)
  for index, v in ipairs(self.states) do
    if v:GetStateName() == stageName then
      return index
    end
  end
  return nil
end
function BossStateMachine:IsDebugSkip()
  return self.debugSkip
end
function BossStateMachine:SetDebugInput(enable)
  if enable then
    self.bVFS_ShowDebug.value = true
  else
    self.bVFS_ShowDebug.value = false
  end
end
function BossStateMachine:UpdateCustomTable(table)
  if type(table) == "table" then
    self.customTable = table
  end
end
function BossStateMachine:HideBoss(index)
  if index > #self.bossTable then
    engine.Error("invalid boss index")
  end
  if index == nil then
    for _, boss in ipairs(self.bossTable) do
      boss:Hide()
    end
  else
    local boss = self.bossTable[index]
    boss:Hide()
  end
end
function BossStateMachine:ShowBoss(index)
  if index > #self.bossTable then
    engine.Error("invalid boss index")
  end
  if index == nil then
    for _, boss in ipairs(self.bossTable) do
      boss:Show()
    end
  else
    local boss = self.bossTable[index]
    boss:Show()
  end
end
function BossStateMachine:UpdateDebug()
  local debugTable = {}
  debugTable[#debugTable + 1] = {
    "Active State: ",
    self.activeState.stateName
  }
  for _, v in ipairs(self.bossTable) do
    if v ~= nil and not v:IsDead() then
      debugTable[#debugTable + 1] = {
        "Boss: ",
        v,
        " Health: ",
        v:GetHitPoints()
      }
    end
  end
  for _, v in ipairs(self.customTable) do
    table.insert(debugTable, v)
  end
  debugTable.Title = "Boss: "
  debugTable.Y = 8
  engine.DrawDebugTable(debugTable)
  engine.DrawText2D("BOSS Forward Stage:L1+L2 + Right Dpad", 1240, 60, 65280, 50, 1)
  engine.DrawText2D("BOSS Backward Stage:L1+L2 + Left Dpad", 1240, 76, 65280, 50, 1)
  engine.DrawText2D("BOSS Restart Stage:L1+L2 +  Up Dpad", 1240, 92, 65280, 50, 1)
end
function BossStateMachine:UpdateDebugInput()
  if player.Pad == nil or engine.IsDebug() ~= true then
    return
  end
  if self.debugInputBuffer > 0 then
    self.debugInputBuffer = self.debugInputBuffer - self.level:GetUnitTime()
    return
  end
  if player.Pad.L1Down and player.Pad.L2Down then
    if player.Pad.LeftDown then
      if self.OnDebugBack ~= nil then
        self:OnDebugBack()
      end
      self.debugSkip = true
      self:BackwardState()
      self.debugInputBuffer = 0.5
    elseif player.Pad.RightDown then
      if self.OnDebugForward ~= nil then
        self:OnDebugForward()
      end
      self:ForwardState()
      self.debugSkip = true
      self.debugInputBuffer = 0.5
    elseif player.Pad.UpDown then
      if self.OnDebugRestart ~= nil then
        self:OnDebugRestart()
      end
      self:RestartState()
      self.debugSkip = true
      self.debugInputBuffer = 0.5
    elseif player.Pad.DownDown then
      self.debugInputBuffer = 0.5
    end
  end
end
local CoreBoss = BossStateMachine.New()
local DRAW_POS_TL_X = 100
local DRAW_POS_TL_Y = 100
local DrawBackground = function()
end
local DrawOutlines = function()
end
return CoreBoss
