local Srendarr		= _G['Srendarr'] -- grab addon table from global
local L				= Srendarr:GetLocale()
local Aura			= Srendarr.Aura
local DisplayFrame	= {}

-- CONSTS --
local AURA_TYPE_TIMED		= Srendarr.AURA_TYPE_TIMED
local AURA_TYPE_TOGGLED		= Srendarr.AURA_TYPE_TOGGLED
local AURA_TYPE_PASSIVE		= Srendarr.AURA_TYPE_PASSIVE

local AURA_HEIGHT			= Srendarr.AURA_HEIGHT

local AURA_GROW_UP			= Srendarr.AURA_GROW_UP
local AURA_GROW_DOWN		= Srendarr.AURA_GROW_DOWN
local AURA_GROW_LEFT		= Srendarr.AURA_GROW_LEFT
local AURA_GROW_RIGHT		= Srendarr.AURA_GROW_RIGHT
local AURA_GROW_CENTERLEFT	= Srendarr.AURA_GROW_CENTERLEFT
local AURA_GROW_CENTERRIGHT	= Srendarr.AURA_GROW_CENTERRIGHT

local AURA_SORT_NAMEASC		= Srendarr.AURA_SORT_NAMEASC
local AURA_SORT_TIMEASC		= Srendarr.AURA_SORT_TIMEASC
local AURA_SORT_CASTASC		= Srendarr.AURA_SORT_CASTASC
local AURA_SORT_NAMEDESC	= Srendarr.AURA_SORT_NAMEDESC
local AURA_SORT_TIMEDESC	= Srendarr.AURA_SORT_TIMEDESC
local AURA_SORT_CASTDESC	= Srendarr.AURA_SORT_CASTDESC


-- UPVALUES --
local round					= zo_round
local tsort					= table.sort
local tinsert				= table.insert
local tremove				= table.remove
local AddControl			= Srendarr.AddControl


-- ------------------------
-- SORTING FUNCTIONS
-- ------------------------
local function SortAurasByTimeAsc(a, b)
	if (a.auraType == AURA_TYPE_TIMED and b.auraType == AURA_TYPE_TIMED) then -- timed auras, sort by time
		return a.finish > b.finish
	elseif (a.auraType == b.auraType) then -- non-timed auras, same type, sort by name
		return a.auraName < b.auraName
	else -- non-timed auras, different types, sort by type
		return a.auraType > b.auraType
	end
end

local function SortAurasByNameAsc(a, b)
	return a.auraName < b.auraName
end

local function SortAurasByCastAsc(a, b)
	return a.start < b.start
end

local function SortAurasByTimeDesc(a, b)
	if (a.auraType == AURA_TYPE_TIMED and b.auraType == AURA_TYPE_TIMED) then -- timed auras, sort by time
		return a.finish < b.finish
	elseif (a.auraType == b.auraType) then -- non-timed auras, same type, sort by name
		return a.auraName > b.auraName
	else -- non-timed auras, different types, sort by type
		return a.auraType < b.auraType
	end
end

local function SortAurasByNameDesc(a, b)
	return a.auraName > b.auraName
end

local function SortAurasByCastDesc(a, b)
	return a.start > b.start
end


-- ------------------------
-- DISPLAY FRAME
-- ------------------------
function DisplayFrame:Create(id, point, x, y, alpha, scale)
	local df = WINDOW_MANAGER:CreateControl(nil, GuiRoot, CT_TOPLEVELCONTROL)
	df:SetKeyboardEnabled(false)
	df:SetMouseEnabled(false)
	df:SetMovable(false)
	df:SetClampedToScreen(true)
	df:SetDimensions(AURA_HEIGHT, AURA_HEIGHT)
	df:SetAnchor(point, GuiRoot, point, x, y)
	df:SetAlpha(alpha)	-- both values, if configured after load, are done directly)
	df:SetScale(scale)	-- both values, if configured after load, are done directly)

	df.displayAlpha	= alpha
	df.displayID	= id

	df:SetHandler('OnReceiveDrag', function(f)
		f:StartMoving()
	end)
	df:SetHandler('OnMouseUp', function(f)
		f:StopMovingOrResizing()
		local point, x, y = Srendarr:GetEdgeRelativePosition(f)
		Srendarr.db.displayFrames[f.displayID].base.point	= point
		Srendarr.db.displayFrames[f.displayID].base.x		= x
		Srendarr.db.displayFrames[f.displayID].base.y		= y
	end)

	df.aurasActive				= {}
	df.aurasInactive			= {}
	df.aurasSorted				= {}
	df.settings					= Srendarr.db.displayFrames[id]

	-- aura handling
	df['AddAuraToDisplay']		= DisplayFrame.AddAuraToDisplay
	df['OnAuraReleased']		= DisplayFrame.OnAuraReleased
	df['ConfigureAssignedAuras']= DisplayFrame.ConfigureAssignedAuras
	-- configuration
	df['Configure']				= DisplayFrame.Configure
--	df['UpdateDisplay']			= [Set By Configure]
	-- drag overlay
	df['EnableDragOverlay']		= DisplayFrame.EnableDragOverlay
	df['DisableDragOverlay']	= DisplayFrame.DisableDragOverlay
	df['ConfigureDragOverlay']	= DisplayFrame.ConfigureDragOverlay

	return df
end


-- ------------------------
-- AURA HANDLING
-- ------------------------
function DisplayFrame:AddAuraToDisplay(flagBurst, auraGroup, auraType, auraName, unitTag, start, finish, icon, effectType, abilityType, abilityID)
	local aura = tremove(self.aurasInactive, 1) -- grab an aura from inactive pool (if any)

	if (not aura) then -- no inactive auras, create one
		aura = Srendarr.Aura:Create(self)
		aura:Configure(self.settings)
	end

	aura:Initialize(auraGroup, auraType, auraName, unitTag, start, finish, icon, effectType, abilityType, abilityID)

	self.aurasActive[aura.auraID] = aura

	tinsert(self.aurasSorted, aura)

	if (not flagBurst) then
		self:UpdateDisplay()
	end
end

function DisplayFrame:OnAuraReleased(flagBurst, aura)
	if (self.aurasActive[aura.auraID]) then -- aura is displayed here, remove it
		self.aurasActive[aura.auraID] = nil

		tinsert(self.aurasInactive, aura)

		for i, sortedAura in ipairs(self.aurasSorted) do
			if (sortedAura.auraID == aura.auraID) then
				tremove(self.aurasSorted, i)
				break
			end
		end

		if (not flagBurst) then
			self:UpdateDisplay()
		end
	end
end

function DisplayFrame:ConfigureAssignedAuras()
	for _, aura in pairs(self.aurasActive) do	-- configure all active auras assigned to this display frame
		aura:Configure(self.settings)
		aura:UpdateVisuals()
	end

	for _, aura in pairs(self.aurasInactive) do	-- configure all inactive auras in this display frame's pool
		aura:Configure(self.settings)
	end
end


-- ------------------------
-- CONFIGURATION
-- ------------------------
local function UpdateDisplayCentered(self)
	tsort(self.aurasSorted, self.SortAuras)

	local offsetI = self.displayOffsetX * ((#self.aurasSorted - 1) / 2)

	for i, aura in ipairs(self.aurasSorted) do
		aura:ClearAnchors()
		aura:SetAnchor(CENTER, self, CENTER, offsetI, 0)
		offsetI = offsetI - self.displayOffsetX
	end
end

local function UpdateDisplayDirectional(self)
	tsort(self.aurasSorted, self.SortAuras)

	for i, aura in ipairs(self.aurasSorted) do
		aura:ClearAnchors()
		aura:SetAnchor(self.displayPoint, self, self.displayPoint, self.displayOffsetX * (i - 1), self.displayOffsetY * (i - 1))
	end
end

function DisplayFrame:Configure()
	local db = self.settings

	if (db.auraSort == AURA_SORT_NAMEASC) then
		self['SortAuras'] = SortAurasByNameAsc
	elseif (db.auraSort == AURA_SORT_TIMEASC) then
		self['SortAuras'] = SortAurasByTimeAsc
	elseif (db.auraSort == AURA_SORT_CASTASC) then
		self['SortAuras'] = SortAurasByCastAsc
	elseif (db.auraSort == AURA_SORT_NAMEDESC) then
		self['SortAuras'] = SortAurasByNameDesc
	elseif (db.auraSort == AURA_SORT_TIMEDESC) then
		self['SortAuras'] = SortAurasByTimeDesc
	elseif (db.auraSort == AURA_SORT_CASTDESC) then
		self['SortAuras'] = SortAurasByCastDesc
	end

	if (db.auraGrowth == AURA_GROW_CENTERLEFT or db.auraGrowth == AURA_GROW_CENTERRIGHT) then
		self.displayOffsetX = round((db.auraPadding + AURA_HEIGHT) * ((db.auraGrowth == AURA_GROW_CENTERLEFT) and 1 or -1))
		self['UpdateDisplay'] = UpdateDisplayCentered
	else
		if (db.auraGrowth == AURA_GROW_UP) then
			self.displayPoint = BOTTOM
			self.displayOffsetX = 0
			self.displayOffsetY = round(-1 * (db.auraPadding + AURA_HEIGHT))
		elseif (db.auraGrowth == AURA_GROW_DOWN) then
			self.displayPoint = TOP
			self.displayOffsetX = 0
			self.displayOffsetY = round(db.auraPadding + AURA_HEIGHT)
		elseif (db.auraGrowth == AURA_GROW_LEFT) then
			self.displayPoint = RIGHT
			self.displayOffsetX = round(-1 * (db.auraPadding + AURA_HEIGHT))
			self.displayOffsetY = 0
		else -- AURA_GROW_RIGHT
			self.displayPoint = LEFT
			self.displayOffsetX = round(db.auraPadding + AURA_HEIGHT)
			self.displayOffsetY = 0
		end

		self['UpdateDisplay'] = UpdateDisplayDirectional
	end
end


-- ------------------------
-- DRAG OVERLAY
-- ------------------------
local function OnMouseEnter(self)
	self.dragOverlay.highlight:SetHidden(false)
	InitializeTooltip(InformationTooltip, self, TOP, 0, 4)
	InformationTooltip:AddLine(self.tooltipText, '$(BOLD_FONT)|14',  ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB())
end

local function OnMouseExit(self)
	self.dragOverlay.highlight:SetHidden(true)
	ClearTooltip(InformationTooltip)
end

local function CreateDragOverlay(self)
	local ctrl, anim

	local drag = AddControl(self, CT_TEXTURE, 3)
	drag:SetDimensions(AURA_HEIGHT, AURA_HEIGHT)
	drag:SetAnchor(CENTER)
	drag:SetTexture([[/esoui/art/actionbar/abilityframe64_up.dds]]) -- border (and root)
	-- OVERLAY ICON
	drag.icon, ctrl = AddControl(drag, CT_TEXTURE, 2)
	ctrl:SetDimensions(AURA_HEIGHT, AURA_HEIGHT)
	ctrl:SetAnchor(CENTER)
	ctrl:SetTexture[[/esoui/art/icons/ability_restorationstaff_001.dds]]
	-- MOUSEOVER HIGHLIGHT
	drag.highlight, ctrl = AddControl(drag, CT_TEXTURE, 4)
	ctrl:SetDimensions(AURA_HEIGHT, AURA_HEIGHT)
	ctrl:SetAnchor(CENTER)
	ctrl:SetTexture([[/esoui/art/actionbar/actionslot_toggledon.dds]])
	ctrl:SetColor(1, 0.82, 0)
	ctrl:SetHidden(true)
	-- LABEL
	drag.label, ctrl = AddControl(drag, CT_LABEL, 4)
	ctrl:SetFont('ZoFontWinH1')
	ctrl:SetDimensions(AURA_HEIGHT, AURA_HEIGHT)
	ctrl:SetAnchor(CENTER)
	ctrl:SetVerticalAlignment(1)
	ctrl:SetHorizontalAlignment(1)
	ctrl:SetColor(0.64, 0.52, 0, 1)
	ctrl:SetText(self.displayID)
	-- AURA OUTLINES
	drag.auraOutlines = {}
	for x = 1, 4 do
		drag.auraOutlines[x], ctrl = AddControl(drag, CT_BACKDROP, 1)
		ctrl:SetHeight(AURA_HEIGHT)
		ctrl:SetCenterColor(0, 0.5, 0.7, 0.24 / x)
		ctrl:SetEdgeColor(0, 0.5, 0.7, 0.32 / x)
		ctrl:SetEdgeTexture('', 8, 1, 0)
	end
	-- ANIMATION
	drag.timeline = ANIMATION_MANAGER:CreateTimeline()
	drag.timeline:SetPlaybackType(ANIMATION_PLAYBACK_LOOP, -1)
	anim = drag.timeline:InsertAnimation(ANIMATION_COLOR, drag.label, 0)
	anim:SetDuration(750)
	anim:SetEasingFunction(ZO_LinearEase)
	anim:SetColorValues(0.64, 0.52, 0, 1, 1, 0.82, 0, 1)
    anim = drag.timeline:InsertAnimation(ANIMATION_COLOR, drag.label, 750)
    anim:SetDuration(750)
    anim:SetEasingFunction(ZO_LinearEase)
    anim:SetColorValues(1, 0.82, 0, 1, 0.64, 0.52, 0, 1)

	self:SetHandler('OnMouseEnter', OnMouseEnter)
	self:SetHandler('OnMouseExit',	OnMouseExit)

	self.dragOverlay = drag
end

function DisplayFrame:ConfigureDragOverlay()
	if (not self.dragOverlay) then return end -- no overlay yet, so no need to configure it

	local db	= self.settings
	local drag	= self.dragOverlay
	local outline, offsetI

	local outlineWidth = AURA_HEIGHT
	local point = self.displayPoint
	self.tooltipText = zo_strformat('|cffffff<<Z:1>>|r', L.Group_Displayed_Here)

	local noGroups = true -- check if there are no groups assigned to this frame

	for group, frame in pairs(Srendarr.db.auraGroups) do
		if (frame == self.displayID) then -- this group is being show on this frame
			self.tooltipText = string.format('%s\n%s', self.tooltipText, Srendarr.auraGroupStrings[group])
			noGroups = false
		end
	end

	if (noGroups) then
		self.tooltipText = string.format('%s\n%s', self.tooltipText, L.Group_Displayed_None)
	end

	if (db.auraGrowth == AURA_GROW_CENTERLEFT or db.auraGrowth == AURA_GROW_CENTERRIGHT) then
		offsetI = self.displayOffsetX * 1.5
	elseif (db.auraGrowth == AURA_GROW_UP or db.auraGrowth == AURA_GROW_DOWN) then
		point = (db.barReverse and TOPRIGHT or TOPLEFT)
		outlineWidth = AURA_HEIGHT + 3 + db.barWidth
	end

	for x = 1, 4 do
		outline = drag.auraOutlines[x]
		outline:ClearAnchors()

		if (db.auraGrowth == AURA_GROW_CENTERLEFT or db.auraGrowth == AURA_GROW_CENTERRIGHT) then
			outline:SetAnchor(CENTER, self, CENTER, offsetI, 0)
			offsetI = offsetI - self.displayOffsetX
		else
			outline:SetAnchor(point, self, point, self.displayOffsetX * (x - 1), self.displayOffsetY * (x - 1))
		end

		outline:SetWidth(outlineWidth)
	end
end

function DisplayFrame:EnableDragOverlay()
	if (not self.dragOverlay) then
		CreateDragOverlay(self)
	end

	self:ConfigureDragOverlay()
	self:SetMouseEnabled(true)
	self:SetMovable(true)
	self.dragOverlay:SetHidden(false)
	self.dragOverlay.timeline:PlayFromStart()
end

function DisplayFrame:DisableDragOverlay()
	self:SetMouseEnabled(false)
	self:SetMovable(false)

	if (self.dragOverlay) then
		self.dragOverlay.timeline:Stop()
		self.dragOverlay:SetHidden(true)
	end
end


Srendarr.DisplayFrame = DisplayFrame