--[[
# Element: Totem Indicator

Handles the updating and visibility of totems.

## Widget

Totems - A `table` to hold sub-widgets.

## Sub-Widgets

Totem - Any UI widget.

## Sub-Widget Options

.Icon     - A `Texture` representing the totem icon.
.Cooldown - A `Cooldown` representing the duration of the totem.

## Notes

OnEnter and OnLeave script handlers will be set to display a Tooltip if the `Totem` widget is mouse enabled.

## Examples

    local Totems = {}
    for index = 1, 5 do
        -- Position and size of the totem indicator
        local Totem = CreateFrame('Button', nil, self)
        Totem:SetSize(40, 40)
        Totem:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', index * Totem:GetWidth(), 0)

        local Icon = Totem:CreateTexture(nil, 'OVERLAY')
        Icon:SetAllPoints()

        local Cooldown = CreateFrame('Cooldown', nil, Totem, 'CooldownFrameTemplate')

        Totem.Icon = Icon
        Totem.Cooldown = Cooldown

        Totems[index] = Totem
    end

    -- Register with oUF
    self.Totems = Totems
--]]

local _, ns = ...
local oUF = ns.oUF

local GetTime = GetTime
local GameTooltip = GameTooltip
local GetTotemInfo = GetTotemInfo
local GetTotemDuration = GetTotemDuration

local priority = (oUF.myclass == 'SHAMAN' and SHAMAN_TOTEM_PRIORITIES) or STANDARD_TOTEM_PRIORITIES

local function UpdateTooltip(self)
	if GameTooltip:IsForbidden() then return end

	GameTooltip:SetTotem(self:GetID())
end

local function OnEnter(self)
	if GameTooltip:IsForbidden() or not self:IsVisible() then return end

	GameTooltip:SetOwner(self, 'ANCHOR_BOTTOMRIGHT')
	self:UpdateTooltip()
end

local function OnLeave()
	if GameTooltip:IsForbidden() then return end

	GameTooltip:Hide()
end

local function TotemOnUpdate(self, elapsed)
	self.elapsed = (self.elapsed or 0) + elapsed

	if (self.elapsed >= .01) then
		self.elapsed = 0

		local _, _, startTime, expiration = GetTotemInfo(self:GetID())
		local currentTime = GetTime() - startTime

		if currentTime <= 0 or expiration <= 0 then
			self:SetValue(0)
		else
			self:SetValue(1 - (currentTime / expiration))
		end
	end
end

local function UpdateTotem(self, event, slot)
	local element = self.Totems
	if(slot > #element) then return end

	--[[ Callback: Totems:PreUpdate(slot)
	Called before the element has been updated.

	* self - the Totems element
	* slot - the slot of the totem to be updated (number)
	--]]
	if(element.PreUpdate) then element:PreUpdate(slot) end

	local totem, durationObj = element[priority[slot]]
	local haveTotem, name, start, duration, icon = GetTotemInfo(slot) -- slot is the same as totem:GetID()
	if haveTotem then
		if totem.Icon then
			totem.Icon:SetTexture(icon)
		end

		if totem:IsObjectType('StatusBar') then
			totem:SetValue(0)
		end
	end

	if totem.Cooldown then
		if oUF:IsSecretValue(duration) then
			durationObj = GetTotemDuration(slot)
			totem.Cooldown:SetCooldownFromDuration(durationObj)
		elseif start and (duration and duration > 0) then
			totem.Cooldown:SetCooldown(start, duration)
		end
	end

	if totem.SetAlphaFromBoolean then
		totem:SetAlphaFromBoolean(haveTotem, 1, 0)
	else
		totem:SetAlpha(haveTotem and 1 or 0)
	end

	--[[ Callback: Totems:PostUpdate(slot, haveTotem, name, start, duration, icon)
	Called after the element has been updated.

	* self        - the Totems element
	* slot        - the slot of the updated totem (number)
	* haveTotem   - indicates if a totem is present in the given slot (boolean)
	* name        - the name of the totem (string)
	* start       - the value of `GetTime()` when the totem was created (number)
	* duration    - the total duration for which the totem should last (number)
	* icon        - the totem's icon (Texture)
	* durationObj - totem duration ([DurationObject](https://warcraft.wiki.gg/wiki/ScriptObject_DurationObject))
	--]]
	if(element.PostUpdate) then
		return element:PostUpdate(slot, haveTotem, name, start, duration, icon, durationObj)
	end
end

local function Path(self, ...)
	--[[ Override: Totem.Override(self, event, ...)
	Used to completely override the internal update function.

	* self  - the parent object
	* event - the event triggering the update (string)
	* ...   - the arguments accompanying the event
	--]]
	return (self.Totems.Override or UpdateTotem) (self, ...)
end

local function Update(self, event)
	local element = self.Totems

	for i = 1, #element do
		Path(self, event, i)
	end

	if(element.PostUpdateColor) then
		element:PostUpdateColor()
	end
end

local function ForceUpdate(element)
	return Update(element.__owner, 'ForceUpdate')
end

local function Enable(self)
	local element = self.Totems
	if(element) then
		element.__owner = self
		element.ForceUpdate = ForceUpdate

		for i = 1, #element do
			local totem = element[priority[i]]

			totem:SetID(i)

			if totem:IsObjectType('StatusBar') then
				totem:SetScript('OnUpdate', TotemOnUpdate)
			end

			if totem:IsMouseEnabled() then
				totem:SetScript('OnEnter', OnEnter)
				totem:SetScript('OnLeave', OnLeave)

				--[[ Override: Totems[slot]:UpdateTooltip()
				Used to populate the tooltip when the totem is hovered.

				* self - the widget at the given slot index
				--]]
				if(not totem.UpdateTooltip) then
					totem.UpdateTooltip = UpdateTooltip
				end
			end
		end

		element:Show()

		self:RegisterEvent('PLAYER_TOTEM_UPDATE', Path, true)

		return true
	end
end

local function Disable(self)
	local element = self.Totems
	if(element) then
		for i = 1, #element do
			element[i]:Hide()
		end

		element:Hide()

		self:UnregisterEvent('PLAYER_TOTEM_UPDATE', Path)
	end
end

oUF:AddElement('Totems', Update, Enable, Disable)
