local classlib = require("core.class")
local tablex = require("core.tablex")
local consts = require("ui.consts")
local hooks = require("ui.hooks")
local meter = require("ui.meter")
local pickupConsts = require("ui.pickupConsts")
local pickupUtil = require("ui.pickupUtil")
local util = require("ui.util")
local iconConsts = require("ui.iconConsts")
local Pickup = game.Pickup
local UI = game.UI
local MeterCooldown = classlib.Class("MeterCooldown", meter.Meter)
local CooldownStartHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandlePickup(pickupName) and meter:ShouldHandleCreature(creature) then
    meter._state:SendEventToUIFsm("mainHUD", "EVT_COOLDOWN_METER_START", pickupName)
    meter._isCoolingDown = true
    meter:StopReadyAnim()
    if meter._OnCooldownStart ~= nil then
      meter._OnCooldownStart(pickupName)
    end
  end
end
local CooldownChangeHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandlePickup(pickupName) and meter:ShouldHandleCreature(creature) then
    local currTime = meter:SafeGetEventTime(2)
    local totalTime = meter:SafeGetEventTime(3)
    local fillPercent = (totalTime - currTime) / totalTime
    meter:SetPercent(fillPercent)
    if meter._OnCooldownChange ~= nil then
      meter._OnCooldownChange(pickupName)
    end
  end
end
local CooldownEndHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandlePickup(pickupName) and meter:ShouldHandleCreature(creature) then
    local fillGO = meter:GetInstancedFillObject()
    local activateGO = meter:GetActivateAnimObject()
    if fillGO ~= nil then
      UI.Anim(fillGO, consts.AS_Forward, meter._fillAnimName, 0, 1)
      UI.Anim(fillGO, meter._flourishAnimStyleForward, meter._flourishAnimName)
    end
    if activateGO ~= nil then
      UI.Anim(activateGO, consts.AS_Forward, meter._activateAnimName, 0, 0)
    end
    if meter._OnCooldownEnd ~= nil then
      meter._OnCooldownEnd(pickupName)
    end
    meter._state:SendEventToUIFsm("mainHUD", "EVT_COOLDOWN_METER_END", pickupName)
    meter._isCoolingDown = false
  end
end
local ItemAcquiredHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandleCreature(creature) then
    meter:Update(pickupName)
  end
end
local ItemActivatedHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandlePickup(pickupName) and meter:ShouldHandleCreature(creature) then
    meter:InitializeFlourishAnim()
    local activateGO = meter:GetActivateAnimObject()
    if activateGO ~= nil then
      UI.Anim(activateGO, consts.AS_Forward, meter._activateAnimName, 1, 0, 1)
    end
    if meter._OnItemActivated ~= nil then
      meter._OnItemActivated(pickupName)
    end
  end
end
local ItemLostHandler = function(meter)
  local pickupName = Pickup.GetName(UI.GetEventArgInt(1))
  local creature = UI.GetEventSenderGameObject()
  if meter:ShouldHandlePickup(pickupName) and meter:ShouldHandleCreature(creature) then
    local fillGO = meter:GetInstancedFillObject()
    local activateGO = meter:GetActivateAnimObject()
    if meter:get_deactivateOnLoss() then
      meter:InitializeFlourishAnim()
      if fillGO ~= nil then
        UI.Anim(fillGO, consts.AS_Forward, meter._fillAnimName, 0, 1)
      end
      if activateGO ~= nil then
        UI.Anim(activateGO, consts.AS_Forward, meter._activateAnimName, 0, 0)
      end
      meter:Deactivate(true)
    end
    if meter._OnItemLost ~= nil then
      meter._OnItemLost(pickupName)
    end
  end
end
function MeterCooldown:init(state, args)
  assert(state ~= nil, "Attempted to create new MeterCooldown without state!")
  assert(args ~= nil, "Attempted to create new MeterCooldown without args!")
  self._state = state
  self._active = false
  self._isCoolingDown = false
  self._goMeter = nil
  self._goMeterChild = nil
  self._goFill = nil
  self._goFillChild = nil
  self._goActivate = nil
  self._goActivateChild = nil
  self._readyAnimName = args.ReadyAnimName
  self._activateObjectName = args.ActivateObjectName
  self._activateInstanceChildName = args.ActivateInstanceChildName
  self._slotName = args.SlotName
  self._pickupName = args.PickupName
  self._timerMode = args.ListenForTimer
  self._playerOnly = args.PlayerOnly
  self._activateAnimName = util.GetValueWithDefault(args.ActivateAnimName, "")
  self._deactivateOnLoss = util.GetValueWithDefault(args.DeactivateOnLoss, true)
  self._flourishAnimStyleForward = util.GetValueWithDefault(args.FlourishAnimStyle, consts.AS_Forward)
  self._initFlourish = util.GetValueWithDefault(args.FlourishNeedsInit, true)
  self._hookTokens = {}
  self._OnCooldownStart = args.OnCooldownStart
  self._OnCooldownChange = args.OnCooldownChange
  self._OnCooldownEnd = args.OnCooldownEnd
  self._OnItemAcquired = args.OnItemAcquired
  self._OnItemActivated = args.OnItemActivated
  self:SetGameObject(args.MeterObject)
  self:SetupIcon()
  if args.Percent ~= nil then
    self:SetPercent(args.Percent)
  else
    self:SetPercent(1)
  end
end
function MeterCooldown:GetStartChangeEndEventNames()
  if self._timerMode then
    return "EVT_Item_Timer_Start", "EVT_Item_Timer_Change", "EVT_Item_Timer_End"
  else
    return "EVT_Item_Cooldown_Start", "EVT_Item_Cooldown_Change", "EVT_Item_Cooldown_End"
  end
end
function MeterCooldown:Activate(show)
  if not self._active then
    local startEventName, changeEventName, stopEventName
    startEventName, changeEventName, stopEventName = self:GetStartChangeEndEventNames()
    util.InstallEventHooks(hooks, self, {startEventName}, CooldownStartHandler)
    util.InstallEventHooks(hooks, self, {changeEventName}, CooldownChangeHandler)
    util.InstallEventHooks(hooks, self, {stopEventName}, CooldownEndHandler)
    util.InstallEventHooks(hooks, self, {
      "EVT_Item_Acquired"
    }, ItemAcquiredHandler)
    util.InstallEventHooks(hooks, self, {
      "EVT_Item_Activated"
    }, ItemActivatedHandler)
    util.InstallEventHooks(hooks, self, {
      "EVT_Item_Lost"
    }, ItemLostHandler)
    if self:ShouldHandleAnimEndEvent() then
      util.InstallEventHooks(hooks, self, {
        "EVT_Anim_End"
      }, self.AnimEndEventHandler)
    end
    self._active = true
    if show == true then
      self:Show()
    end
  end
end
function MeterCooldown:Deactivate(hide)
  if self._active then
    self._active = false
    local startEventName, changeEventName, stopEventName
    startEventName, changeEventName, stopEventName = self:GetStartChangeEndEventNames()
    util.UninstallEventHooks(hooks, self, {startEventName})
    util.UninstallEventHooks(hooks, self, {changeEventName})
    util.UninstallEventHooks(hooks, self, {stopEventName})
    util.UninstallEventHooks(hooks, self, {
      "EVT_Item_Acquired"
    })
    util.UninstallEventHooks(hooks, self, {
      "EVT_Item_Activated"
    })
    util.UninstallEventHooks(hooks, self, {
      "EVT_Item_Lost"
    })
    if self:ShouldHandleAnimEndEvent() then
      util.UninstallEventHooks(hooks, self, {
        "EVT_Anim_End"
      })
    end
    tablex.Clear(self._hookTokens)
  end
  if hide == true then
    self:Hide()
  end
end
function MeterCooldown:Show()
  self._goMeter:Show()
  if self._goMeterChild ~= nil then
    self._goMeterChild:Show()
  end
  if self._goFill ~= nil then
    self._goFill:Show()
    if self._goFillChild ~= nil then
      self._goFillChild:Show()
    end
  end
  if self._goActivate ~= nil then
    self._goActivate:Show()
    if self._goActivateChild ~= nil then
      self._goActivateChild:Show()
    end
  end
end
function MeterCooldown:Hide()
  self._goMeter:Hide()
end
function MeterCooldown:ShouldHandlePickupInternal(pickupName)
  if self._pickupName == nil then
    return true
  end
  if type(self._pickupName) == "table" then
    for _, name in ipairs(self._pickupName) do
      if name == pickupName then
        return true
      end
    end
    return false
  else
    return self._pickupName == pickupName
  end
end
function MeterCooldown:CheckSlot(pickupName)
  local pickupSlotName = pickupUtil.GetSlotName(pickupName)
  local ret = pickupSlotName == self._slotName
  if self._slotName == pickupConsts.Slot_TalismanCooldown then
    ret = ret or pickupSlotName == pickupConsts.Slot_ArmorTrinket
  end
  return ret
end
function MeterCooldown:ShouldHandlePickup(pickupName)
  local shouldHandle = false
  if pickupUtil.IsValidName(pickupName) and pickupUtil.IsValidSlotName(self._slotName) and self:CheckSlot(pickupName) then
    shouldHandle = self:ShouldHandlePickupInternal(pickupName)
  end
  return shouldHandle
end
function MeterCooldown:GetPickupInSlot(creature)
  if creature ~= nil and pickupUtil.IsValidSlotName(self._slotName) and creature:PickupIsSlotUsed(self._slotName) then
    local pickupInSlot = creature:PickupGetPickupNameInSlot(self._slotName)
    if pickupUtil.IsValidName(pickupInSlot) then
      return pickupInSlot
    end
  end
  return nil
end
function MeterCooldown:Update(pickupName)
  if self:ShouldHandlePickup(pickupName) then
    self:UpdateIcon(pickupName)
    self:InitializeFlourishAnim()
    local childObj = self:GetInstancedChildObject()
    if self:IsCloseEnoughFull() then
      self:StartReadyAnim()
    end
    if self._OnItemAcquired ~= nil then
      self._OnItemAcquired(pickupName)
    end
  end
end
function MeterCooldown:StartReadyAnim()
  if not util.IsStringNilOrEmpty(self._readyAnimName) then
    local fillObj = self:GetInstancedFillObject()
    if fillObj ~= nil then
      UI.Anim(fillObj, consts.AS_ForwardCycle_NoReset, self._readyAnimName, 1)
    end
  end
end
function MeterCooldown:StopReadyAnim()
  if not util.IsStringNilOrEmpty(self._readyAnimName) then
    local fillObj = self:GetInstancedFillObject()
    if fillObj ~= nil then
      UI.Anim(fillObj, consts.AS_Forward, self._readyAnimName, 0, 0)
    end
  end
end
function MeterCooldown:InitializeFlourishAnim()
  if not self._initFlourish then
    return
  end
  local fillGO = self:GetInstancedFillObject()
  if fillGO ~= nil then
    UI.Anim(fillGO, consts.AS_Forward, self._flourishAnimName, 0, 0)
  end
end
function MeterCooldown:SafeGetEventTime(index)
  if self._timerMode then
    return UI.GetEventArgInt(index) / 100
  end
  return UI.GetEventArgFloat(index)
end
function MeterCooldown:get_isCoolingDown()
  return self._isCoolingDown
end
function MeterCooldown:get_deactivateOnLoss()
  return self._deactivateOnLoss
end
function MeterCooldown:SetupIcon()
  self:UpdateIcon(nil)
end
function MeterCooldown:UpdateIcon(pickupName)
  local goChild = self:GetInstancedChildObject()
  local thIconText = util.GetTextHandle(goChild, "icon_text")
  if thIconText ~= nil then
    if self._slotName == pickupConsts.Slot_TalismanCooldown then
      pickupName = pickupUtil.GetPickupNameInSlot(game.Player.FindPlayer(), pickupConsts.Slot_ArmorTrinket)
    end
    local icon_text
    icon_text = iconConsts.GetIcon(pickupName)
    if icon_text == nil then
      icon_text = ""
    end
    UI.SetText(thIconText, icon_text)
  end
end
function MeterCooldown:SetGameObject(goMeter)
  assert(goMeter ~= nil, "Meter:SetGameObject called with no game object!")
  self._goMeter = goMeter
  if not util.IsStringNilOrEmpty(self._meterInstanceChildName) then
    self._goMeterChild = self._goMeter:FindSingleGOByName(self._meterInstanceChildName)
  end
  if not util.IsStringNilOrEmpty(self._fillObjectName) then
    self._goFill = self._goMeter:FindSingleGOByName(self._fillObjectName)
    if not util.IsStringNilOrEmpty(self._fillInstanceChildName) then
      self._goFillChild = self._goFill:FindSingleGOByName(self._fillInstanceChildName)
    end
  end
  if not util.IsStringNilOrEmpty(self._activateObjectName) then
    self._goActivate = util.GetUiObjByName(self._activateObjectName)
    if not util.IsStringNilOrEmpty(self._activateInstanceChildName) then
      self._goActivateChild = self._goActivate:FindSingleGOByName(self._fillInstanceChildName)
    end
  end
end
function MeterCooldown:GetActivateAnimObject()
  local objToAnimate = self:GetInstancedFillObject()
  if self._goActivate ~= nil then
    objToAnimate = self._goActivate
    if self._goActivateChild ~= nil then
      objToAnimate = self._goActivateChild
    end
  end
  return objToAnimate
end
function MeterCooldown:ShouldHandleCreature(creature)
  if self._playerOnly and creature ~= game.Player.FindPlayer() then
    return false
  end
  return true
end
function MeterCooldown:IsCloseEnoughFull()
  return self._percent > 0.99
end
return {MeterCooldown = MeterCooldown}
