--[[
	Elements handled: .Auras, .Buffs, .Debuffs
	Notes: .Auras override .Buffs and .Debuffs if they exist on the same frame.

	Shared:
	 - spacing: Padding between aura icons. (Default: 0)
	 - size: Size of the aura icons. (Default: 16)
	 - initialAnchor: Initial anchor in the aura frame. (Default: "BOTTOMLEFT")
	 - onlyShowDuration: Only display icons that have a duration (swirly circle of
	   doom). (Default: nil)
	 - growth-x: Growth direction, affected by initialAnchor. (Default: "UP")
	 - growth-y: Growth direction, affected by initialAnchor. (Default: "RIGHT")
	 - filter
		- 2.x: Expects a boolean. (Default: nil)
		- 3.x: Expects a string with filter. See the UnitAura[1] documentation for
		more information.

	.Auras only:
	 - gap: Adds a empty icon to separate buffs and debuffs. (Default: nil)
	 - numBuffs: The maximum number of buffs that should be shown. (Default: 32)
	 - numDebuffs: The maximum number of debuffs that should be shown. (Default: 40)
	 - buffFilter: See filter on Shared. (Default: "HELPFUL")
	 - debuffFilter: See filter on Shared. (Default: "HARMFUL")
	 - Variables set by .Auras:
		 - visibleBuffs: Number of currently visible buff icons.
		 - visibleDebuffs: Number of currently visible debuff icons.
		 - visibleAuras: Total number of currently visible buffs + debuffs.

	.Buffs only:
	 - num: The maximum number of buffs that should be shown. (Default: 32)
	 - Variables set by .Buffs:
		 - visibleBuffs: Number of currently visible buff icons.

	.Debuffs only:
	 - num: The maximum number of debuffs that should be shown. (Default: 40)
	 - Variables set by .Debuffs:
		 - visibleDebuffs: Number of currently visible debuff icons.

	Functions that can be overridden from within a layout:
	 - :PostCreateAuraIcon(icon, icons, index, isDebuff)
	 - :CreateAuraIcon(icons, index, isDebuff)
	 - :PostUpdateAuraIcon(icons, unit, icon, index, offset, filter, isDebuff)
	 - :PreUpdateAura(event, unit)
	 - :SetAuraPosition(auras, max)
	 - :PostUpdateAura(event, unit)

	[1] http://www.wowwiki.com/API_UnitAura
--]]
local parent = debugstack():match[[\AddOns\(.-)\]]
local global = GetAddOnMetadata(parent, 'X-oUF')
assert(global, 'X-oUF needs to be defined in the parent add-on.')
local oUF = _G[global]

local	select, table_insert, math_floor, UnitDebuff, UnitBuff, GetTime, DebuffTypeColor =
		select, table.insert, math.floor, UnitDebuff, UnitBuff, GetTime, DebuffTypeColor

local total, col, row, size, anchor, button, growthx, growthy, cols, rows, spacing, gap
local auras, buffs, debuffs, mod, max, filter, index, icon

local OnEnter = function(self)
	if(not self:IsVisible()) then return end

	GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT")
	GameTooltip:SetUnitAura(self.frame.unit, self:GetID(), self.filter)
end

local OnLeave = function()
	GameTooltip:Hide()
end

local createAuraIcon = function(self, icons, index, debuff)
	local button = CreateFrame("Frame", nil, icons)
	button:EnableMouse(true)

	button:SetWidth(icons.size or 16)
	button:SetHeight(icons.size or 16)

	local cd = CreateFrame("Cooldown", nil, button)
	cd:SetAllPoints(button)

	local icon = button:CreateTexture(nil, "BACKGROUND")
	icon:SetAllPoints(button)

	local count = button:CreateFontString(nil, "OVERLAY")
	count:SetFontObject(NumberFontNormal)
	count:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -1, 0)

	local overlay = button:CreateTexture(nil, "OVERLAY")
	overlay:SetTexture"Interface\\Buttons\\UI-Debuff-Overlays"
	overlay:SetAllPoints(button)
	overlay:SetTexCoord(.296875, .5703125, 0, .515625)
	button.overlay = overlay

	button:SetScript("OnEnter", OnEnter)
	button:SetScript("OnLeave", OnLeave)

	table_insert(icons, button)

	button.parent = icons
	button.frame = self
	button.debuff = debuff

	button.icon = icon
	button.count = count
	button.cd = cd

	if(self.PostCreateAuraIcon) then self:PostCreateAuraIcon(button, icons, index, debuff) end

	return button
end

local updateIcon = function(self, unit, icons, index, offset, filter, isDebuff, max)
	if(index == 0) then index = max end
	local name, rank, texture, count, dtype, duration, timeLeft = UnitAura(unit, index, filter)

	icon = icons[index + offset]
	if((icons.onlyShowDuration and duration) or (not icons.onlyShowDuration and name)) then
		if(not icon) then icon = (self.CreateAuraIcon and self:CreateAuraIcon(icons, index, isDebuff)) or createAuraIcon(self, icons, index, isDebuff) end

		if(duration and duration > 0) then
			icon.cd:SetCooldown(timeLeft - duration, duration)
			icon.cd:Show()
		else
			icon.cd:Hide()
		end

		if((isDebuff and icons.showDebuffType) or (not isDebuff and icons.showBuffType) or icons.showType) then
			local color = DebuffTypeColor[dtype] or DebuffTypeColor.none

			icon.overlay:SetVertexColor(color.r, color.g, color.b)
			icon.overlay:Show()
			icon.count:SetText((count > 1 and count))
		else
			icon.overlay:Hide()
		end

		icon:Show()
		icon:SetID(index)

		icon.filter = filter
		icon.debuff = isDebuff
		icon.icon:SetTexture(texture)
		icon.count:SetText((count > 1 and count))

		if(self.PostUpdateAuraIcon) then self:PostUpdateAuraIcon(icons, unit, icon, index, offset, filter, isDebuff) end
		return true
	elseif(icon) then
		icon:Hide()
	end
end

function oUF:SetAuraPosition(icons, x)
	if(icons and x > 0) then
		col = 0
		row = 0
		spacing = icons.spacing or 0
		gap = icons.gap
		size = (icons.size or 16) + spacing
		anchor = icons.initialAnchor or "BOTTOMLEFT"
		growthx = (icons["growth-x"] == "LEFT" and -1) or 1
		growthy = (icons["growth-y"] == "DOWN" and -1) or 1
		cols = math_floor(icons:GetWidth() / size + .5)
		rows = math_floor(icons:GetHeight() / size + .5)

		for i = 1, x do
			button = icons[i]
			if(button and button:IsShown()) then
				if(gap and button.debuff) then
					if(col > 0) then
						col = col + 1
					end

					gap = false
				end

				if(col >= cols) then
					col = 0
					row = row + 1
				end
				button:ClearAllPoints()
				button:SetPoint(anchor, icons, anchor, col * size * growthx, row * size * growthy)

				col = col + 1
			end
		end
	end
end

function oUF:UNIT_AURA(event, unit)
	if(self.unit ~= unit) then return end
	if(self.PreUpdateAura) then self:PreUpdateAura(event, unit) end

	auras, buffs, debuffs = self.Auras, self.Buffs, self.Debuffs

	if(auras) then
		buffs = auras.numBuffs or 32
		debuffs = auras.numDebuffs or 40
		max = debuffs + buffs

		local visibleBuffs, visibleDebuffs = 0, 0
		for index = 1, max do
			if(index > buffs) then
				if(updateIcon(self, unit, auras, index % debuffs, buffs, auras.debuffFilter or auras.filter or 'HARMFUL', true, debuffs)) then
					visibleBuffs = visibleBuffs + 1
				end
			else
				if(updateIcon(self, unit, auras, index, 0, auras.buffFilter or  auras.filter or 'HELPFUL')) then
					visibleDebuffs = visibleDebuffs + 1
				end
			end
		end

		auras.visibleBuffs = visibleBuffs
		auras.visibleDebuffs = visibleDebuffs
		auras.visibleAuras = visibleBuffs + visibleDebuffs

		self:SetAuraPosition(auras, max)
	else
		if(buffs) then
			filter = buffs.filter or 'HELPFUL'
			max = buffs.num or 32
			local visibleBuffs = 0
			for index = 1, max do
				if(not updateIcon(self, unit, buffs, index, 0, filter)) then
					max = index - 1

					while(buffs[index]) do
						buffs[index]:Hide()
						index = index + 1
					end
					break
				end

				visibleBuffs = visibleBuffs + 1
			end

			buffs.visibleBuffs = visibleBuffs
			self:SetAuraPosition(buffs, max)
		end
		if(debuffs) then
			filter = debuffs.filter or 'HARMFUL'
			max = debuffs.num or 40
			local visibleDebuffs = 0
			for index = 1, max do
				if(not updateIcon(self, unit, debuffs, index, 0, filter, true)) then
					max = index - 1

					while(debuffs[index]) do
						debuffs[index]:Hide()
						index = index + 1
					end
					break
				end

				visibleDebuffs = visibleDebuffs + 1
			end
			debuffs.visibleDebuffs = visibleDebuffs
			self:SetAuraPosition(debuffs, max)
		end
	end

	if(self.PostUpdateAura) then self:PostUpdateAura(event, unit) end
end

table.insert(oUF.subTypes, function(self)
	if(self.Buffs or self.Debuffs or self.Auras) then
		self:RegisterEvent"UNIT_AURA"
	end
end)

oUF:RegisterSubTypeMapping"UNIT_AURA"
