local statemachine = require("ai.statemachine")
local tablex = require("core.tablex")
local picklelib = require("core.pickle")
local DL = require("design.DesignerLibrary")
local timer = require("creature.timer")
print("Flyer00 main start")
Brain_Core = require("behavior.corebrain")
constants = {}
global = {}
local powerLevel = "1"
local pLevelInt = 1
local flyerType
local followupCount = 0
local flyerIntro = false
local followupSeidrShotCountLimit = 1
local followupDiveCountLimit = 1
local followupIceShotCountLimit = 2
local followupBlindShotCountLimit = 1
function OnAIPostSpawn(ai)
  global.navData.navSpeedWalk = ai:LookupFloatConstant("NAV_SPEED_FLIGHT")
  global.navData.navSpeedJog = ai:LookupFloatConstant("NAV_SPEED_FLIGHT")
  SetupCharacter(ai)
end
function LuaHook_SetSpawnParamsFromWitch(ai, spawnedPowerLevel, spawnedFlyerType)
  powerLevel = spawnedPowerLevel
  flyerType = spawnedFlyerType
  SetFlyerConfigAndPowerLevel(ai, true)
end
function Brain_Core:OnSetPowerlevelOverrideCustom(ai, global, constants, level, isElite, forced)
  powerLevel = level
  SetFlyerConfigAndPowerLevel(ai, forced)
end
function SetupCharacter(ai)
  local aggroState = ""
  local spawnContext = ai:GetContext()
  if not ai:PickupIsAcquired("Flyer_VariantType") then
    ai:PickupAcquire("Flyer_VariantType")
  end
  if StartConfig ~= nil then
    if StartConfig.AggroState ~= nil then
      DL.switch(StartConfig.AggroState):caseof({
        Unaware = function()
          aggroState = "UNAWARE"
        end,
        InCombat = function()
          aggroState = "INCOMBAT"
        end
      })
    end
    if StartConfig.FlyerType ~= nil then
      flyerType = StartConfig.FlyerType
    end
    SetFlyerConfigAndPowerLevel(ai, not game.CHECK_FEATURE("PREEMPT_SPAWN_CONFIG"))
    if StartConfig.AllowImmediatePossess then
      timer.StartCreatureTimer(0.1, function()
        if ai:PickupIsAcquired("FlyerDelayAbilityUse") then
          ai:PickupRelinquish("FlyerDelayAbilityUse")
        end
      end)
    end
  end
  local levelName = ai.Level.Name
  if string.find(levelName, "WAD_Peak720_SummitAscentHUB") ~= nil then
    flyerIntro = true
  end
  if string.find(levelName, "WAD_HelR300_AI_ShipFight") ~= nil and not ai:PickupIsAcquired("Flyer_DisableSonGrab") then
    ai:PickupAcquire("Flyer_DisableSonGrab")
  end
  if aggroState == "" then
    if DL.CheckCreatureContext(spawnContext, "INCOMBAT") then
      aggroState = "INCOMBAT"
    elseif DL.CheckCreatureContext(spawnContext, "UNAWARE") then
      aggroState = "UNAWARE"
    elseif DL.CheckCreatureContext(spawnContext, "NONHOSTILE") then
      aggroState = "NONHOSTILE"
    elseif DL.CheckCreatureContext(spawnContext, "PASSIVE") then
      aggroState = "PASSIVE"
    end
  end
  global.aggroState = aggroState
  DL.NewGamePlusLevelBoost(ai, "Flyer00_PowerLevel")
  DL.NewGamePlusLevelBoost(ai, "Flyer10_PowerLevel")
end
function SetFlyerConfigAndPowerLevel(ai, override)
  ai:PickupAcquire("Flyer_VariantType")
  local variantPickupAcquired = ai:PickupIsAcquired("Flyer_VariantType")
  if powerLevel == "1" then
    pLevelInt = 1
  elseif powerLevel == "2" then
    pLevelInt = 2
  elseif powerLevel == "3" then
    pLevelInt = 3
  elseif powerLevel == "4" then
    pLevelInt = 4
  elseif powerLevel == "5" then
    pLevelInt = 5
  elseif powerLevel == "6" then
    pLevelInt = 6
  elseif powerLevel == "7" then
    pLevelInt = 7
  elseif powerLevel == "8" then
    pLevelInt = 8
  else
    pLevelInt = powerLevel
  end
  if not ai:PickupIsAcquired("Flyer00_PowerLevel") and not ai:PickupIsAcquired("Flyer10_PowerLevel") then
    local variantID = ai:GetName()
    if variantID == "flyer10" then
      ai:PickupAcquire("Flyer10_PowerLevel")
      ai:PickupSetStage("Flyer10_PowerLevel", pLevelInt - 1)
    else
      ai:PickupAcquire("Flyer00_PowerLevel")
      ai:PickupSetStage("Flyer00_PowerLevel", pLevelInt - 1)
    end
  end
  if variantPickupAcquired then
    if flyerType == "Seidr" then
      ai:PickupSetStage("Flyer_VariantType", 0)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_seidr_hard")
        else
          ai:SetCharacterConfig("Flyer_seidr_easy")
        end
      end
    elseif flyerType == "Fire" then
      ai:PickupSetStage("Flyer_VariantType", 1)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_fire_hard")
        else
          ai:SetCharacterConfig("Flyer_fire_easy")
        end
      end
    elseif flyerType == "Suicide" then
      ai:PickupSetStage("Flyer_VariantType", 2)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_suicide_hard")
        else
          ai:SetCharacterConfig("Flyer_suicide_easy")
        end
      end
    elseif flyerType == "Ice" then
      ai:PickupSetStage("Flyer_VariantType", 3)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_ice_hard")
        else
          ai:SetCharacterConfig("Flyer_ice_easy")
        end
      end
    elseif flyerType == "Blind" then
      ai:PickupSetStage("Flyer_VariantType", 4)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_blind_hard")
        else
          ai:SetCharacterConfig("Flyer_blind_easy")
        end
      end
    elseif flyerType == "Possess" then
      ai:PickupSetStage("Flyer_VariantType", 5)
      if override ~= nil and override == true then
        if 4 < pLevelInt then
          ai:SetCharacterConfig("Flyer_posses_hard")
        else
          ai:SetCharacterConfig("Flyer_posses_easy")
        end
      end
    else
      ai:PickupSetStage("Flyer_VariantType", 0)
      if override ~= nil and override == true then
        ai:SetCharacterConfig("Flyer_seidr_easy")
      end
      print("!!!! FLYER !!!! -- something wasnt set correctly on the spawner, so proper config and dTree are not set")
    end
  end
end
function OnAIUpdate(ai)
end
function _G.OnAICreateLuaState(ai)
  _G.OnAICreateLuaState = nil
end
function Brain_Core:OnUpdateMotion(ai, global, constants, actuatorData, avoidanceArgs)
  if _G.global.target ~= nil then
    local targetWorldPos = _G.global.target:GetWorldPosition()
    local flyerPosition = ai:GetWorldPosition()
    local distToTarget = targetWorldPos:Distance(flyerPosition)
    if distToTarget < 10 then
      actuatorData.Facing = (targetWorldPos - flyerPosition):Normalized()
      if not _G.global.navData.isStrafing then
        _G.global.navData.isStrafing = true
        _G.global.navBank = constants.DefaultNavBank
        ai:SetNavBank(_G.global.navBank)
      end
    elseif 13.5 < distToTarget then
      actuatorData.Facing = nil
      if _G.global.navData.isStrafing then
        _G.global.navData.isStrafing = false
        _G.global.navBank = constants.NonStrafeNavBank
        ai:SetNavBank(_G.global.navBank)
      end
    end
  else
    actuatorData.Facing = nil
    if _G.global.navData.isStrafing then
      _G.global.navData.isStrafing = false
      _G.global.navBank = constants.NonStrafeNavBank
      ai:setNavBank(_G.global.navBank)
    end
  end
end
function Brain_Core:OnDeath(ai, attacker, deathByDespawn)
  if ai:PickupIsAcquired("FlyerVaporForm") then
    ai:PickupRelinquish("FlyerVaporForm")
  end
end
function OnWeaponApproaching(gameObject, attacker, weapon, side)
  local ai = gameObject:GetCreature():GetAI()
  if weapon:GetName() == "explosive00" and ai:IsInNavigationMove() then
    local attackerTarget = attacker:GetTargetCreature()
    if attackerTarget ~= nil and attackerTarget == ai and attacker:HasMarker("ScorpionToss") then
      ai:TriggerMoveEvent("kLEDodgeBladeGrab")
    end
  end
end
picklelib.Install()
function Pickle(GO)
  return {
    global = global
  }
end
function Unpickle(GO, t)
  global = t.global
end
function LuaHook_ClearArrow(C, data)
  game.Combat.RemoveArrows(C)
end
function LuaHook_FollowupCountReset(C, data)
  followupCount = 0
end
function LuaHook_AttackCountIncrement(C, data)
  followupCount = followupCount + 1
end
function LuaHook_CanSeidrFollowupCheck(C, data)
  local variantPickupAcquired = C:PickupIsAcquired("Flyer_VariantType")
  if variantPickupAcquired and C:PickupGetStage("Flyer_VariantType") == 0 then
    if followupCount <= followupSeidrShotCountLimit then
      return true
    else
      return false
    end
  end
end
function LuaHook_CanBlindFollowupCheck(C, data)
  local variantPickupAcquired = C:PickupIsAcquired("Flyer_VariantType")
  if variantPickupAcquired and C:PickupGetStage("Flyer_VariantType") == 4 then
    if followupCount <= followupBlindShotCountLimit then
      return true
    else
      return false
    end
  end
end
function LuaHook_CanPossessorFollowupCheck(C, data)
  local variantPickupAcquired = C:PickupIsAcquired("Flyer_VariantType")
  if variantPickupAcquired and C:PickupGetStage("Flyer_VariantType") == 5 then
    if followupCount <= followupDiveCountLimit then
      return true
    else
      return false
    end
  end
end
function LuaHook_CanIceFollowupCheck(C, data)
  local variantPickupAcquired = C:PickupIsAcquired("Flyer_VariantType")
  if variantPickupAcquired and C:PickupGetStage("Flyer_VariantType") == 3 then
    if followupCount <= followupIceShotCountLimit then
      return true
    else
      return false
    end
  end
end
function LuaHook_FlyerTargetKratos(C, data)
  _G.global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY"
end
function LuaHook_TargetSon(C, data)
  _G.global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_SON_ONLY"
end
function LuaHook_SonIncapacitatedCheck(C, data)
  local sonCreature = game.AI.FindSon()
  if sonCreature ~= nil then
    if sonCreature:GetContext() ~= sonCreature:LookupConstant("INCAPACITATED") then
      return true
    else
      return false
    end
  else
    return false
  end
end
local PossessBehavior = function(ai)
  if ai:GetHitPoints() > 0 and ai:PickupIsAcquired("HealthThreshold") and ai:PickupGetStage("HealthThreshold") ~= 2 then
    local closestDist = 100
    local closestCreature
    local NearbyCreatures = ai:FindFriendlies(30)
    for _, creature in ipairs(NearbyCreatures) do
      if creature ~= ai and creature:GetHitPoints() > 0 and creature:HasMarker("PossessableByFlyer") and not creature:HasMarker("Dormant") and creature:PickupIsAcquired("HealthThreshold") and not creature:PickupIsAcquired("TurretMode") then
        if flyerIntro then
          local distance = game.AIUtil.Distance(creature, ai)
          if closestDist > distance then
            closestCreature = creature
            closestDist = distance
            break
          end
        else
          local creatureContext = creature:GetContext()
          local creatureTarget = creature:GetTargetCreature()
          if creatureTarget ~= nil then
            local creatureTargetName = creatureTarget:GetName()
            if (creatureTargetName == "heroa00" or creatureTarget == "son00") and creatureContext ~= creature:LookupConstant("NONHOSTILE") then
              local pickupStage = creature:PickupGetStage("HealthThreshold")
              if pickupStage ~= nil and pickupStage ~= 2 and not creature:PickupIsAcquired("PossessedByFlyer") and not creature:PickupIsAcquired("FlyerPossessionTarget") then
                local heightDiff = creature.WorldPosition.y - ai.WorldPosition.y
                if heightDiff <= 0.5 then
                  local distance = game.AIUtil.Distance(creature, ai)
                  if closestDist > distance then
                    closestCreature = creature
                    closestDist = distance
                    break
                  end
                end
              end
            end
          end
        end
      end
    end
    if closestCreature ~= nil then
      if not closestCreature:PickupIsAcquired("FlyerPossessionTarget") then
        closestCreature:PickupAcquire("FlyerPossessionTarget")
      end
      _G.global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_POSSESS_FRIENDLY"
      _G.global.target = closestCreature
      ai:SetCombatTarget(_G.global.target)
      ai:ForceMove("BRA_AttPossess_Pursue_Enter")
    else
      _G.global.targetParams = "FIND_TARGET_PARAMETERS_COMBAT_KRATOS_ONLY"
      return
    end
  end
end
function LuaHook_AttemptPossess(C, data)
  if C:GetHitPoints() > 0 then
    PossessBehavior(C)
  end
end
function LuaHook_AddBlood_Cleave_Impact(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.1)
end
function LuaHook_AddBlood_Flyer_RageEnter(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.8)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.6)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.6)
end
function LuaHook_AddBlood_Flyer_RageSlam(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.2)
end
function LuaHook_AddBlood_Flyer_RageRip(ai, data)
  local player = game.Player.FindPlayer()
  player:AddBlood(game.GameObject.eRegionId.kHead, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightChest, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kFrontAbdomen, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftFrontLeg, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kRightFrontLeg, 0.4)
  player:AddBlood(game.GameObject.eRegionId.kLeftBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBack, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kBackAbdomen, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackUpperArm, 0.1)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLowerArm, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kLeftBackLeg, 0.2)
  player:AddBlood(game.GameObject.eRegionId.kRightBackLeg, 0.2)
end
local CheckForClearPath = function(C, direction, distanceCheck)
  local ai = C:GetAI()
  local jointIndex = ai:GetJointIndex("JOPelvis1")
  if direction == "forward" then
    local jointFacingFwd = ai:GetWorldJointForward(jointIndex)
    local toFront = jointFacingFwd * distanceCheck
    local offsetFront = ai.WorldPosition + toFront
    local hitFront = game.World.RaycastCollision(ai.WorldPosition, offsetFront, {
      SourceGameObject = ai,
      EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
    })
    if not hitFront then
      return true
    else
      return false
    end
  elseif direction == "back" then
    local jointFacingFwd = ai:GetWorldJointForward(jointIndex)
    local toBack = jointFacingFwd * (distanceCheck * -1)
    local offsetBack = ai.WorldPosition + toBack
    local hitBack = game.World.RaycastCollision(ai.WorldPosition, offsetBack, {
      SourceGameObject = ai,
      EntityType = game.CollisionType.New("kEnvironment", "kInvisibleBarrier")
    })
    if not hitBack then
      return true
    else
      return false
    end
  else
    print("[Flyer00] Did NOT pass a valid direction into CheckForClearEvadePath()")
    return false
  end
end
function LuaHook_BackRepositionCheck(C, data)
  if CheckForClearPath(C, "back", 4) then
    return true
  else
    return false
  end
end
function LuaHook_ForwardRepositionCheck(C, data)
  if CheckForClearPath(C, "forward", 14) then
    return true
  else
    return false
  end
end
function LuaHook_FlyerDropHealthConsumable(ai, data)
  local index = ai:GetJointIndex("JOPelvis1")
  local pos = ai:GetWorldJointPosition(index)
  local dir = ai:GetWorldForward()
  pos = pos + dir * math.random(0.15, 0.25)
  local spawnArgs = {
    SpawnObject = "golootGenericHealthRune",
    OffsetPosition = pos,
    Level = game.Level.GetPermLevel()
  }
  game.Player.FindPlayer():CallScript("LuaHook_SpawnGameObject", spawnArgs)
end
