local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local helper = require("son.helper")
local poilib = require("behavior.poi")
local LD = require("design.LevelDesignLibrary")
GameState = statemachine.StateMachine.New("MentalState")
local Idle = GameState:State("Idle")
local PreCombat = GameState:State("PreCombat")
local EnterCombat = GameState:State("EnterCombat")
local InCombat = GameState:State("InCombat")
local PostCombat = GameState:State("PostCombat")
statemachine.AddTags(Idle, "GameState_Idle")
statemachine.AddTags(PreCombat, "GameState_PreCombat")
statemachine.AddTags(EnterCombat, "GameState_EnterCombat")
statemachine.AddTags(InCombat, "GameState_InCombat")
statemachine.AddTags(PostCombat, "GameState_PostCombat")
function GameState:SelectNextState(ai, global, constants)
  if ai:IsInVehicle() then
    return Idle
  end
  if InCombat:IsAvailable(ai, global, constants) then
    return InCombat
  end
  if EnterCombat:IsAvailable(ai, global, constants) then
    return EnterCombat
  end
  if PreCombat:IsAvailable(ai, global, constants) then
    return PreCombat
  end
  if PostCombat:IsAvailable(ai, global, constants) then
    return PostCombat
  end
  return Idle
end
function Idle:OnBrainInit(ai, global, constants)
  global.currentState = "Idle"
  global.previousState = "Idle"
end
function Idle:Update(ai, global, constants)
  if global.overrides.suppressCombat then
    return
  end
  if global.bInCombat == true and ai:IsAvailableForCombat() then
    global.previousState = "Idle"
    global.currentState = "EnterCombat"
  elseif ai.OwnedPOI and ai.OwnedPOI:GetLuaTableAttribute("PostUp") and ai.OwnedPOI:GetStageName() ~= "Disengage" and ai.OwnedPOI:GetStageName() ~= "Exit" then
    global.previousState = "Idle"
    global.currentState = "PreCombat"
  end
end
function Idle:Enter(ai, global, constants)
  global.enteredNewState = true
  global.sTargetMode = "RegularTarget"
  game.Level.SetVariable("CBT_SonAggro", constants.maxAggro)
  ResetStates(ai, global, constants)
end
function Idle:Exit(ai, global, constants)
end
function PreCombat:OnBrainInit(ai, global, constants)
end
function PreCombat:IsAvailable(ai, global, constants)
  return global.currentState == "PreCombat"
end
function PreCombat:Update(ai, global, constants)
  local player = game.Player.FindPlayer()
  local enemiesList = DL.FindLivingEnemies(player, 50, true)
  local target
  local playerTarget = player:GetTargetCreature()
  for _, i in ipairs(enemiesList) do
    if playerTarget ~= i then
      local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, i.WorldPosition)
      if -40 <= desiredDirectionDelta and desiredDirectionDelta <= 40 then
        target = i
        break
      end
    end
  end
  if playerTarget then
    local desiredDirectionDelta = DL.FrontAngleFromPoint(ai, playerTarget.WorldPosition)
    if -40 <= desiredDirectionDelta and desiredDirectionDelta <= 40 then
      target = playerTarget
    end
    constants.postUpTarget = playerTarget
  else
    constants.postUpTarget = target
  end
  if constants.postUpTarget then
    ai:ClearForcedLookAtToObject()
    ai:ForceLookAtToObject(constants.postUpTarget)
  else
    ai:ClearForcedLookAtToObject()
  end
  if global.bInCombat == true then
    global.previousState = "PreCombat"
    global.currentState = "InCombat"
    if (constants.doingPostUpAutonomousShot or not target) and (not ai:IsPlayingMove("MOV_PostUpShot") or ai:IsPlayingMove("MOV_PostUpShot") and ai:GetActiveMovePercent() > 0.5) and not ai:IsPlayingMove("MOV_PostUpCombatExit") then
      ai:ClearForcedLookAtToObject()
      ai:TriggerMoveEvent("kLE_CombatExitPostUp")
      constants.doingPostUpAutonomousShot = false
    elseif ai:IsPlayingMove("MOV_CA_PostUp_Loop") and target then
      ai:ClearForcedLookAtToObject()
      ai:TriggerMoveEvent("kLEPostUpBowFire")
    end
  elseif constants.numEnemies == 0 and (not (ai.OwnedPOI ~= nil and ai.OwnedPOI) or not ai.OwnedPOI:GetLuaTableAttribute("PostUp")) then
    global.previousState = "PreCombat"
    global.currentState = "Idle"
  elseif ai.OwnedPOI ~= nil and ai.OwnedPOI and (ai.OwnedPOI:GetLuaTableAttribute("PostUp") and ai.OwnedPOI:GetStageName() == "Disengage" or ai.OwnedPOI:GetStageName() == "Exit") then
    global.previousState = "Idle"
    global.currentState = "Idle"
  end
end
function PreCombat:Enter(ai, global, constants)
  global.enteredNewState = true
  ResetStates(ai, global, constants)
end
function PreCombat:Exit(ai, global, constants)
end
function PostCombat:OnBrainInit(ai, global, constants)
  self.PostCombatDuration = 8
  self.CurrentPostCombatTimer = 8
  self.CachedBehaviorContextName = nil
end
function PostCombat:IsAvailable(ai, global, constants)
  return global.currentState == "PostCombat"
end
function PostCombat:Update(ai, global, constants)
  local player = game.Player.FindPlayer()
  if constants.extraPostCombatTime > 0 then
    self.CurrentPostCombatTimer = constants.extraPostCombatTime
    constants.extraPostCombatTime = 0
  end
  local angleBetween = LD.GetAngleBetweenVector(ai:GetWorldForward(), player:GetWorldPosition() - ai:GetWorldPosition())
  if 90 < angleBetween or angleBetween < -90 then
    helper.FocusKratos(ai, global, constants, 0.5)
  end
  self.CurrentPostCombatTimer = self.CurrentPostCombatTimer - ai:GetFrameTime()
  if 0 >= self.CurrentPostCombatTimer or not constants.postCombatIgnoreDistanceExit and player.WorldPosition:Distance(ai.WorldPosition) > 15 then
    global.previousState = "PostCombat"
    global.currentState = "Idle"
  end
end
function PostCombat:Enter(ai, global, constants)
  self.CurrentPostCombatTimer = self.PostCombatDuration
  global.enteredNewState = true
  global.sTargetMode = "RegularTarget"
  ResetStates(ai, global, constants)
  self.CachedBehaviorContextName = global.currentContextName
  ai:CallScript("ClearBehaviorContext")
  if constants.extraPostCombatTime > 0 then
    self.CurrentPostCombatTimer = constants.extraPostCombatTime
    constants.extraPostCombatTime = 0
  end
  ai:CallScript("SetOneOffTimerActivation", false)
end
function PostCombat:Exit(ai, global, constants)
  ai:CallScript("SetOneOffTimerActivation", true)
  if self.CachedBehaviorContextName ~= nil then
    ai:CallScript("EnterBehaviorContext", self.CachedBehaviorContextName)
  end
end
function EnterCombat:OnBrainInit(ai, global, constants)
  self.EnterCombatDuration = 5
  self.CurrentEnterCombatTimer = 5
end
function EnterCombat:IsAvailable(ai, global, constants)
  return global.currentState == "EnterCombat"
end
function EnterCombat:Update(ai, global, constants)
  self.CurrentEnterCombatTimer = self.CurrentEnterCombatTimer - ai:GetFrameTime()
  if global.bInCombat == true and ai:IsAvailableForCombat() then
    if self.CurrentEnterCombatTimer < 0 then
      global.previousState = "EnterCombat"
      global.currentState = "InCombat"
    end
  else
    global.previousState = "EnterCombat"
    global.currentState = "PostCombat"
  end
end
function EnterCombat:Enter(ai, global, constants)
  self.CurrentEnterCombatTimer = self.EnterCombatDuration
  global.enteredNewState = true
  global.sTargetMode = "none"
  local cineNumber = game.Level.GetVariable("CompletedCineNumber")
  if game.Level.GetVariable("TUT_ShowSonAggression") == false and (245 <= cineNumber and cineNumber < 539 or 570 < cineNumber) and game.Wallets.GetResourceValue("HERO", "Dummy_DisableAutonomous") >= 1 then
    game.Wallets.RemoveResource("HERO", "Dummy_DisableAutonomous", 1)
  end
  game.Player.FindPlayer():RemoveMarker("QuestGiverInteract")
  ResetStates(ai, global, constants)
end
function EnterCombat:Exit(ai, global, constants)
end
function InCombat:OnBrainInit(ai, global, constants)
end
function InCombat:IsAvailable(ai, global, constants)
  return global.currentState == "InCombat"
end
function InCombat:Update(ai, global, constants)
  if constants.doingPostUpAutonomousShot == false and ai:IsPlayingMove("MOV_PostUpShot") then
    constants.doingPostUpAutonomousShot = true
  end
  if constants.doingPostUpAutonomousShot and (not ai:IsPlayingMove("MOV_PostUpShot") or ai:GetActiveMovePercent() > 0.5) and not ai:IsPlayingMove("MOV_PostUpCombatExit") then
    ai:ClearForcedLookAtToObject()
    ai:TriggerMoveEvent("kLE_CombatExitPostUp")
    constants.doingPostUpAutonomousShot = false
  end
  if global.bInCombat == false and constants.numEnemies == 0 then
    global.previousState = "InCombat"
    global.currentState = "PostCombat"
  elseif global.bInCombat == false and constants.numEnemies >= 0 then
    global.previousState = "InCombat"
    global.currentState = "Idle"
  end
end
function InCombat:Enter(ai, global, constants)
  global.enteredNewState = true
end
function InCombat:Exit(ai, global, constants)
end
function ResetStates(ai, global, constants)
  global.bDodgeNow = false
  for k in pairs(constants.enemiesInReactionList) do
    constants.enemiesInReactionList[k] = nil
  end
end
return GameState
