--[[
# Element: PrivateAuras

Handles positioning and configuration of private aura frames on player and group.

## Notes

These auras are fully controlled by Blizzard, so the structure of this element is unconventional.
oUF provides a means to create the auras and initiate them into Blizzard's care, but is otherwise
unable to provide customizations other than size and positioning.

## Options

.disableCooldown     - Disables the cooldown spiral (boolean)
.disableCooldownText - Disables the cooldown duration text (boolean)
.size                - Private aura anchor frame size. Defaults to 16 (number)
.width               - Private aura anchor frame width. Takes priority over `size` (number)
.height              - Private aura anchor frame height. Takes priority over `size` (number)
.spacing             - Spacing between each private aura anchor frame. Defaults to 0 (number)
.spacingX            - Horizontal spacing between each private aura anchor frame. Takes priority over `spacing` (number)
.spacingY            - Vertical spacing between each private aura anchor frame. Takes priority over `spacing` (number)
.growthX             - Horizontal growth direction. Defaults to 'RIGHT' (string)
.growthY             - Vertical growth direction. Defaults to 'UP' (string)
.initialAnchor       - Anchor point for the private aura anchor frame. Defaults to 'BOTTOMLEFT' (string)
.num                 - Number of private aura anchor frames to create. Defaults to 6 (number)
.maxCols             - Maximum number of private aura columns before wrapping to a new row. Defaults to element width divided by private aura anchor frame size (number)
.borderScale         - Scale of the private aura border (number?)

## Examples

    -- Position
    local PrivateAuras = CreateFrame('Frame', nil, self)
    PrivateAuras:SetPoint('CENTER', self)
    PrivateAuras:SetSize(60, 30)

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

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

local function CreateAura(element, index)
	-- similar to BuffFramePrivateAuraAnchorTemplate
	local aura = CreateFrame('Frame', nil, element)

	-- each sub-widget just acts as anchor points (frames) and cannot be used to customize things
	-- like texture coordinates or font size
	local icon = CreateFrame('Frame', nil, aura)
	icon:SetAllPoints()
	aura.Icon = icon

	--[[ Callback: PrivateAuras:PostCreateAura(aura, auraIndex)
	Called after a private aura anchor frame has been created.

	* self      - the PrivateAuras element
	* aura      - the private aura anchor frame to be positioned
	* auraIndex - the index of the private aura anchor frame
	--]]
	if(element.PostCreateAura) then element:PostCreateAura(aura, index) end

	return aura
end

local function SetPosition(element, aura, auraIndex)
	-- more or less copied straight from Auras
	local width = element.width or element.size or 16
	local height = element.height or element.size or 16
	local sizeX = width + (element.spacingX or element.spacing or 0)
	local sizeY = height + (element.spacingY or element.spacing or 0)
	local anchor = element.initialAnchor or 'BOTTOMLEFT'
	local growthX = (element.growthX == 'LEFT' and -1) or 1
	local growthY = (element.growthY == 'DOWN' and -1) or 1
	local cols = element.maxCols or math.floor(element:GetWidth() / sizeX + 0.5)

	local col = (auraIndex - 1) % cols
	local row = math.floor((auraIndex - 1) / cols)

	aura:ClearAllPoints()
	aura:SetPoint(anchor, element, anchor, col * sizeX * growthX, row * sizeY * growthY)
end

local function resetAnchors(element)
	if(element.anchors) then
		for _, anchor in next, element.anchors do
			C_UnitAuras.RemovePrivateAuraAnchor(anchor)
		end

		table.wipe(element.anchors)
	end
end

local function resetAnchorsAfterCombat(self)
	resetAnchors(self.PrivateAuras)
	self:UnregisterEvent('PLAYER_REGEN_ENABLED', resetAnchorsAfterCombat)
end

local function Update(self)
	local element = self.PrivateAuras
	if(element.anchors) then
		resetAnchors(element)
	else
		element.anchors = {}
	end

	for index = 1, (element.num or 6) do -- 5 or 6 is what Blizzard creates, so we default to that
		local aura = element[index]
		if(not aura) then
			--[[ Override: PrivateAuras:CreateAura(auraIndex)
			Used to completely override the internal function for creating private aura anchor frames.

			* self      - the PrivateAuras element
			* auraIndex - the index of the private aura anchor frame
			--]]
			aura = (element.CreateAura or CreateAura) (element, index)
			table.insert(element, aura)
		end

		aura:SetSize(element.width or element.size or 16, element.height or element.size or 16)

		--[[ Override: PrivateAuras:SetPosition(aura, auraIndex)
		Used to completely override the internal function for (re-)positioning private aura anchor
		frames. Called when new auras have been created.

		* self      - the PrivateAuras element
		* aura      - the private aura anchor frame to be positioned
		* auraIndex - the index of the private aura anchor frame
		--]]
		do
			(element.SetPosition or SetPosition) (element, aura, index)
		end

		table.insert(element.anchors, C_UnitAuras.AddPrivateAuraAnchor({
			unitToken = element.__owner.unit,
			auraIndex = index,
			parent = aura,
			isContainer = false,
			showCountdownFrame = not element.disableCooldown,
			showCountdownNumbers = not element.disableCooldownText,
			iconInfo = {
				iconWidth = aura:GetWidth(),
				iconHeight = aura:GetHeight(),
				iconAnchor = {
					-- we anchor to sub-widgets of each "aura" frame to make it easier to move
					-- after-the-fact
					point = 'CENTER',
					relativeTo = aura.Icon,
					relativePoint = 'CENTER',
					offsetX = 0,
					offsetY = 0,
				},
				borderScale = element.borderScale,
			},
		}))
	end

	--[[ Callback: PrivateAuras:PostUpdate()
	Called after the element has been updated.

	* self - the PrivateAuras element
	--]]
	if(element.PostUpdate) then element:PostUpdate() end
end

local function Path(self, ...)
	--[[ Override: PrivateAuras:Override()
	Used to completely override the internal function for creating, positioning and registering
	all private auras.

	* self - the PrivateAuras element
	--]]
	if(InCombatLockdown()) then
		self:RegisterEvent('PLAYER_REGEN_ENABLED', Path, true)
	else
		self:UnregisterEvent('PLAYER_REGEN_ENABLED', Path)

		do
			(self.PrivateAuras.Override or Update) (self, ...)
		end
	end
end

local function ForceUpdate(element)
	return Path(element.__owner)
end

local function Disable(self)
	local element = self.PrivateAuras
	if(element and element.anchors) then
		if(InCombatLockdown()) then
			self:RegisterEvent('PLAYER_REGEN_ENABLED', resetAnchorsAfterCombat, true)
		else
			resetAnchors(element)
		end
	end
end

local function Enable(self, unit)
	local element = self.PrivateAuras
	if(element) then
		element.__owner = self
		element.ForceUpdate = ForceUpdate

		return true
	end
end

oUF:AddElement('PrivateAuras', Path, Enable, Disable)
