local statemachine = require("ai.statemachine")
local DL = require("design.DesignerLibrary")
local unaware = require("awareness.unaware")
local sonAggro = require("behavior.aggroSystem")
local incombatlib = require("behavior.incombat")
local poilib = require("behavior.poi")
local positioning = require("behavior.positioning")
local baldur_poistates = require("baldur00.POIStates")
local locomotion = require("creature.locomotion")
local color = require("core.color")
local goPlayerCreature
local Brain = statemachine.StateMachine.New("Baldur00")
local NONHOSTILE = Brain:State("NonHostile")
local INCOMBAT = Brain:State("InCombat", incombatlib.InCombat)
local INAGGRO = Brain:Action("InAggro")
local LEASHING = Brain:State("Leashing")
local UNAWARE = Brain:State("Unaware", unaware.Unaware) .. {sixthSenseDistance = 0}
local SON_AGGRO = Brain:State("Son_Aggro", sonAggro.AggroSystem)
function Brain:OnBrainInit(ai, global, constants)
  goPlayerCreature = game.Player.FindPlayer()
  constants.fightKnowledgeInputs = positioning.CreateStandardPositioningInputs(constants.fightPosDataDefault)
  constants.fightKnowledge_SM_Fire = positioning.CreateStandardPositioningInputs(constants.fightPosData_SM_Fire)
  constants.fightKnowledge_SM_Ph2 = positioning.CreateStandardPositioningInputs(constants.fightPosData_SM_Ph2)
end
function SON_AGGRO:init()
  SON_AGGRO.aggroAttributes.radius = 6.5
  SON_AGGRO.aggroAttributes.dTree = "DTR_BALDUR00_OFFENSIVE"
  SON_AGGRO.forceAggroThreshold = 1000
  SON_AGGRO.aggroAttributes.tempDistanceToSonThreshold = 3
  SON_AGGRO.aggroAttributes.normalDtree = "DTR_BALDUR00_OFFENSIVE"
  SON_AGGRO.aggroAttributes.tempAttackDtree = "DTR_BALDUR00_OFFENSIVE"
  SON_AGGRO.aggroAttributes.classType = "dGrunts"
  SON_AGGRO.aggroAttributes.name = "Baldur"
  SON_AGGRO.sonAggroSystemEnabled = false
end
local UsePOI = poilib.NewPOIState(Brain, "UsePOI")
poilib.AllowPOIFromStates(INCOMBAT, UNAWARE, NONHOSTILE, INAGGRO)
baldur_poistates.SetupPOIStates(UsePOI)
function UsePOI:OnUpdateAwareness(ai, global, constants)
  local update_this
  if global.aggroState == "UNAWARE" then
    update_this = UNAWARE
  elseif global.aggroState == "NONHOSTILE" then
    update_this = NONHOSTILE
  end
  if update_this then
    update_this:Update(ai, global, constants)
  end
end
function LuaHook_BackAttackOpportunity(ai, data)
  if not _G.global.target then
    return
  end
  local relativePos = DL.GetObjectARelativePositionToObjectB(_G.global.target, ai)
  if relativePos == "objBack" then
    ai:TriggerMoveEvent("kLEBackAttackOpportunity")
  else
    return
  end
end
local recentStop = false
local JSwarpRot, JSwarpLoc
local isInZone = false
function NONHOSTILE:Enter(ai, global, constants)
  local actuatorData = {
    Destination = ai:GetWorldPosition()
  }
  locomotion.SetActuator(ai, actuatorData)
  DL.DebugPrint(ai, "NONHOSTILE selected")
end
function NONHOSTILE:Update(ai, global, constants)
  local actuatorData = {
    Destination = ai:GetWorldPosition()
  }
  locomotion.SetActuator(ai, actuatorData)
  if ai:IsPlayingMove("MOV_Phase0_BaldurIntroductionLaunch") then
    global.aggroState = "INCOMBAT"
  end
  if isInZone == false then
    if ai:IsInsideEntityZone("Zone_Impale") and DL.CheckCreatureContext(ai:GetContext(), "REACT_WALL") == false then
      local warpRot = ai:GetAnimDriver("RotationDriver")
      local warpLoc = ai:GetAnimDriver("TranslationDriver")
      warpRot.ValueVec = DL.GetJointRotation(ai.GroundLevel, "Zone_Impale", "Warp_Location")
      warpLoc.ValueVec = DL.GetJointPosition(ai.GroundLevel, "Zone_Impale", "Warp_Location")
      ai:TriggerMoveEvent("LE_BaldurImpaled")
    end
    if ai:IsInsideEntityZone("Zone_WallImpact") and DL.CheckCreatureContext(ai:GetContext(), "REACT_WALL") == false then
      local warpRot = ai:GetAnimDriver("RotationDriver")
      local warpLoc = ai:GetAnimDriver("TranslationDriver")
      warpRot.ValueVec = DL.GetJointRotation(ai.GroundLevel, "Zone_WallImpact", "Warp_Location")
      warpLoc.ValueVec = DL.GetJointPosition(ai.GroundLevel, "Zone_WallImpact", "Warp_Location")
      ai:TriggerMoveEvent("LE_BaldurWallImpact")
    end
  end
  if ai:IsInsideEntityZone("Zone_Impale") or ai:IsInsideEntityZone("Zone_WallImpact") then
    isInZone = true
  else
    isInZone = false
  end
end
function UNAWARE:Update(ai, global, constants)
  unaware.Unaware.Update(self, ai, global, constants)
end
local mmaDamage = 0
function LuaHook_BaldurMMABegin(ai, data)
  ai:RemoveMarker("MMA_Done")
  mmaDamage = 0
end
function LuaHook_BaldurBlockRecoil(ai, data)
  _G.global.quickRecoverCooldown = 2
end
function LuaHook_BaldurComboBreakerAttack(ai, data)
  DL.ComboBreakerDecay(ai, 9999)
end
function LuaHook_ReactionBreakerReset(ai, data)
  DL.ComboBreakerDecayAbsolute(ai, 30)
  if ai:HasMarker("ReactionBreakerReady") then
    ai:RemoveMarker("ReactionBreakerReady")
  end
end
function LuaHook_ReactionBreakerReduce(ai, data)
  DL.ComboBreakerDecay(ai, 30)
  if ai:HasMarker("ReactionBreakerReady") then
    ai:RemoveMarker("ReactionBreakerReady")
  end
end
local vkHealth_SpawnMax = 8
local vkHealth_SpawnCooldown = 30
local vkHealth_SpawnThresholdHigh = 0.5
local vkHealth_SpawnThresholdLow = 0.25
local vkHealth_SpawnDamageReq = 0.1
local valkHealthSpawnTimer = 0
local valkHealthSpawnCount = 0
local valkHealthPercentReqToSpawn = 1
function LuaHook_CheckToSpawnHealth(ai)
  if valkHealthSpawnTimer < game.GetGameTime() and valkHealthSpawnCount < vkHealth_SpawnMax and ai ~= nil and ai.GroundLevel ~= nil then
    local player = game.Player.FindPlayer()
    local healthPerc = player:MeterGetValue("Health") / player:MeterGetMax("Health")
    local valkPerc = ai:MeterGetValue("Health") / ai:MeterGetMax("Health")
    local spawnAllowed = false
    if valkPerc < valkHealthPercentReqToSpawn and (healthPerc < vkHealth_SpawnThresholdLow or healthPerc < vkHealth_SpawnThresholdHigh and math.random(0, 100) <= 10) then
      spawnAllowed = true
    end
    if spawnAllowed then
      valkHealthSpawnTimer = game.GetGameTime() + vkHealth_SpawnCooldown
      valkHealthSpawnCount = valkHealthSpawnCount + 1
      valkHealthPercentReqToSpawn = valkPerc - vkHealth_SpawnDamageReq
      return true
    else
      return false
    end
  end
end
function INCOMBAT.Events:OnBroadcastReaction(event, ai, global, constants)
  if DL.CheckCreatureContext(event.broadcastContext, "PROJECTION_FREYA_GRAB") then
    ai:TriggerMoveEvent("LE_EvadeCollsionCheck")
  end
end
function INCOMBAT.Events:OnHitReaction(event, ai, global, constants)
  if ai:IsAxeEmbedded() then
    game.Player.FindPlayer():DropThrowable("ATT_Axe")
  end
  local mmaDamageThreshold = 12
  local comboBreakerDamageThreshold = 90
  if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
    comboBreakerDamageThreshold = 65
  end
  if ai:PickupIsAcquired("Baldur_Phase") and ai:PickupGetStage("Baldur_Phase") == 3 then
    comboBreakerDamageThreshold = 130
  end
  if goPlayerCreature:PickupIsAcquired("RageMode") and goPlayerCreature:PickupGetStage("RageMode") == 1 then
    if goPlayerCreature:IsPlayingMove("MOV_RageFlurryCombo01_Finisher") then
      DL.ComboBreakerCheck(ai, 999, comboBreakerDamageThreshold, 1, nil)
      if ai:HasMarker("ReactionBreakerReady") == false then
        ai:AddMarker("ReactionBreakerReady")
      end
    else
      return
    end
  end
  if global.aggroState == "INCOMBAT" and event.enemyId == DL.HashCreatureID(ai, "HERO") then
    local eventDamage = event.damage
    if eventDamage < 8 then
      eventDamage = 8
      if ai:HasHitFlag("HIT_AXE", event.hitFlags) and ai:PickupIsAcquired("Baldur_IceMode") then
        eventDamage = 0
      elseif ai:HasHitFlag("HIT_BLADES", event.hitFlags) and ai:PickupIsAcquired("Baldur_FireMode") then
        eventDamage = 0
      end
    end
    if eventDamage ~= 0 and goPlayerCreature ~= nil and goPlayerCreature:IsPlayingMove("MOV_ChaosElementalSlashExit") and eventDamage < 35 then
      eventDamage = 35
    end
    local breakThisFrame = DL.ComboBreakerCheck(ai, eventDamage, comboBreakerDamageThreshold, 1, nil)
    if breakThisFrame == false and ai:PickupIsAcquired("Baldur_StoneMasonBoss") and ai:HasMarker("ReactionBreakerReady") == true then
      ai:RemoveMarker("ReactionBreakerReady")
    end
    if breakThisFrame and ai:HasMarker("AllowComboBreak") and ai:HasMarker("MMA") == false and ai:IsPlayingMove("MOV_StunnedPhaseTransitionEnter") == false and game.Level.GetVariable("DEBUG_CBT_Disable_Defense") == false then
      if ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
        if ai:HasHitFlag("HIT_MELEE", event.hitFlags) or goPlayerCreature ~= nil and goPlayerCreature:IsPlayingMove("MOV_ChaosElementalSlashExit") then
          if ai:HasMarker("ReactionBreakerReady") == false then
            ai:AddMarker("ReactionBreakerReady")
          end
        elseif ai:HasMarker("ReactionBreakerReady") == true then
          ai:RemoveMarker("ReactionBreakerReady")
        end
      end
      if not ai:PickupIsAcquired("Baldur_StoneMasonBoss") then
        if ai:HasHitFlag("HIT_AXE", event.hitFlags) and ai:HasHitFlag("HIT_MELEE", event.hitFlags) and ai:HasHitFlag("HIT_THROWABLE", event.hitFlags) == false then
          if ai:HasHitFlag("HIT_DIRECTION_RIGHT", event.hitFlags) then
            DL.ComboBreakerCheck(ai, 1, comboBreakerDamageThreshold, 0.5, "kLEAxeStuckCounter01")
          else
            DL.ComboBreakerCheck(ai, 1, comboBreakerDamageThreshold, 0.5, "kLEAxeStuckCounter")
          end
        elseif ai:HasHitFlag("HIT_BLADES", event.hitFlags) and ai:HasHitFlag("HIT_MELEE", event.hitFlags) then
          DL.ComboBreakerCheck(ai, 1, comboBreakerDamageThreshold, 0.5, "kLEBareStuckCounter")
        else
          DL.ComboBreakerCheck(ai, 1, comboBreakerDamageThreshold, 0.5, "kLEBareStuckCounter")
        end
      end
    end
    if (ai:PickupIsAcquired("Baldur_IceMode") and ai:HasHitFlag("HIT_AXE", event.hitFlags) or ai:PickupIsAcquired("Baldur_FireMode") and ai:HasHitFlag("HIT_BLADES", event.hitFlags)) and ai:HasHitFlag("HIT_MELEE", event.hitFlags) then
      local concussionParams = {}
      concussionParams = {
        Tweak = "CNC_AXE_REJECT",
        WorldLocation = ai:GetWorldPosition(),
        GameObject = ai,
        EnemyId = ai:GetID()
      }
      game.Combat.PlayConcussion(concussionParams)
    end
    if ai:PickupIsAcquired("Baldur_Invulnerable") then
      local currentHealthValue = global.healthValue_SM_Phase0
      local newHealthValue = currentHealthValue - eventDamage
      global.healthValue_SM_Phase0 = newHealthValue
      if 0 >= global.healthValue_SM_Phase0 and ai:HasMarker("SM_Phase0_Complete") == false then
        ai:AddMarker("SM_Phase0_Complete")
      end
    end
  end
  if global.mortarsActive == true then
    global.mortarsActive = false
  end
end
function INCOMBAT:Enter(ai, global, constants)
  DL.DebugPrint(ai, "INCOMBAT selected")
end
function MMA_Logic(ai)
  if ai:IsPlayingMove("MOV_MMA_Defense_Idle") then
    local rand = math.random(0, 100)
    if rand <= 30 then
      if ai:HasMarker("MMA_NoFlurry") == false then
        ai:TriggerMoveEvent("LE_Atk_Flurry")
        Baldur2Fight_BaldurPunch()
      end
    elseif rand <= 60 then
      if ai:HasMarker("MMA_NoFlurry") == false then
        ai:TriggerMoveEvent("LE_Atk_Flurry2")
        Baldur2Fight_BaldurPunch()
      end
    elseif rand <= 80 then
      ai:TriggerMoveEvent("LE_Atk_HeavyLeft")
      Baldur2Fight_BaldurPunch()
    else
      ai:TriggerMoveEvent("LE_Atk_HeavyRight")
      Baldur2Fight_BaldurPunch()
    end
  end
  if ai:IsPlayingMove("MOV_ChiselMMA_Defense_Idle") then
    local rand = math.random(0, 100)
    if rand <= 30 then
      if ai:HasMarker("MMA_NoFlurry") == false then
        ai:TriggerMoveEvent("LE_Atk_Flurry")
      end
    elseif rand <= 60 then
      if ai:HasMarker("MMA_NoFlurry") == false then
        ai:TriggerMoveEvent("LE_Atk_Flurry")
      end
    elseif rand <= 80 then
      ai:TriggerMoveEvent("LE_Atk_Flurry")
    else
      ai:TriggerMoveEvent("LE_Atk_Flurry")
    end
  end
end
function Baldur2Fight_BaldurPunch()
  local cineNum = game.Level.GetVariable("CompletedCineNumber")
  local peak800 = game.FindLevel("Peak800_DragonRide")
  if cineNum == 470 and peak800 ~= nil then
    peak800:GetGameObject("Peak800_Banter"):CallScript("BaldurPunch_Banters")
  end
end
function INCOMBAT:OnUpdateCombat(ai, global, constants, dTreeParams)
  DL.ComboBreakerDecay(ai, 10)
  if global.quickRecoverCooldown > 0 then
    global.quickRecoverCooldown = global.quickRecoverCooldown - ai:GetFrameTime() * 1
  end
  MMA_Logic(ai)
  if isInZone == false then
    if ai:IsInsideEntityZone("Zone_Impale") and DL.CheckCreatureContext(ai:GetContext(), "REACT_WALL") == false then
      local warpRot = ai:GetAnimDriver("RotationDriver")
      local warpLoc = ai:GetAnimDriver("TranslationDriver")
      warpRot.ValueVec = DL.GetJointRotation(ai.GroundLevel, "Zone_Impale", "Warp_Location")
      warpLoc.ValueVec = DL.GetJointPosition(ai.GroundLevel, "Zone_Impale", "Warp_Location")
      ai:TriggerMoveEvent("LE_BaldurImpaled")
    end
    if ai:IsInsideEntityZone("Zone_WallImpact") and DL.CheckCreatureContext(ai:GetContext(), "REACT_WALL") == false then
      local warpRot = ai:GetAnimDriver("RotationDriver")
      local warpLoc = ai:GetAnimDriver("TranslationDriver")
      warpRot.ValueVec = DL.GetJointRotation(ai.GroundLevel, "Zone_WallImpact", "Warp_Location")
      warpLoc.ValueVec = DL.GetJointPosition(ai.GroundLevel, "Zone_WallImpact", "Warp_Location")
      ai:TriggerMoveEvent("LE_BaldurWallImpact")
    end
  end
  if ai:IsInsideEntityZone("Zone_Impale") or ai:IsInsideEntityZone("Zone_WallImpact") then
    isInZone = true
  else
    isInZone = false
  end
end
function INCOMBAT:OnUpdateMotion(ai, global, constants, actuatorData)
  actuatorData.Speed = global.navData.navSpeedWalk
  local currentDistFromTarget = game.AIUtil.Distance(global.target:GetWorldPosition(), ai:GetWorldPosition())
  global.distToPlayer = currentDistFromTarget
end
function DrawBaldurDebug(ai)
  engine.DrawText2D([[



]] .. DL.ComboBreakerGetDebugString(), 200, 100, color.yellow)
  engine.DrawText2D([[






MMA Dmg: ]] .. mmaDamage, 200, 100, color.yellow)
end
function INAGGRO:Enter(seq, ai, global, constants)
  DL.DebugPrint(ai, "INAGGRO selected")
  local delay = math.random()
  DL.DebugPrint(ai, "Starting timer for Aggro Event with value: " .. delay)
  seq:At(delay, function()
    DL.DebugPrint(ai, "Triggering Aggro Event")
    DL.SendAggroEvent(goPlayerCreature, ai)
  end)
  seq:At(delay + 0.1, function()
    global.aggroState = "INCOMBAT"
  end)
end
function LEASHING:Enter(ai, global, constants)
end
function LEASHING:Update(ai, global, constants)
end
function Brain:SelectNextState(ai, global, constants)
  local ActiveTags = statemachine.ActiveTags()
  if poilib.IsPOIActive() then
    return UsePOI
  elseif global.aggroState == "INAGGRO" then
    return INAGGRO
  elseif global.aggroState == "INCOMBAT" then
    global.target = ai:FindTarget(global.targetParams)
    if global.target then
      if (ActiveTags.LeashFallback or ActiveTags.LeashRetreat) and global.useLeashing then
        return LEASHING
      else
        return INCOMBAT
      end
    end
    DL.DebugPrint(ai, "NONHOSTILE selected because no target found")
    return NONHOSTILE
  elseif global.aggroState == "UNAWARE" then
    return UNAWARE
  else
    return NONHOSTILE
  end
end
return Brain
