local LD = require("design.LevelDesignLibrary")
local uiCalls = require("ui.uicalls")
local timers = require("level.timer")
local monitors = require("level.MonitorLibrary")
local LARGE_HEALTH_AMOUNT = 100
local SMALL_HEALTH_AMOUNT = 25
local LARGE_RAGE_AMOUNT = 100
local SMALL_RAGE_AMOUNT = 25
local SMALL_EXPIRATION_TIMER = 60
local thisObj, lootObject, player, Resource, XPResourceName, interactZone, consumableFX, useLargeAmount, retainIfSmall
local healthAwarded = 0
local rageAwarded = 0
local consumed = false
local timerTable = {}
local deferredFunction
IsLoot = true
local SECONDS_PER_TRANSITION = 3
local transitionRuneStone, FXTable, animationPlaybackSpeed, CorpseObject, currentState, transitionAnimMonitor
local alwaysRepopulate = false
function OnScriptLoaded(level, obj)
  thisObj = obj
  lootObject = thisObj
  player = game.Player.FindPlayer()
  Resource = obj:FindLuaTableAttribute("Resource")
  useLargeAmount = obj:FindLuaTableAttribute("LargeHealthOrRageAmount")
  retainIfSmall = obj:FindLuaTableAttribute("RetainIfSmall")
  if Resource == "XP" or Resource == "Transition" then
    XPResourceName = obj:FindLuaTableAttribute("XPResourceName")
    assert(XPResourceName, "If you select XP or Transition as a resource you should provide an XP resource name")
  end
  alwaysRepopulate = obj:FindLuaTableAttribute("RepopulateHealthRune") or false
  interactZone = LD.CreateInteractZone_ConsumableStomp_360(lootObject, "zeroJoint")
  interactZone:SetTags("CarryActive")
  interactZone:SetTags("SpecialBossMoment")
  game.SubObject.Sleep(thisObj)
  if alwaysRepopulate then
    game.SubObject.SetForgetOnCheckpoint(thisObj)
    thisObj:SetInhibitSave()
  end
  SoundInit()
end
function OnStart(level, obj)
  if consumed then
    Disable()
    return
  end
  if Resource == "Transition" then
    InitializeTransitionRune()
  end
  SetAwards()
  SoundOnStart()
end
function SetAwards()
  if Resource == "Health" or Resource == "Transition" then
    if player:AttributeGetValue("Difficulty") == 4 then
      healthAwarded = useLargeAmount and LARGE_HEALTH_AMOUNT * 0.5 or SMALL_HEALTH_AMOUNT * 0.75
    else
      healthAwarded = useLargeAmount and LARGE_HEALTH_AMOUNT or SMALL_HEALTH_AMOUNT
    end
  end
  if Resource == "Rage" or Resource == "Transition" then
    if player:AttributeGetValue("Difficulty") == 4 then
      rageAwarded = useLargeAmount and LARGE_RAGE_AMOUNT * 0.5 or SMALL_RAGE_AMOUNT * 0.75
    else
      rageAwarded = useLargeAmount and LARGE_RAGE_AMOUNT or SMALL_RAGE_AMOUNT
    end
  end
end
function SetFadeAway()
  if not useLargeAmount and not retainIfSmall then
    local timerVisualLength = 5
    local numberOfBlinks = timerVisualLength * timerVisualLength
    local startExpirationTime = SMALL_EXPIRATION_TIMER - timerVisualLength
    local phaseLength = 0.1
    for i = 1, numberOfBlinks + 1 do
      local phaseOutTime = math.sqrt(i - 1) + startExpirationTime
      local phaseInTime = phaseOutTime + phaseLength
      if phaseOutTime < SMALL_EXPIRATION_TIMER then
        table.insert(timerTable, timers.StartLevelTimer(phaseOutTime, HideObject))
      end
      if phaseInTime < SMALL_EXPIRATION_TIMER then
        table.insert(timerTable, timers.StartLevelTimer(phaseInTime, ShowObject))
      end
    end
    table.insert(timerTable, timers.StartLevelTimer(SMALL_EXPIRATION_TIMER, DestroyObject))
  end
end
function DestroyObject()
  if thisObj ~= nil then
    ClearTimers()
    PlayFinishEffect()
    PutAway()
    consumed = true
  end
end
function HideObject()
  if thisObj ~= nil then
    thisObj:HideModel()
  end
end
function ShowObject()
  if thisObj ~= nil then
    thisObj:ShowModel()
  end
end
function OnUseWorld(level, obj)
  local allowUseWorldHack = false
  if game.CHECK_FEATURE("ALLOW_USE_WORLD_HACK") then
    allowUseWorldHack = player:IsAllowUseWorldHack()
  end
  if interactZone:PlayerCanInteract() and (player:IsInNavigationMove() or allowUseWorldHack) then
    player:RequestInteract(obj, interactZone)
    if Resource == "Transition" then
      transitionRuneStone:ClearAllAnimationCallbacks()
      transitionRuneStone:PauseAnimation()
    end
  end
end
function ClearTimers()
  for i = 1, #timerTable do
    if timerTable[i].running then
      timerTable[i]:Pause()
    end
    timerTable[i] = nil
  end
end
function OnInteractStart()
  ShowObject()
  ClearTimers()
  local puppeteer = game.Puppeteer.NewForce(thisObj, "Prop Scoop", player)
  puppeteer:StartBranch("BRA_ConsumableScoop_StillEnter")
  puppeteer:OnComplete(function()
    puppeteer:Clear()
    puppeteer = nil
  end)
  PlaySoundPickup()
end
function PickUp(level, obj, creature)
end
local AwardResource_Health = function()
  local reducedHealth = false
  if player:AttributeGetValue("Difficulty") == 4 then
    local statusMeterDecrease = 25
    player:MeterSetValue("Frost", player:MeterGetValue("Frost") - statusMeterDecrease)
    player:MeterSetValue("Burn", player:MeterGetValue("Burn") - statusMeterDecrease)
    player:MeterSetValue("Poison", player:MeterGetValue("Poison") - statusMeterDecrease)
    player:MeterSetValue("Blind", player:MeterGetValue("Blind") - statusMeterDecrease)
    player:MeterSetValue("Daze", player:MeterGetValue("Daze") - statusMeterDecrease)
    if player:PickupIsAcquired("Debuff_Hero_Frost") then
      player:PickupRelinquish("Debuff_Hero_Frost")
    end
    if player:PickupIsAcquired("Debuff_Hero_Burn") then
      player:PickupRelinquish("Debuff_Hero_Burn")
      reducedHealth = true
    end
    if player:PickupIsAcquired("Debuff_Hero_Poison_Short") then
      player:PickupRelinquish("Debuff_Hero_Poison_Short")
      reducedHealth = true
    end
  end
  if player:MeterGetValue("Health") < player:MeterGetMax("Health") then
    local targetValue = healthAwarded * SonArmorPerkModifier()
    if reducedHealth == true then
      targetValue = targetValue * 0.5
    end
    targetValue = math.min(player:MeterGetValue("Health") + targetValue, player:MeterGetMax("Health"))
    player:MeterSetValue("Health", targetValue)
  elseif player:PickupIsAcquired("Perk_HealthRagePickupFull") and player:MeterGetValue("Blood") < player:MeterGetMax("Blood") then
    rageAwarded = useLargeAmount and LARGE_RAGE_AMOUNT * 0.5 or SMALL_RAGE_AMOUNT * 0.75
    local targetValue = math.min(player:MeterGetValue("Blood") + rageAwarded * SonArmorPerkModifier(), player:MeterGetMax("Blood"))
    player:MeterSetValue("Blood", targetValue)
  end
end
local AwardResource_Rage = function()
  if player:MeterGetValue("Blood") < player:MeterGetMax("Blood") then
    local targetValue = math.min(player:MeterGetValue("Blood") + rageAwarded * SonArmorPerkModifier(), player:MeterGetMax("Blood"))
    player:MeterSetValue("Blood", targetValue)
  elseif player:PickupIsAcquired("Perk_HealthRagePickupFull") and player:MeterGetValue("Health") < player:MeterGetMax("Health") then
    healthAwarded = useLargeAmount and LARGE_HEALTH_AMOUNT * 0.5 or SMALL_HEALTH_AMOUNT * 0.75
    local targetValue = math.min(player:MeterGetValue("Health") + healthAwarded * SonArmorPerkModifier(), player:MeterGetMax("Health"))
    player:MeterSetValue("Health", targetValue)
  end
end
local AwardResource_Cooldown = function()
  player:PickupAcquire("Buff_CooldownBurst")
  player:PickupActivate("Buff_CooldownBurst")
end
local AwardResource_XP = function()
  game.Loot.GrantAward(XPResourceName, "HERO", nil, thisObj:GetName())
end
local AwardResource_Transition = function()
  if currentState == "Health" then
    AwardResource_Health()
  elseif currentState == "Rage" then
    AwardResource_Rage()
  elseif currentState == "XP" then
    AwardResource_XP()
  end
  game.SubObject.SoftSave(thisObj)
end
function AwardResource(level, obj, creature)
  if Resource == "Health" then
    AwardResource_Health()
  elseif Resource == "Rage" then
    AwardResource_Rage()
  elseif Resource == "Cooldown" then
    AwardResource_Cooldown()
  elseif Resource == "XP" then
    AwardResource_XP()
  elseif Resource == "Transition" then
    AwardResource_Transition()
  end
  PlayFinishEffect()
  if lootObject.Parent then
    lootObject.Parent:HideModel()
  else
    lootObject:HideModel()
  end
  consumed = true
end
function DeferFunctionUntilFXSpawned(fn)
  deferredFunction = fn
  game.SubObject.Wake(thisObj)
end
function CallDeferredFunction()
  if consumableFX and consumableFX.Spawned then
    deferredFunction()
    return true
  end
  return false
end
function PlayFinishEffect()
  local effectsString, effectsResource
  if Resource == "Transition" then
    effectsResource = currentState
  else
    effectsResource = Resource
  end
  if effectsResource == "Health" then
    effectsString = "runeActivateHealth_LODGRP"
  elseif effectsResource == "Rage" then
    effectsString = "runeActivateRage"
  elseif effectsResource == "Cooldown" then
    effectsString = "runeActivateCooldown"
  elseif effectsResource == "XP" then
    effectsString = "runeActivateNeutral"
  end
  if effectsString then
    local jointName
    if thisObj:FindJointIndex("PhysicsObject") ~= nil then
      jointName = "PhysicsObject"
    end
    if player:PickupIsAcquired("Perk_HealthAndRagePickups_Buff") then
      consumableFX = game.FX.Spawn(effectsString, nil, {
        GameObject = thisObj,
        Joint = jointName,
        AutoDelete = true,
        Scale = 2.5
      })
    else
      consumableFX = game.FX.Spawn(effectsString, nil, {
        GameObject = thisObj,
        Joint = jointName,
        AutoDelete = true
      })
    end
  end
end
function PutAway(level, obj, creature)
  Disable()
  if Resource == "Transition" then
    DisableTransition()
  end
  thisObj:Hide()
  interactZone:Disable()
end
function SonArmorPerkModifier()
  local son = game.AI.FindSon()
  if son == nil then
    return 1
  end
  if son:PickupIsAcquired("SonArmorDefense02") or son:PickupIsAcquired("SonArmorDefense03") or son:PickupIsAcquired("SonArmorBonus01") or son:PickupIsAcquired("SonArmorBonus02") or son:PickupIsAcquired("SonArmorBonus04") then
    return 1.35
  end
  return 1
end
function OnUpdate(level, obj)
  if CallDeferredFunction() then
    game.SubObject.Sleep(thisObj)
  end
end
function OnSaveCheckpoint(level, obj)
  return {consumed = consumed}
end
function OnRestoreCheckpoint(level, obj, savedInfo)
  consumed = savedInfo.consumed
end
function Enable()
  interactZone:Enable()
  lootObject:Show()
end
function Disable()
  interactZone:Disable()
  lootObject:Hide()
end
function DisableInteractZone()
  interactZone:Disable()
end
function EnableInteractZone()
  interactZone:Enable()
end
function GetInteractZone()
  return interactZone
end
function GetResource()
  return Resource
end
function OnZoneEnter()
end
function OnZoneExit()
end
function InitializeTransitionRune()
  transitionRuneStone = thisObj:FindSingleGOByName("LootTransitionRune_RN").Child
  FXTable = {
    Health = transitionRuneStone:FindSingleGOByName("PickUp_Glow_LRG_Green_RN"),
    Rage = transitionRuneStone:FindSingleGOByName("PickUp_Glow_LRG_Red_RN"),
    XP = transitionRuneStone:FindSingleGOByName("PickUp_Glow_LRG3_RN")
  }
  transitionRuneStone = transitionRuneStone:FindSingleGOByName("TransitionRune")
  Transition_SetNull()
  Transition_SetHealth()
  local mayaAnimLengthFrames = 100
  local mayaAnimLengthSeconds = mayaAnimLengthFrames / 30
  animationPlaybackSpeed = SECONDS_PER_TRANSITION / mayaAnimLengthSeconds
  transitionAnimMonitor = monitors.CreateAnimFrameMonitor(transitionRuneStone)
  transitionAnimMonitor:OnFrameForward(95, Transition_SetNull)
  transitionAnimMonitor:OnFrameForward(120, Transition_SetRage)
  transitionAnimMonitor:OnFrameForward(215, Transition_SetNull)
  transitionAnimMonitor:OnFrameForward(240, Transition_SetXP)
  transitionAnimMonitor:OnFrameForward(335, Transition_SetNull)
  transitionAnimMonitor:OnFrameForward(370, Transition_SetHealth)
  transitionRuneStone:PlayAnimationCycle({Rate = animationPlaybackSpeed})
end
function SetCorpseObject(corpse)
  CorpseObject = corpse.LuaObjectScript
end
function EnableTransitionInteract(flag)
  if flag then
    if CorpseObject then
      CorpseObject.Enable()
    else
      interactZone:Enable()
    end
  elseif CorpseObject then
    CorpseObject.Disable()
  else
    interactZone:Disable()
  end
end
function Transition_SetHealth()
  EnableTransitionInteract(true)
  FXTable.Health:Show()
  currentState = "Health"
  PlaySoundIdleLoop()
end
function Transition_SetRage()
  EnableTransitionInteract(true)
  FXTable.Rage:Show()
  currentState = "Rage"
  PlaySoundIdleLoop()
end
function Transition_SetXP()
  EnableTransitionInteract(true)
  FXTable.XP:Show()
  currentState = "XP"
  PlaySoundIdleLoop()
end
function Transition_SetNull()
  if consumableFX ~= nil and consumableFX.Spawned then
    consumableFX:Remove()
  else
    consumableFX = nil
  end
  EnableTransitionInteract(false)
  for _, fx in pairs(FXTable) do
    fx:Hide()
  end
  StopSoundIdleLoop()
  currentState = "Null"
end
function DisableTransition()
  transitionRuneStone:ClearAllAnimationCallbacks()
  Transition_SetNull()
  FXTable = nil
  transitionAnimMonitor:Stop()
  transitionAnimMonitor:Terminate()
  transitionAnimMonitor = nil
end
function OnSpawnedObjectDestroyed()
  print("Calling Despawn LuaHook")
  ClearTimers()
  if Resource == "Transition" then
    DisableTransition()
  end
end
function IsHealthOrRage()
  return 0 < healthAwarded or 0 < rageAwarded
end
local soundEmitter
local soundEvents = {
  Green_Idle = "SND_LOOT_Shard_Green_Idle_LP",
  Green_Pickup = "SND_LOOT_Shard_Green_Pickup",
  Red_Idle = "SND_LOOT_Shard_Red_Idle_LP",
  Red_Pickup = "SND_LOOT_Shard_Red_Pickup",
  White_Idle = "SND_LOOT_Shard_White_LP",
  White_Pickup = "SND_LOOT_Shard_White_Pickup",
  Yellow_Idle = "SND_LOOT_Shard_Yellow_LP",
  Yellow_Pickup = "SND_LOOT_Shard_Yellow_Pickup"
}
function SoundInit()
  local targetSoundEmitter = thisObj.SoundEmitters[1]
  if targetSoundEmitter ~= nil then
    soundEmitter = targetSoundEmitter
  end
end
function SoundOnStart()
  PlaySoundIdleLoop()
end
function PlaySoundIdleLoop()
  local targetColor = GetCurrentColor()
  if targetColor ~= nil then
    LD.PlayRestartableSoundLoop(soundEmitter, soundEvents[targetColor .. "_Idle"])
  end
end
function StopSoundIdleLoop(forcedTargetColor)
  local targetColor
  if forcedTargetColor ~= nil then
    targetColor = forcedTargetColor
  else
    targetColor = GetCurrentColor()
  end
  if targetColor ~= nil then
    LD.StopRestartableSoundLoop(soundEmitter, soundEvents[targetColor .. "_Idle"])
  end
end
function PlaySoundPickup()
  local targetColor = GetCurrentColor()
  StopSoundIdleLoop(targetColor)
  if Resource == "Transition" then
    LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), "SND_LOOT_Shard_Transition_Pickup")
    return
  elseif targetColor ~= nil then
    LD.PlaySound(player:FindSingleSoundEmitterByName("SNDKratos"), soundEvents[targetColor .. "_Pickup"])
  end
end
function GetCurrentColor()
  local currentColor
  if Resource == "Health" then
    currentColor = "Green"
  elseif Resource == "Rage" then
    currentColor = "Red"
  elseif Resource == "Cooldown" then
    currentColor = "Yellow"
  elseif Resource == "XP" then
    currentColor = "White"
  elseif Resource == "Transition" then
    currentColor = GetCurrentColor_Transtion()
  end
  return currentColor
end
function GetCurrentColor_Transtion()
  local currentColor_Transition
  if currentState == "Health" then
    currentColor_Transition = "Green"
  elseif currentState == "Rage" then
    currentColor_Transition = "Red"
  elseif currentState == "XP" then
    currentColor_Transition = "White"
  end
  return currentColor_Transition
end
