Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
##############################################################################
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--LUA
local unpack        = unpack;
local select        = select;
local pairs         = pairs;
local type          = type;
local rawset        = rawset;
local rawget        = rawget;
local tostring      = tostring;
local error         = error;
local next          = next;
local pcall         = pcall;
local getmetatable  = getmetatable;
local setmetatable  = setmetatable;
local assert        = assert;
--BLIZZARD
local _G            = _G;
local tinsert       = _G.tinsert;
local tremove       = _G.tremove;
local twipe         = _G.wipe;
--STRING
local string        = string;
local upper         = string.upper;
local format        = string.format;
local find          = string.find;
local match         = string.match;
local gsub          = string.gsub;
--MATH
local math          = math;
local floor         = math.floor
local ceil         	= math.ceil
--TABLE
local table         = table;
local tsort         = table.sort;
local tremove       = table.remove;
--[[
##########################################################
GET ADDON DATA
##########################################################
]]--
local SV = select(2, ...)
local oUF_Villain = SV.oUF

assert(oUF_Villain, "SVUI was unable to locate oUF.");

local L = SV.L;
local LSM = LibStub("LibSharedMedia-3.0")
local MOD = SV.SVUnit

if(not MOD) then return end

local CustomAuraFilter,CustomBarFilter;
local AURA_FONT = [[Interface\AddOns\SVUI\assets\fonts\Display.ttf]];
local AURA_FONTSIZE = 11;
local AURA_OUTLINE = "OUTLINE";
local BASIC_TEXTURE = [[Interface\AddOns\SVUI\assets\artwork\Bars\DEFAULT]];
local shadowTex = [[Interface\AddOns\SVUI\assets\artwork\Template\GLOW]];
local counterOffsets = {
	["TOPLEFT"] = {6, 1},
	["TOPRIGHT"] = {-6, 1},
	["BOTTOMLEFT"] = {6, 1},
	["BOTTOMRIGHT"] = {-6, 1},
	["LEFT"] = {6, 1},
	["RIGHT"] = {-6, 1},
	["TOP"] = {0, 0},
	["BOTTOM"] = {0, 0},
}
local textCounterOffsets = {
	["TOPLEFT"] = {"LEFT", "RIGHT", -2, 0},
	["TOPRIGHT"] = {"RIGHT", "LEFT", 2, 0},
	["BOTTOMLEFT"] = {"LEFT", "RIGHT", -2, 0},
	["BOTTOMRIGHT"] = {"RIGHT", "LEFT", 2, 0},
	["LEFT"] = {"LEFT", "RIGHT", -2, 0},
	["RIGHT"] = {"RIGHT", "LEFT", 2, 0},
	["TOP"] = {"RIGHT", "LEFT", 2, 0},
	["BOTTOM"] = {"RIGHT", "LEFT", 2, 0},
}
--[[
##########################################################
LOCAL FUNCTIONS
##########################################################
]]--
local AuraIcon_OnClick = function(self)
	if not IsShiftKeyDown() then return end
	local name = self.name;
	if name then
		SV:AddonMessage((L["The spell '%s' has been added to the Blocked unitframe aura filter."]):format(name))
		SV.filters["Blocked"][name] = {["enable"] = true, ["priority"] = 0}
		MOD:RefreshUnitFrames()
	end
end

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

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

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

local Aura_UpdateTooltip = function(self)
	GameTooltip:SetUnitAura(self.parent.__owner.unit, self:GetID(), self.filter)
end

local CreateAuraIcon = function(icons, index)
	local aura = CreateFrame("Button", nil, icons)
	aura:RemoveTextures()
	aura:EnableMouse(true)
	aura:RegisterForClicks('RightButtonUp')

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

	aura:SetBackdrop({
    	bgFile = [[Interface\BUTTONS\WHITE8X8]],
		tile = false,
		tileSize = 0,
		edgeFile = [[Interface\BUTTONS\WHITE8X8]],
        edgeSize = 2,
        insets = {
            left = 0,
            right = 0,
            top = 0,
            bottom = 0
        }
    })
    aura:SetBackdropColor(0, 0, 0, 0)
    aura:SetBackdropBorderColor(0, 0, 0)

	local cd = CreateFrame("Cooldown", nil, aura, "CooldownFrameTemplate");
	cd:FillInner(aura, 1, 1);
	cd.noOCC = true;
	cd.noCooldownCount = true;
	cd:SetReverse(true);
	cd:SetHideCountdownNumbers(true);

	local text = cd:CreateFontString(nil, 'OVERLAY');
	text:SetFontObject(NumberFontNormal);
	text:SetPoint('CENTER', aura, 'CENTER', 1, 1);
	text:SetJustifyH('CENTER');

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

	local icon = aura:CreateTexture(nil, "BORDER");
	icon:SetAllPoints(aura);
	icon:FillInner(aura, 1, 1);
    icon:SetTexCoord(0.1, 0.9, 0.1, 0.9);

	local overlay = aura:CreateTexture(nil, "OVERLAY");
	overlay:SetAllPoints(aura);
	overlay:SetTexture(BASIC_TEXTURE);
	overlay:SetVertexColor(0, 0, 0);
	overlay:Hide();

	-- local stealable = aura:CreateTexture(nil, 'OVERLAY')
	-- stealable:SetTexture(0,0,0,0)
	-- stealable:SetPoint('TOPLEFT', -3, 3)
	-- stealable:SetPoint('BOTTOMRIGHT', 3, -3)
	-- aura.stealable = stealable

	aura.UpdateTooltip = Aura_UpdateTooltip;
	aura:SetScript("OnEnter", AuraIcon_OnEnter);
	aura:SetScript("OnLeave", AuraIcon_OnLeave);
	aura:SetScript("OnClick", AuraIcon_OnClick);

	tinsert(icons, aura);

	aura.parent = icons;
	aura.cd = cd;
	aura.text = text;
	aura.icon = icon;
	aura.count = count;
	aura.overlay = overlay;

	return aura
end

local UpdateAuraTimer = function(self, elapsed)
	self.expiration = self.expiration - elapsed;
	if(self.nextUpdate > 0) then
		self.nextUpdate = self.nextUpdate - elapsed;
	elseif(self.expiration <= 0) then
		self:SetScript("OnUpdate", nil)
		self.text:SetText('')
	else
		local expires = self.expiration;
		local calc, timeLeft = 0, 0;
		local timeFormat;
		if expires < 60 then
			if expires >= 4 then
				timeLeft = floor(expires)
				timeFormat = "|cffffff00%d|r"
				self.nextUpdate = 0.51
			else
				timeLeft = expires
				timeFormat = "|cffff0000%.1f|r"
				self.nextUpdate = 0.051
			end
		elseif expires < 3600 then
			timeFormat = "|cffffffff%dm|r"
			timeLeft = ceil(expires  /  60);
			calc = floor((expires  /  60)  +  .5);
			self.nextUpdate = calc > 1 and ((expires - calc)  *  29.5) or (expires - 59.5);
		elseif expires < 86400 then
			timeFormat = "|cff66ffff%dh|r"
			timeLeft = ceil(expires  /  3600);
			calc = floor((expires  /  3600)  +  .5);
			self.nextUpdate = calc > 1 and ((expires - calc)  *  1799.5) or (expires - 3570);
		else
			timeFormat = "|cff6666ff%dd|r"
			timeLeft = ceil(expires  /  86400);
			calc = floor((expires  /  86400)  +  .5);
			self.nextUpdate = calc > 1 and ((expires - calc)  *  43199.5) or (expires - 86400);
		end

		self.text:SetFormattedText(timeFormat, timeLeft)
	end
end

local PostUpdateAuraIcon = function(self, unit, button, index, offset)
	local name, _, _, _, dtype, duration, expiration, _, isStealable = UnitAura(unit, index, button.filter)
	local isFriend = (UnitIsFriend('player', unit) == 1) and true or false
	if button.isDebuff then
		if(not isFriend and button.owner and button.owner ~= "player" and button.owner ~= "vehicle") then
			button:SetBackdropBorderColor(0.9, 0.1, 0.1, 1)
			button.icon:SetDesaturated((unit and not unit:find('arena%d')) and true or false)
		else
			local color = DebuffTypeColor[dtype] or DebuffTypeColor.none
			if (name == "Unstable Affliction" or name == "Vampiric Touch") and SV.class ~= "WARLOCK" then
				button:SetBackdropBorderColor(0.05, 0.85, 0.94, 1)
			else
				button:SetBackdropBorderColor(color.r * 0.6, color.g * 0.6, color.b * 0.6, 1)
			end
			button.icon:SetDesaturated(false)
		end
	else
		if (isStealable) and not isFriend then
			button:SetBackdropBorderColor(0.92, 0.91, 0.55, 1)
		else
			button:SetBackdropBorderColor(0, 0, 0, 1)
		end
	end

	local size = button:GetParent().size
	if size then
		button:Size(size)
	end

	button.spell = name
	button.isStealable = isStealable
	if(expiration and duration ~= 0) then
		if(not button:GetScript('OnUpdate')) then
			button.expirationTime = expiration
			button.expiration = expiration - GetTime()
			button.nextUpdate = -1
			button:SetScript('OnUpdate', UpdateAuraTimer)
		elseif(button.expirationTime ~= expiration) then
			button.expirationTime = expiration
			button.expiration = expiration - GetTime()
			button.nextUpdate = -1
		end
	end
	if((duration == 0) or (expiration == 0)) then
		button:SetScript('OnUpdate', nil)
		button.text:SetText('')
	end
end


--[[ AURABAR HANDLERS ]]--

local AuraBar_OnClick = function(self)
	if not IsShiftKeyDown() then return end
	local name = self:GetParent().aura.name
	if name then
		SV:AddonMessage((L["The spell '%s' has been added to the Blocked unitframe aura filter."]):format(name))
		SV.filters["Blocked"][name] = {["enable"] = true, ["priority"] = 0}
		MOD:RefreshUnitFrames()
	end
end

local PostCreateAuraBars = function(self)
	self.iconHolder:RegisterForClicks("RightButtonUp")
	self.iconHolder:SetScript("OnClick", AuraBar_OnClick)
end

local ColorizeAuraBars = function(self)
	local bars = self.bars;
	for i = 1, #bars do
		local auraBar = bars[i]
		if not auraBar:IsVisible()then break end
		local color
		local spellName = auraBar.statusBar.aura.name;
		local spellID = auraBar.statusBar.aura.spellID;
		if(SV.filters["Defense"][spellName]) then
			color = oUF_Villain.colors.shield_bars
		elseif(SV.db.media.unitframes.spellcolor[spellName]) then
			color = SV.db.media.unitframes.spellcolor[spellName]
		end
		if color then
			auraBar.statusBar:SetStatusBarColor(unpack(color))
			auraBar:SetBackdropColor(color[1] * 0.25, color[2] * 0.25, color[3] * 0.25, 0.5)
		else
			local r, g, b = auraBar.statusBar:GetStatusBarColor()
			auraBar:SetBackdropColor(r * 0.25, g * 0.25, b * 0.25, 0.5)
		end
	end
end

--[[ AURA FILTERING ]]--

do
	local function _test(setting, helpful)
		local friend, enemy = false, false
		if type(setting) == "boolean" then
			friend = setting;
		  	enemy = setting
		elseif setting and type(setting) ~= "string" then
			friend = setting.friendly;
		  	enemy = setting.enemy
		end
		if (friend and helpful) or (enemy and not helpful) then
		  return true;
		end
	  	return false
	end

	CustomAuraFilter = function(self, unit, icon, name, _, _, _, debuffType, duration, _, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossAura)
		local db = SV.db.SVUnit[self.___key]
		local auraType = self.type;
		if(not auraType) then return true end
		if((not db) or (db and not db[auraType])) then
			return false;
		end
		local auraDB = db[auraType]
		local isPlayer = caster == "player" or caster == "vehicle"
		local filtered = true
		local fromPlayer = true;
		local pass = false;
		local friendly = UnitIsFriend("player", unit) == 1 and true or false;
		local isDefensive = false;

		icon.isPlayer = isPlayer;
		icon.owner = caster;
		icon.name = name;
		icon.priority = 0;

		if(SV.filters.Blocked[name] and SV.filters.Blocked[name].enable) then
			filtered = false;
		else
			if(_test(auraDB.filterAllowed, friendly)) then
				local whiteListSpell = SV.filters.Allowed[name]
				if(whiteListSpell and whiteListSpell.enable) then
					filtered = true;
					icon.priority = whiteListSpell.priority;
				end
				pass = true
			end

			if(_test(auraDB.filterPlayer, friendly)) then
				if isPlayer then
					filtered = true
				else
					filtered = false
				end
				fromPlayer = filtered;
				pass = true
			end

			if(_test(auraDB.filterDispellable, friendly)) then
				if (auraType == "buffs" and not isStealable) or (auraType == "debuffs" and debuffType and not MOD.Dispellable[debuffType]) or debuffType == nil then
					filtered = false
				end
				pass = true
			end

			if(_test(auraDB.filterRaid, friendly)) then
				if shouldConsolidate == 1 then filtered = false end
				pass = true
			end

			if(_test(auraDB.filterInfinite, friendly)) then
				if((duration == 0) or (not duration)) then
					filtered = false
				end
				pass = true
			end

			local active = auraDB.useFilter

			if(active and SV.filters[active]) then
				local spellDB = SV.filters[active];
				if(spellDB[name] and spellDB[name].enable and fromPlayer) then
					filtered = true;
					icon.priority = spellDB[name].priority;
				else
					filtered = pass
				end
			end
		end

		if SV.filters.Defense[name] and SV.filters.Defense[name].enable then
			icon.priority = SV.filters.Defense[name].priority;
		end

		return filtered
	end

	CustomBarFilter = function(self, unit, name, _, _, _, debuffType, duration, _, caster, isStealable, shouldConsolidate, spellID)
		local db = SV.db.SVUnit[self.___key]
		if((not db) or (db and not db.aurabar)) then
			return false;
		end
		local auraDB = db.aurabar
		local isPlayer = caster == "player" or caster == "vehicle"
		local filtered = true
		local fromPlayer = true
		local pass = false;
		local friendly = UnitIsFriend("player", unit) == 1 and true or false;

		if(SV.filters.Blocked[name] and SV.filters.Blocked[name].enable) then
			filtered = false;
		else
			if(_test(auraDB.filterAllowed, friendly)) then
				local whiteListSpell = SV.filters.Allowed[name]
				if(whiteListSpell and whiteListSpell.enable) then
					filtered = true;
				end
				pass = true
			end

			if(_test(auraDB.filterPlayer, friendly)) then
				if isPlayer then
					filtered = true
				else
					filtered = false
				end
				fromPlayer = filtered;
				pass = true
			end

			if(_test(auraDB.filterDispellable, friendly)) then
				if (auraType == "buffs" and not isStealable) or (auraType == "debuffs" and debuffType and not MOD.Dispellable[debuffType]) or debuffType == nil then
					filtered = false
				end
				pass = true
			end

			if(_test(auraDB.filterRaid, friendly)) then
				if shouldConsolidate == 1 then filtered = false end
				pass = true
			end

			if(_test(auraDB.filterInfinite, friendly)) then
				if((duration == 0) or (not duration)) then
					filtered = false
				end
				pass = true
			end

			local active = auraDB.useFilter

			if(active and SV.filters[active]) then
				local spellDB = SV.filters[active];
				if(spellDB[name] and spellDB[name].enable and fromPlayer) then
					filtered = true;
				else
					filtered = pass
				end
			end
		end

		return filtered
	end
end
--[[
##########################################################
BUILD FUNCTION
##########################################################
]]--
function MOD:CreateBuffs(frame, unit)
	local aura = CreateFrame("Frame", nil, frame)
	aura.___key = unit
	aura.spacing = 2;
	aura.CreateIcon = CreateAuraIcon;
	aura.PostUpdateIcon = PostUpdateAuraIcon;
	aura.CustomFilter = CustomAuraFilter;
	aura:SetFrameLevel(10)
	aura.type = "buffs"
	aura.textFont = LSM:Fetch("font", SV.db.SVUnit.auraFont)
	aura.textSize = SV.db.SVUnit.auraFontSize
	aura.textOutline = SV.db.SVUnit.auraFontOutline
	return aura
end

function MOD:CreateDebuffs(frame, unit)
	local aura = CreateFrame("Frame", nil, frame)
	aura.___key = unit
	aura.spacing = 2;
	aura.CreateIcon = CreateAuraIcon;
	aura.PostUpdateIcon = PostUpdateAuraIcon;
	aura.CustomFilter = CustomAuraFilter;
	aura.type = "debuffs"
	aura:SetFrameLevel(10)
	aura.textFont = LSM:Fetch("font", SV.db.SVUnit.auraFont)
	aura.textSize = SV.db.SVUnit.auraFontSize
	aura.textOutline = SV.db.SVUnit.auraFontOutline
	return aura
end

function MOD:CreateAuraWatch(frame, unit)
	local aWatch = CreateFrame("Frame", nil, frame)
	aWatch:SetFrameLevel(frame:GetFrameLevel()  +  25)
	aWatch:SetAllPoints(frame)
	aWatch.presentAlpha = 1;
	aWatch.missingAlpha = 0;
	aWatch.strictMatching = true;
	aWatch.icons = {}
	return aWatch
end

function MOD:CreateAuraBarHeader(frame, unitName)
	local auraBarParent = CreateFrame("Frame", nil, frame)
	auraBarParent.parent = frame;
	auraBarParent.PostCreateBar = PostCreateAuraBars;
	auraBarParent.gap = 2;
	auraBarParent.spacing = 1;
	auraBarParent.spark = true;
	auraBarParent.filter = CustomBarFilter;
	auraBarParent.PostUpdate = ColorizeAuraBars;
	auraBarParent.barTexture = LSM:Fetch("statusbar", SV.db.SVUnit.auraBarStatusbar)
	auraBarParent.timeFont = LSM:Fetch("font", "Roboto")
	auraBarParent.textFont = LSM:Fetch("font", SV.db.SVUnit.auraFont)
	auraBarParent.textSize = SV.db.SVUnit.auraFontSize
	auraBarParent.textOutline = SV.db.SVUnit.auraFontOutline
	return auraBarParent
end

function MOD:SmartAuraDisplay()
	local unit = self.unit;
	local db = SV.db.SVUnit[unit];
	if not db or not db.smartAuraDisplay or db.smartAuraDisplay == 'DISABLED' or not UnitExists(unit) then return end
	local buffs = self.Buffs;
	local debuffs = self.Debuffs;
	local bars = self.AuraBars;
	local friendly = UnitIsFriend('player',unit) == 1 and true or false;

	if friendly then
		if db.smartAuraDisplay == 'SHOW_DEBUFFS_ON_FRIENDLIES' then
			buffs:Hide()
			debuffs:Show()
		else
			buffs:Show()
			debuffs:Hide()
		end
	else
		if db.smartAuraDisplay == 'SHOW_DEBUFFS_ON_FRIENDLIES' then
			buffs:Show()
			debuffs:Hide()
		else
			buffs:Hide()
			debuffs:Show()
		end
	end

	if buffs:IsShown() then
		buffs:ClearAllPoints()
		SV:SetReversePoint(buffs, db.buffs.anchorPoint, self, db.buffs.xOffset, db.buffs.yOffset)
		if db.aurabar.attachTo ~= 'FRAME' then
			bars:ClearAllPoints()
			bars:SetPoint('BOTTOMLEFT', buffs, 'TOPLEFT', 0, 1)
			bars:SetPoint('BOTTOMRIGHT', buffs, 'TOPRIGHT', 0, 1)
		end
	end

	if debuffs:IsShown() then
		debuffs:ClearAllPoints()
		SV:SetReversePoint(debuffs, db.debuffs.anchorPoint, self, db.debuffs.xOffset, db.debuffs.yOffset)
		if db.aurabar.attachTo ~= 'FRAME' then
			bars:ClearAllPoints()
			bars:SetPoint('BOTTOMLEFT', debuffs, 'TOPLEFT', 0, 1)
			bars:SetPoint('BOTTOMRIGHT', debuffs, 'TOPRIGHT', 0, 1)
		end
	end
end
--[[
##########################################################
UPDATE
##########################################################
]]--
function MOD:UpdateAuraWatch(frame, key, override)
	local AW = frame.AuraWatch
	if not SV.db.SVUnit[key] then return end
	local db = SV.db.SVUnit[key].auraWatch
	if not db then return end

	if not db.enable then
		AW:Hide()
		return
	else
		AW:Show()
	end

	local WATCH_CACHE

	if key == "pet" and not override then
		local petBW = SV.filters["PetBuffWatch"]
		if(petBW) then
			WATCH_CACHE = {}
			for _, buff in pairs(petBW)do
				if(buff.style == "text") then
					buff.style = "NONE"
				end
				tinsert(WATCH_CACHE, buff)
			end
		end
	else
		local unitBW = SV.filters["BuffWatch"]
		if(unitBW) then
			WATCH_CACHE = {}
			for _, buff in pairs(unitBW)do
				if(buff.style == "text") then
					buff.style = "NONE"
				end
				tinsert(WATCH_CACHE, buff)
			end
		end
	end

	if WATCH_CACHE then
		local fontFile = LSM:Fetch("font", SV.db.SVUnit.auraFont)
		local fontSize = SV.db.SVUnit.auraFontSize
		local fontOutline = SV.db.SVUnit.auraFontOutline

		if AW.icons then
			for i = 1, #AW.icons do
				local iconTest = false;
				for j = 1, #WATCH_CACHE do
					if(#WATCH_CACHE[j].id and #WATCH_CACHE[j].id == AW.icons[i]) then
						iconTest = true;
						break
					end
				end
				if not iconTest then
					AW.icons[i]:Hide()
					AW.icons[i] = nil
				end
			end
		end

		for i = 1, #WATCH_CACHE do
			if WATCH_CACHE[i].id then
				local buffName, _, buffTexture = GetSpellInfo(WATCH_CACHE[i].id)
				if buffName then
					local watchedAura;
					if not AW.icons[WATCH_CACHE[i].id]then
						watchedAura = CreateFrame("Frame", nil, AW)
					else
						watchedAura = AW.icons[WATCH_CACHE[i].id]
					end
					watchedAura.name = buffName;
					watchedAura.image = buffTexture;
					watchedAura.spellID = WATCH_CACHE[i].id;
					watchedAura.anyUnit = WATCH_CACHE[i].anyUnit;
					watchedAura.style = WATCH_CACHE[i].style;
					watchedAura.onlyShowMissing = WATCH_CACHE[i].onlyShowMissing;
					watchedAura.presentAlpha = watchedAura.onlyShowMissing and 0 or 1;
					watchedAura.missingAlpha = watchedAura.onlyShowMissing and 1 or 0;
					watchedAura.textThreshold = WATCH_CACHE[i].textThreshold or -1;
					watchedAura.displayText = WATCH_CACHE[i].displayText;
					watchedAura:Width(db.size)
					watchedAura:Height(db.size)
					watchedAura:ClearAllPoints()

					watchedAura:SetPoint(WATCH_CACHE[i].point, frame.Health, WATCH_CACHE[i].point, WATCH_CACHE[i].xOffset, WATCH_CACHE[i].yOffset)
					if not watchedAura.icon then
						watchedAura.icon = watchedAura:CreateTexture(nil, "BORDER")
						watchedAura.icon:SetAllPoints(watchedAura)
					end
					if not watchedAura.text then
						local awText = CreateFrame("Frame", nil, watchedAura)
						awText:SetFrameLevel(watchedAura:GetFrameLevel() + 50)
						watchedAura.text = awText:CreateFontString(nil, "BORDER")
					end
					if not watchedAura.border then
						watchedAura.border = watchedAura:CreateTexture(nil, "BACKGROUND")
						watchedAura.border:Point("TOPLEFT", -1, 1)
						watchedAura.border:Point("BOTTOMRIGHT", 1, -1)
						watchedAura.border:SetTexture([[Interface\BUTTONS\WHITE8X8]])
						watchedAura.border:SetVertexColor(0, 0, 0)
					end
					if not watchedAura.cd then
						watchedAura.cd = CreateFrame("Cooldown", nil, watchedAura, "CooldownFrameTemplate")
						watchedAura.cd:SetAllPoints(watchedAura)
						watchedAura.cd:SetReverse(true)
						watchedAura.cd:SetHideCountdownNumbers(true)
						watchedAura.cd:SetFrameLevel(watchedAura:GetFrameLevel())
					end
					if watchedAura.style == "coloredIcon"then
						watchedAura.icon:SetTexture([[Interface\BUTTONS\WHITE8X8]])
						if WATCH_CACHE[i]["color"]then
							watchedAura.icon:SetVertexColor(WATCH_CACHE[i]["color"].r, WATCH_CACHE[i]["color"].g, WATCH_CACHE[i]["color"].b)
						else
							watchedAura.icon:SetVertexColor(0.8, 0.8, 0.8)
						end
						watchedAura.icon:Show()
						watchedAura.border:Show()
						watchedAura.cd:SetAlpha(1)
					elseif watchedAura.style == "texturedIcon" then
						watchedAura.icon:SetVertexColor(1, 1, 1)
						watchedAura.icon:SetTexCoord(.18, .82, .18, .82)
						watchedAura.icon:SetTexture(watchedAura.image)
						watchedAura.icon:Show()
						watchedAura.border:Show()
						watchedAura.cd:SetAlpha(1)
					else
						watchedAura.border:Hide()
						watchedAura.icon:Hide()
						watchedAura.cd:SetAlpha(0)
					end
					if watchedAura.displayText then
						watchedAura.text:Show()
						local r, g, b = 1, 1, 1;
						if WATCH_CACHE[i].textColor then
							r, g, b = WATCH_CACHE[i].textColor.r, WATCH_CACHE[i].textColor.g, WATCH_CACHE[i].textColor.b
						end
						watchedAura.text:SetTextColor(r, g, b)
					else
						watchedAura.text:Hide()
					end
					if not watchedAura.count then
						watchedAura.count = watchedAura:CreateFontString(nil, "OVERLAY")
					end
					watchedAura.count:ClearAllPoints()
					if watchedAura.displayText then
						local anchor, relative, x, y = unpack(textCounterOffsets[WATCH_CACHE[i].point])
						watchedAura.count:SetPoint(anchor, watchedAura.text, relative, x, y)
					else
						watchedAura.count:SetPoint("CENTER", unpack(counterOffsets[WATCH_CACHE[i].point]))
					end

					watchedAura.count:SetFont(fontFile, fontSize, fontOutline)
					watchedAura.text:SetFont(fontFile, fontSize, fontOutline)
					watchedAura.text:ClearAllPoints()
					watchedAura.text:SetPoint(WATCH_CACHE[i].point, watchedAura, WATCH_CACHE[i].point)
					if WATCH_CACHE[i].enabled then
						AW.icons[WATCH_CACHE[i].id] = watchedAura;
						if AW.watched then
							AW.watched[WATCH_CACHE[i].id] = watchedAura
						end
					else
						AW.icons[WATCH_CACHE[i].id] = nil;
						if AW.watched then
							AW.watched[WATCH_CACHE[i].id] = nil
						end
						watchedAura:Hide()
						watchedAura = nil
					end
				end
			end
		end

		WATCH_CACHE = nil
	end
	if frame.AuraWatch.Update then
		frame.AuraWatch.Update(frame)
	end
end

function MOD:UpdateGroupAuraWatch(header, override)
	assert(self.Headers[header], "Invalid group specified.")
	local group = self.Headers[header]
	for i = 1, group:GetNumChildren() do
		local frame = select(i, group:GetChildren())
		if frame and frame.Health then self:UpdateAuraWatch(frame, header, override) end
	end
end