local LD = require("design.LevelDesignLibrary")
local DL = require("design.DesignerLibrary")
local monitors = require("level.MonitorLibrary")
local BossEncounter = require("boss.statemachine")
local TUT = require("game.GlobalTutorials")
local uiCalls = require("ui.uicalls")
local mpicon = require("ui.mpicon")
local thisObj, thisLevel, player, son, pad, eagleman
local showRageGrabMessage = false
local showDestoryMagicOrbMessage = false
local hasBeenDestroyed = false
local puppeteer
local showOrbMessageCooldown = 20
local boss, boss_trigger
local despawn_adds = false
local bStartCheckCamera = false
local bossStage1 = BossEncounter:AddStage("stage1")
local bossStage2 = BossEncounter:AddStage("stage2", true)
local bossStage3 = BossEncounter:AddStage("stage3", true)
local bossStage4 = BossEncounter:AddStage("stage4", true)
local bossStage4b = BossEncounter:AddStage("stage4b", true)
local bossStage5 = BossEncounter:AddStage("stage5")
local finalStage = BossEncounter:AddStage("finalStage")
local Kra_GIntro_RotationDriver, Kra_GIntro_TranslationDriver, GuaridanIntro_warpPoint, Kra_GIntro_Start_Fwd, Kra_GIntro_Start_Position
local healthBarDelayOnCheckpoint = 1.5
local skipBossFight = engine.VFSBool.New("Skip Guardian Boss Fight")
function OnScriptLoaded(level, obj)
  thisObj = obj
  thisLevel = level
  player = game.Player.FindPlayer()
  son = game.AI.FindSon()
  pad = player.Pad
  GuaridanIntro_warpPoint = thisLevel:GetGameObject("Guardian_Intro_Warp_Point")
  if GuaridanIntro_warpPoint ~= nil then
    game.Encounters.RequestGlobalCharacterPrestream(thisLevel, GuaridanIntro_warpPoint:GetWorldPosition(), "CRT_Troll30", "troll30")
  end
  local Hel010 = game.FindLevel("Hel010_Vista")
  if Hel010 then
    eagleman = Hel010:FindSingleGameObject("helheimEagle00")
    print(eagleman)
  end
  game.SubObject.Sleep(obj)
  game.SubObject.SetForgetOnCheckpoint(obj)
end
local StartBossFight = function()
  BossEncounter:SetActiveState("stage1")
  game.SubObject.Wake(thisObj)
  _G.isReload = false
  GameObjects.GuardianDMCWall:Show()
end
function OnStart(level, obj)
  BossEncounter:AddCreature("GuardianBossSpawner")
  BossEncounter:SpawnBoss()
  boss = BossEncounter:GetBoss()
  if boss == nil then
    BossEncounter:SpawnBoss()
    boss = BossEncounter:GetBoss()
  end
  if game.Level.GetVariable("HEL_guardianKilled") then
    return
  end
  if BossEncounter:GetActiveIndex() == 1 then
    boss_trigger = monitors.CreateEntityZoneMonitor(player, "BossStart")
    boss_trigger:OnEnter(StartBossFight)
  end
  BossEncounter:SetDebugInput(true)
  game.Audio.SetWwiseSwitch(boss, "CHR_Variation", "CHR_Variation_05")
end
local SpawnToCombat = function(triggerEvent)
  boss:RequestInteract(thisObj)
  boss:TriggerMoveEvent(triggerEvent)
  boss:CallScript("LuaHook_SetState", "INCOMBAT")
  _G.isReload = false
end
local SetBossName = function()
  mpicon.level.Create(boss:GetGameObject(), "BOSS_HEALTH_BAR")
  mpicon.level.SetTextObjectText(boss, "BOSS_HEALTH_BAR", "bossName", "[MSG:" .. 35116 .. "]")
  mpicon.level.SetTextObjectText(boss, "BOSS_HEALTH_BAR", "icon", "\198\161")
  if boss.SetOverrideStatusEffectIconParent ~= nil then
    boss:SetOverrideStatusEffectIconParent("BOSS_HEALTH_BAR")
  end
  DL.NewGamePlusLevelBoostFlat(boss, 3)
end
local CheckEagleStart = function()
  if bStartCheckCamera then
    local checkObj = thisLevel:FindSingleGameObject("eagle_dir")
    if game.Camera.GetViewPenetration(checkObj:GetWorldPosition(), 0, 0) > 0.35 then
      bStartCheckCamera = false
      boss:TriggerMoveEvent("kLE_RevealNow")
    end
  end
end
local isDebug = false
function bossStage1:OnEnter(level)
  game.Compass.SetDesignerForcedHide(true)
  pad:DisableGameButton(tweaks.ePad.kPadR1)
  pad:DisableGameButton(tweaks.ePad.kPadR2)
  pad:DisableGameButton(tweaks.ePad.kPadCross)
  boss:RequestInteract(thisObj)
  if boss_trigger ~= nil then
    boss_trigger:Stop()
    boss_trigger = nil
  end
  if son ~= nil then
    local availabilityState = {
      AvailableForSync = false,
      AvailableForBanter = false,
      AvailableInLevel = false,
      AvailableForCombat = false
    }
    game.AI.FindSon():SetNewAvailabilityRequest("SonUnavailable", availabilityState)
  end
  self.marker = level:FindSingleGameObject("SpawnSpot")
  boss:CallScript("LuaHook_SetInCombatState")
  Kra_GIntro_Start_Fwd = GuaridanIntro_warpPoint:GetWorldForward()
  Kra_GIntro_Start_Position = GuaridanIntro_warpPoint:GetWorldPosition()
  if Kra_GIntro_RotationDriver == nil then
    Kra_GIntro_RotationDriver = player:GetAnimDriver("kraGIntroRotationDriver")
  end
  Kra_GIntro_RotationDriver.ValueVec = Kra_GIntro_Start_Fwd
  if Kra_GIntro_TranslationDriver == nil then
    Kra_GIntro_TranslationDriver = player:GetAnimDriver("kraGIntroTranslationDriver")
  end
  Kra_GIntro_TranslationDriver.ValueVec = Kra_GIntro_Start_Position
  if isDebug or string.find(thisLevel.Name, "WAD_TB_GuardianTroll") then
    boss:TriggerMoveEvent("kLESpawnBreak")
  else
    player:TriggerMoveEvent("LE_HelGuardianTrollIntro")
    PlaySoundGuardianAlerted()
  end
  boss:ForceHideHealthBar()
end
function bossStage1:OnUpdate(level)
  if boss:IsInNavigationMove() then
    BossEncounter:ForwardState()
  end
end
function bossStage1:OnExit(level)
  pad:EnableGameButton(tweaks.ePad.kPadR1)
  pad:EnableGameButton(tweaks.ePad.kPadR2)
  pad:EnableGameButton(tweaks.ePad.kPadCross)
end
function bossStage2:OnEnter(level, isDebugSkip)
  SetBossName()
  if isDebugSkip or _G.isReload then
    DL.WarpPlayer(thisLevel, "StartSpot1")
    DL.WarpCreature(boss, thisLevel, "teleport_final")
    SpawnToCombat("kLE_CombatSpawn")
    player:TriggerMoveEvent("kLE_SkipIntroHel600")
  end
  SetStageOneMusic()
end
local DespawnAdds = function()
  local remains = player:FindEnemies(35)
  for _, enemy in pairs(remains) do
    if enemy:GetName() ~= "troll30" then
      enemy:Destroy()
    end
  end
end
function bossStage2:OnUpdate(level)
  if not despawn_adds and boss ~= nil and boss:HasMarker("Troll30CSStart") then
    DespawnAdds()
    despawn_adds = true
  end
  if boss ~= nil and boss:HasMarker("Troll30Dead") then
    guardianKilledVAR()
    BossEncounter:SetActiveState("finalStage")
    return
  end
  if boss:PickupGetStage("TrollPhase") >= 1 then
    BossEncounter:ForwardState()
  end
end
function bossStage2:OnExit(level, isDebugskip)
end
function bossStage3:OnEnter(level, isDebugSkip)
  if isDebugSkip or _G.isReload then
    _G.StartLevelTimer(healthBarDelayOnCheckpoint, SetBossName)
    DL.WarpPlayer(thisLevel, "StartSpot1")
    DL.WarpCreature(boss, thisLevel, "teleport_final")
    SpawnToCombat("kLE_AdavanceStage3")
    boss:PickupSetStage("TrollPhase", 1)
  end
end
function bossStage3:OnUpdate(level)
  if not despawn_adds and boss ~= nil and boss:HasMarker("Troll30CSStart") then
    DespawnAdds()
    despawn_adds = true
  end
  if boss ~= nil and boss:HasMarker("Troll30Dead") then
    guardianKilledVAR()
    BossEncounter:SetActiveState("finalStage")
    return
  end
  CheckEagleStart()
  if boss:PickupGetStage("TrollPhase") >= 2 then
    BossEncounter:ForwardState()
  end
end
function bossStage4:OnEnter(level, isDebugSkip)
  if isDebugSkip or _G.isReload then
    _G.StartLevelTimer(healthBarDelayOnCheckpoint, SetBossName)
    DL.WarpPlayer(thisLevel, "StartSpot1")
    DL.WarpCreature(boss, thisLevel, "teleport_final")
    SpawnToCombat("kLE_AdavanceStage4")
    boss:PickupSetStage("TrollPhase", 2)
  end
end
function bossStage4:OnUpdate(level)
  if not despawn_adds and boss ~= nil and boss:HasMarker("Troll30CSStart") then
    DespawnAdds()
    despawn_adds = true
  end
  if boss ~= nil then
    if boss:HasMarker("Troll30Dead") then
      guardianKilledVAR()
      BossEncounter:SetActiveState("finalStage")
      return
    end
    CheckEagleStart()
    if boss:PickupGetStage("TrollPhase") >= 3 then
      BossEncounter:ForwardState()
    end
  end
end
function bossStage4b:OnEnter(level, isDebugSkip)
  if isDebugSkip or _G.isReload then
    _G.StartLevelTimer(healthBarDelayOnCheckpoint, SetBossName)
    DL.WarpPlayer(thisLevel, "StartSpot1")
    DL.WarpCreature(boss, thisLevel, "teleport_final")
    SpawnToCombat("kLE_AdavanceStage4b")
    boss:PickupSetStage("TrollPhase", 3)
  end
end
function bossStage4b:OnUpdate(level)
  if not despawn_adds and boss ~= nil and boss:HasMarker("Troll30CSStart") then
    DespawnAdds()
    despawn_adds = true
  end
  if boss ~= nil and boss:HasMarker("Troll30Dead") then
    guardianKilledVAR()
    BossEncounter:SetActiveState("finalStage")
    return
  end
  CheckEagleStart()
  if boss:IsPlayingMove("MOV_HitFinalStunLoop") or boss:IsPlayingMove("MOV_HitFinalStunEnter") then
    BossEncounter:ForwardState()
  end
end
function bossStage5:OnEnter(level, isDebugSkip)
  if isDebugSkip or _G.isReload then
    SetBossName()
    DL.WarpPlayer(thisLevel, "StartSpot1")
    DL.WarpCreature(boss, thisLevel, "teleport_final")
    SpawnToCombat("kLE_AdavanceStage5")
  end
end
function bossStage5:OnUpdate(level)
  if showRageGrabMessage and boss:IsPlayingMove("MOV_CSRageGrabTroll30Enter") then
    uiCalls.UI_Event_TurnOffTutorialMessage()
    showRageGrabMessage = false
  end
  CheckEagleStart()
  if not despawn_adds and boss ~= nil and boss:HasMarker("Troll30CSStart") then
    DespawnAdds()
    despawn_adds = true
    mpicon.level.Off(boss:GetGameObject(), "BOSS_HEALTH_BAR")
    boss:ForceHideHealthBar()
  end
  if boss ~= nil and boss:HasMarker("Troll30Dead") then
    guardianKilledVAR()
    game.Level.SetVariable("CompletedCineNumber", 387)
    BossEncounter:SetDebugInput(false)
    BossEncounter:ForwardState()
    GameObjects.GuardianDMCWall:Hide()
  end
end
function finalStage:OnEnter(level, isDebugSkip)
  if isDebugSkip then
    if showRageGrabMessage then
      uiCalls.UI_Event_TurnOffTutorialMessage()
      showRageGrabMessage = false
    end
    if not despawn_adds and boss ~= nil then
      DespawnAdds()
      despawn_adds = true
      mpicon.level.Off(boss:GetGameObject(), "BOSS_HEALTH_BAR")
      boss:ForceHideHealthBar()
      guardianKilledVAR()
      game.Level.SetVariable("CompletedCineNumber", 387)
      BossEncounter:SetDebugInput(false)
    end
  end
  if _G.isReload then
    _G.isReload = false
    boss:PickupRelinquish("TrollStone")
    local cineObj = level:FindSingleGameObject("Cine_RetrieveHeart")
    puppeteer = game.Puppeteer.NewForce(cineObj, "deadTroll", boss)
    puppeteer:Sync("BRA_RetrieveHeartTrollIdle", true, {}, "RetrieveHeart_SynchJoint")
  end
end
function OnUpdate(level, obj)
  if engine.IsDebug() then
    local debugTable = {}
    local livingenemies = DL.FindLivingEnemies(player, 45)
    local numberClones = 0
    for _, enemy in pairs(livingenemies) do
      if string.find(enemy:GetName(), "projection") then
        numberClones = numberClones + 1
      end
    end
    table.insert(debugTable, {
      "Number Clones: ",
      numberClones
    })
    BossEncounter:UpdateCustomTable(debugTable)
  end
  if skipBossFight.value == true then
    boss:AddMarker("Troll30Dead")
  end
end
local trollTeleporting = false
local TeleportSettingEnd = function(ai)
  ai:HideJoint(ai:GetJointIndex("phaseSphereJoint"))
  if ai:PickupIsAcquired("TrollGuardianTeleport") then
    ai:PickupRelinquish("TrollGuardianTeleport")
  end
  ai:CallScript("LuaHook_StopTeleportAudio")
  ai:StartMaterialAnim("troll30_teleportOff")
  trollTeleporting = false
end
local TeleportAttack = function(ai)
  if ai:PickupIsAcquired("TrollGuardianTeleport") then
    ai:PickupRelinquish("TrollGuardianTeleport")
  end
  ai:StartMaterialAnim("troll30_teleportOff")
  trollTeleporting = false
  ai:CallScript("LuaHook_StopTeleportAudio")
  ai:CallScript("LuaHook_SetState", "INCOMBAT")
end
local GetTeleportSpot = function(level, ai, isClosest)
  local teleport = level:FindSingleGameObject("TeleportSpots")
  local teleportSpots = teleport.Children
  local minDist = 10
  local teleportPoint
  while 0 < #teleportSpots do
    local index = math.random(#teleportSpots)
    local point = teleportSpots[index]
    local distPlayer = game.AIUtil.Distance(point, game.Player.FindPlayer())
    local distAI = game.AIUtil.Distance(point, ai)
    if minDist > distPlayer or distAI < 12.5 then
      table.remove(teleportSpots, index)
    else
      teleportPoint = point
      break
    end
  end
  return teleportPoint
end
function OnTrollPhase3Start()
  if boss:PickupIsAcquired("TrollGuardianArmor") then
    boss:PickupRelinquish("TrollGuardianArmor")
  end
  boss:TriggerMoveEvent("kLEFinalSummon")
  showRageGrabMessage = true
end
function OnInteractStart(level)
  print("interact start")
end
function LuaHook_StartIdle(level, obj, ai)
  _G.StartLevelTimer(12, function()
    boss:TriggerMoveEvent("kLEFinalSummon")
  end)
end
function LuaHook_StartFinalTeleport(level, obj, ai)
  LuaHook_TeleportTroll(level, obj, ai)
  if boss:PickupIsAcquired("TrollGuardianArmor") then
    boss:PickupRelinquish("TrollGuardianArmor")
  end
  _G.StartLevelTimer(1.2, function()
    local teleport = level:FindSingleGameObject("Cine_RetrieveHeart")
    ai:Warp(teleport:GetWorldPosition(), teleport:GetWorldForward())
  end)
end
function LuaHook_EndFinalTeleport(level, obj, ai)
  TeleportSettingEnd(ai)
end
function LuaHook_ResumePhase3(level, obj, ai)
  uiCalls.UI_Event_TurnOffTutorialMessage()
end
function guardianKilledVAR()
  game.Level.SetVariable("HEL_guardianKilled", true)
  boss:CallScript("LuaHook_SetState", "NONHOSTILE")
  ShowGuardianDrain()
  GameObjects.Cine_RetrieveHeart.LuaObjectScript.SetupCineSequence()
end
function ShowGuardianDrain()
  local drainGroup = GameObjects.GuardianDrainGroup_Lua
  drainGroup:FindSingleGOByName("helmarkertemp"):Show()
  drainGroup:FindSingleGOByName("InteractZone_Arena01").LuaObjectScript.Enable()
  GameObjects.BifrostDrainCrystal_Arena01:Show()
  GameObjects.BifrostDrainCrystal_Arena01.LuaObjectScript.ForceDrainCrystal()
end
function ShowDestoryOrbMessage(lvl, obj, ai)
  if showDestoryMagicOrbMessage == false and hasBeenDestroyed == false then
    showDestoryMagicOrbMessage = true
    _G.StartLevelTimer(1.5, function()
      TUT.Hel600_GuardianDestroyMagicOrb()
      _G.StartLevelTimer(showOrbMessageCooldown, function()
        showDestoryMagicOrbMessage = false
      end)
    end)
  end
end
function SetHasDestroyByPlayer()
  hasBeenDestroyed = true
end
function LuaHook_TeleportTroll(level, obj, ai)
  ai:ShowJoint(ai:GetJointIndex("phaseSphereJoint"))
  ai:StartMaterialAnim("troll30_teleportON")
  trollTeleporting = true
  ai:CallScript("LuaHook_PlayTeleportAudio")
  ai:PickupAcquire("TrollGuardianTeleport")
end
function LuaHook_TeleportNow(level, obj, ai)
  if ai:HasMarker("Phase") then
    StartBossTimeTeleport(level, ai)
    return
  end
  ai:CallScript("LuaHook_SetState", "NONHOSTILE")
  ai:HideJoint(ai:GetJointIndex("phaseSphereJoint"))
  local spot = GetTeleportSpot(level, ai, false)
  assert(spot, "No found teleport spot!!")
  local facing = (player:GetWorldPosition() - spot:GetWorldPosition()):Normalized()
  ai:Warp(spot:GetWorldPosition(), facing)
  _G.StartLevelTimer(1, function()
    TeleportAttack(ai)
  end)
end
function LuaHook_TeleportInterrupted(level, obj, ai)
  if trollTeleporting then
    TeleportSettingEnd(ai)
  end
end
function LuaHook_TeleportAttack(level, obj, ai)
  TeleportSettingEnd(ai)
  local isJuggerAttack = ai:HasMarker("Jugger")
  local dir = game.Camera.GetOrbitForward()
  local maxDist = 4.5
  if isJuggerAttack then
    maxDist = 6
  end
  local position = player:GetWorldPosition() + dir * maxDist
  local spawnSpot = game.NavMesh.ClosestPoint(position)
  local aiFacing = (player:GetWorldPosition() - position):Normalized()
  if spawnSpot.x > 372 and spawnSpot.y < 9 then
    ai:Warp(spawnSpot, aiFacing)
  end
end
function LuaHook_TeleportRangeAttack(level, obj, ai)
  TeleportSettingEnd(ai)
  local spot = GetTeleportSpot(level, ai, false)
  assert(spot, "No found teleport spot!!")
  local facing = (player:GetWorldPosition() - spot:GetWorldPosition()):Normalized()
  ai:Warp(spot:GetWorldPosition(), facing)
end
function LuaHook_TriggerEagleMan(level, obj, ai)
end
function StartBossTimeTeleport(level, ai)
  local teleport
  teleport = level:FindSingleGameObject("teleport_wait")
  ai:TriggerMoveEvent("kLE_StartWindGust")
  ai:Warp(teleport:GetWorldPosition(), teleport:GetWorldForward())
  bStartCheckCamera = true
end
function BossTimeStartsEagle(level, obj, ai)
end
function BossTimeStartGust(level, obj, ai)
end
function BossTimeReveal(level, obj, ai)
  TeleportAttack(ai)
  local teleport = level:FindSingleGameObject("teleport_final")
  ai:Warp(teleport:GetWorldPosition(), teleport:GetWorldForward())
end
local slam_Number = 1
local SetWaveSpawnerPositionPhase1 = function(level, ai)
  local postion = "explosive_wave_"
  if slam_Number == 1 then
    postion = postion .. "center"
  elseif slam_Number == 2 then
    postion = postion .. "left"
  elseif slam_Number == 3 then
    postion = postion .. "right"
  elseif slam_Number == 4 then
    postion = postion .. "rightFar"
  elseif slam_Number == 5 then
    postion = postion .. "leftFar"
  end
  local spawnObj = level:FindSingleGameObject(postion)
  return spawnObj
end
local SetWaveSpawnerPositionPhase2 = function(level, ai)
  local postion = "explosive_wave_"
  if slam_Number == 1 then
    postion = postion .. "left"
  elseif slam_Number == 2 then
    postion = postion .. "right"
  elseif slam_Number == 3 then
    postion = postion .. "leftFar"
  elseif slam_Number == 4 then
    postion = postion .. "center"
  elseif slam_Number == 5 then
    postion = postion .. "left"
  end
  local spawnObj = level:FindSingleGameObject(postion)
  return spawnObj
end
local SetWaveSpawnerPositionPhase3 = function(level, ai)
  local postion = "explosive_wave_"
  if slam_Number == 1 then
    postion = postion .. "left"
  elseif slam_Number == 2 then
    postion = postion .. "right"
  elseif slam_Number == 3 then
    postion = postion .. "leftFar"
  elseif slam_Number == 4 then
    postion = postion .. "center"
  elseif slam_Number == 5 then
    postion = postion .. "left"
  end
  local spawnObj = level:FindSingleGameObject(postion)
  return spawnObj
end
function BossFireExplosiveWave(level, obj, ai)
  if 5 < slam_Number then
    slam_Number = 1
  end
  local spawnObj = SetWaveSpawnerPositionPhase1(level, ai)
  local waveSpawner = game.FX.Spawn("troll30_explosive_wave_spawner", level, {EffectCreator = boss})
  waveSpawner:SetWorldPosition(spawnObj:GetWorldPosition())
  waveSpawner:SetWorldFacing(spawnObj:GetWorldForward())
  spawnObj = SetWaveSpawnerPositionPhase2(level, ai)
  local waveSpawner2 = game.FX.Spawn("troll30_explosive_wave_spawner", level, {EffectCreator = boss})
  waveSpawner2:SetWorldPosition(spawnObj:GetWorldPosition())
  waveSpawner2:SetWorldFacing(spawnObj:GetWorldForward())
  slam_Number = slam_Number + 1
end
function BossStartGroundPhase(level, obj, ai)
  local currentStage = BossEncounter:GetActiveStateName()
  if currentStage == "stage3" then
    SetStageOneGroundPoundMusic()
  elseif currentStage == "stage4" then
    SetStageTwoGroundPoundMusic()
  elseif currentStage == "stage4b" then
    SetStageThreeGroundPoundMusic()
  end
end
function BossStartNormalPhase(level, obj, ai)
  local currentStage = BossEncounter:GetActiveStateName()
  if currentStage == "stage3" then
    SetStageTwoMusic()
  elseif currentStage == "stage4" then
    SetStageThreeMusic()
  end
end
function SetStageOneMusic()
end
function SetStageOneGroundPoundMusic()
  game.Audio.StartCheckpointedMusic("SND_MX_HEL_guardian_fight_p2_in")
end
function SetStageTwoMusic()
  game.Audio.StartCheckpointedMusic("SND_MX_HEL_guardian_fight_p3_in")
end
function SetStageTwoGroundPoundMusic()
  game.Audio.StartCheckpointedMusic("SND_MX_HEL_guardian_fight_p4_in")
end
function SetStageThreeMusic()
  game.Audio.StartCheckpointedMusic("SND_MX_HEL_guardian_fight_p5_in")
end
function SetStageThreeGroundPoundMusic()
  game.Audio.StartCheckpointedMusic("SND_MX_HEL_guardian_fight_p6_in")
end
function PlaySoundGuardianAlerted()
  LD.PlaySound(boss:FindSingleSoundEmitterByName("SNDMouth"), "SND_CINE_HelGuardianAlerted")
end
