local E = select(2, ...) -- Engine
CUI = E
local CO,L,UF,AB,LT,AUR,TT = E:LoadModules("Config", "Locale", "Unitframes", "Actionbars", "Layout", "Auras", "Tooltip")

local _
AUR.E = CreateFrame("Frame")

-- AurasNamespace = AUR

AUR.AURA_SIZE 				= 32 -- X and Y size
AUR.AURA_MARGIN 			= 5 -- Margin between auras in px
AUR.BUFF_PER_ROW 			= 8
AUR.BUFF_NUM_PLAYER 		= 32
AUR.DEBUFF_NUM_PLAYER 		= 16
AUR.AURA_TYPES 				= {"Buff", "Debuff"}

local AuraBorderColorButtonName, AuraBorderColorButton
local function UpdateAuraBorderColor(self, r, g, b)
	AuraBorderColorButtonName = E:GetFullFrameName(self)
	AuraBorderColorButton = _G[AuraBorderColorButtonName]
	
	AuraBorderColorButton.border:SetVertexColor(r,g,b)
end

local function AuraAttributeChanged(button, value, index)
	if value == "index" and button and index then
		button.header = button:GetParent()
		button.unit = button.header:GetAttribute("unit")
		button.filter = button.header:GetAttribute("filter")
		-- if not button.filter then button.filter = _ end
		--local name, rank, icon, count = UnitBuff("player", self.index)
		if E:IsLegionClient() then
			button.AuraName, _, button.AuraTexture, button.AuraCount, button.AuraDType, button.AuraDuration, button.AuraExpirationTime = UnitAura(button.unit, index, button.filter)
		else
			button.AuraName, button.AuraTexture, button.AuraCount, button.AuraDType, button.AuraDuration, button.AuraExpirationTime = UnitAura(button.unit, index, button.filter)
		end
		
		if button.filter == "HARMFUL" then
				UpdateAuraBorderColor(button,0.85,0,0)
				button.border:Show()
			else
				-- button.border:Hide()
				UpdateAuraBorderColor(button,1,1,1)
			end
		
		
		button.time:ClearAllPoints()
		button.time:SetPoint("BOTTOM", button, 'BOTTOM', button.header:GetAttribute("xTimeOffset"), button.header:GetAttribute("yTimeOffset"))
		
		if button.AuraDuration == 0 then
			button.time:SetText("")
		end
		
		
		button:SetScript("OnUpdate", function(self, elapsed)
			self.rangeTimer = self.rangeTimer - elapsed;

			if ( self.rangeTimer <= 0 ) then
				--------------------------------------------------------------
				-- OnUpdate Code BEGIN
				
				if button.AuraDuration ~= 0 and button.AuraExpirationTime ~= nil then
					self.timeLeftBase = E:Round(GetTime() - self.AuraExpirationTime, 2)
					if self.timeLeftBase < 0 then
						self.timeLeftBase = self.timeLeftBase * -1
					end
					
					if self.timeLeftBase > 10 then self.timeLeft = E:FormatTime(self.timeLeftBase, 0); else self.timeLeft = E:FormatTime(self.timeLeftBase, 1); end
					self.time:SetText(self.timeLeft)
				end

				self.auraCount = tonumber(self.AuraCount)
			
				if self.auraCount and self.auraCount <= 1 then self.AuraCount = "" end
				self.count:SetText(self.AuraCount)
				
				self.texture:SetTexture(self.AuraTexture)

				-- OnUpdate Code END
				--------------------------------------------------------------
				self.rangeTimer = 0.02;
			end
		end)
	end
end

local CreateIconFont = "FRIZQT__.TTF"
function AUR:CreateIcon(button)

	-- This method gets called when a new button was created automatically by the "SecureAuraHeaderTemplate" attribute "template".
	-- The template is simply an XML construct with several initial data for the frame.
	-- Right here, we can do all sort of "post-processing" for that frame(s).
	-- @TODO
	-- 		Make the method more dynamic and allow aura bars to be created through it.
	--		Maybe a simple bool?
	
	button.texture = button:CreateTexture(nil, "BORDER")
	button.texture:SetTexCoord(0,1,0,1)
	
	button.texture:SetAllPoints(button)
	E:SkinButtonIcon(button.texture)

	button.count = button:CreateFontString(nil, "ARTWORK")
	E:InitializeFontFrame(button.count, "ARTWORK", CreateIconFont, 14, {1,1,1}, 1, {0,0}, "", 32, 32, button, "BOTTOMRIGHT", {1,1})
	button.count:ClearAllPoints()
	button.count:SetPoint("BOTTOMRIGHT", button, 'BOTTOMRIGHT', 5, -5)

	button.time = button:CreateFontString(nil, "ARTWORK")
	E:InitializeFontFrame(button.time, "ARTWORK", CreateIconFont, 11, {1,0.96,0.41}, 1, {0,-100}, "", 32, 32, button, "CENTER", {1,1})
	button.time:ClearAllPoints()
	
	button.time:SetPoint("BOTTOM", button, 'BOTTOM', 0, -20)
	button:SetScript("OnUpdate", function() end)
	button.rangeTimer = 0

	button.highlight = button:CreateTexture(nil, "HIGHLIGHT")
	button.highlight:SetColorTexture(1, 1, 1, 0.45)

	-- This Script gets called every time a aura changed/was added or removed.
	-- We can use this to update the whole thing and its children.
	button:SetScript("OnAttributeChanged", AuraAttributeChanged)
	button.border:Hide()

	
	-- Unfortunately, we have already created the button. Now, there's a aura that wants to be shown.
	-- But this would happen just when the next "OnAttributeChanged" fires. So we have to force the update.
	-- We'll do this in the UpdateHeaderAuras method.
end

-- Target to store auras in
local auraframes = {}

function AUR:UpdateHeader(header)
	-- By setting the template, everything starts.
	-- The buttons are being created by the secure aura header
	-- To be exact, it reads the set filter and calculates the number of total auras to display.
	-- Based on that, AUR:CreateIcon() gets called by the OnLoad handler of the template.
	-- This way, we can modify the auras however we want to.
	-- We simply need different headers for multiple aura bars.
	-- That said, a frame with the "SecureAuraHeaderTemplate" simply needs to be created. Followed by the SetAttribute("template") or SetAttribute("weaponTemplate")
	-- Maybe XML proves to be not that bad.
	
	header:SetAttribute("consolidateTo", 0)
	header:SetAttribute('weaponTemplate', ("CUIAuraTemplate%d"):format(self.db.size))

	header:SetAttribute("separateOwn", 0)
	header:SetAttribute("sortMethod", "DURATION_REMAINING")
	header:SetAttribute("sortDirection", "ASCENDING")
	
	if header:GetName() == "CUIPlayerBuffs" or header:GetName() == "CUIPlayerDebuffs" then
		header:SetAttribute("maxWraps", 3)
	else
		header:SetAttribute("maxWraps", 1)
	end
	header:SetAttribute("wrapAfter", AUR.BUFF_PER_ROW)

	header:SetAttribute("point", "TOPRIGHT")

		header:SetAttribute("minWidth", 32)
		header:SetAttribute("minHeight", 32)
		header:SetAttribute("xOffset", -35)
		header:SetAttribute("yOffset", 0)
		header:SetAttribute("wrapXOffset", 0)
		header:SetAttribute("wrapYOffset", -50)

	header:SetAttribute("template", ("CUIAuraTemplate%d"):format(self.db.size))

	local unit = header:GetAttribute("unit")
	local filter = header:GetAttribute("filter")
	
	local index = 1
	local child = select(index, header:GetChildren())
	while(child) do
		AUR:CreateIcon(child)
		
		child.auraIndex = index
		child.unit = unit
		
		-- We have to force an update for the current child
		-- Otherwise, there would be an empty box until the next event fires
		AuraAttributeChanged(child, "index")
		
		index = index + 1
		child = select(index, header:GetChildren())
	end
end

local function CreatePlayerAuraHeader(filter)
	local name = "CUIPlayerDebuffs"
	if filter == "HELPFUL" then name = "CUIPlayerBuffs" end
	
	local header = CreateFrame("Frame", name, E.Parent, "SecureAuraHeaderTemplate")
	header:SetClampedToScreen(true)
	header:SetAttribute("unit", "player")
	header:SetAttribute("filter", filter)
	
	RegisterStateDriver(header, "visibility", "[petbattle] hide; show")
	RegisterAttributeDriver(header, "unit", "[vehicleui] vehicle; player")

	if filter == "HELPFUL" then
		header:SetAttribute('consolidateDuration', nil)
		header:SetAttribute("includeWeapons", 1)
	end
	
	header:SetAttribute("xTimeOffset", 0)
	header:SetAttribute("yTimeOffset", -20)
	
	AUR:UpdateHeader(header)
	header:SetSize(300, 90)
	header:Show()

	return header
end

local function CreateUnitAuraHeader(unit, filter)
	local name = "CUIUnitDebuffs"
	if filter == "HELPFUL" then name = "CUIUnitBuffs" end
	
	local header = CreateFrame("Frame", name, E.Parent, "SecureAuraHeaderTemplate")
	header:SetClampedToScreen(true)
	header:SetAttribute("unit", unit)
	header:SetAttribute("filter", filter)
	
	RegisterStateDriver(header, "visibility", "show")
	RegisterAttributeDriver(header, "unit", unit)

	header:SetAttribute('consolidateDuration', nil)
	header:SetAttribute("includeWeapons", 1)
	
	header:SetAttribute("xTimeOffset", 0)
	header:SetAttribute("yTimeOffset", 20)
	
	AUR:UpdateHeader(header)
	header:SetSize(300, 90)

	return header
end

function AUR:InitializeAuras()
	self.BuffFrame = CreatePlayerAuraHeader("HELPFUL")
	self.BuffFrame:SetPoint("TOPRIGHT", E.Parent, "TOPRIGHT", -185, -10)

	E:CreateMover(self.BuffFrame, L["buffs"], "TOPRIGHT")

	self.DebuffFrame = CreatePlayerAuraHeader("HARMFUL")
	self.DebuffFrame:SetPoint("TOPRIGHT", E.Parent, "TOPRIGHT", -185, -95)
	
	E:CreateMover(self.DebuffFrame, L["debuffs"], "BOTTOMRIGHT")
	
	if CO.db.profile.auras.bars.targetBuffs == false then
		self.UnitBuffFrame = CreateUnitAuraHeader("target", "HELPFUL")
		self.UnitBuffFrame:SetPoint("TOPRIGHT", UF.Frames.target, "TOPRIGHT", 0, 65)
	end
	
	if CO.db.profile.auras.bars.targetDebuffs == false then
		self.UnitDebuffFrame = CreateUnitAuraHeader("target", "HARMFUL|PLAYER")
		self.UnitDebuffFrame:SetPoint("TOPRIGHT", UF.Frames.target, "TOPRIGHT", 0, 120)
	end
end

function AUR:Init()
	CO = E:GetModule("Config")
	UF = E:GetModule("Unitframes")
	self.db = CO.db.profile.auras
	
	AUR:InitializeAuras()
end

E:AddModule("Auras", AUR)