Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
	______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
	 _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
	  __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
	   _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
		___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
##############################################################################
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--[[ GLOBALS ]]--
local _G 		= _G;
local unpack 	= _G.unpack;
local select 	= _G.select;
local pairs 	= _G.pairs;
local ipairs 	= _G.ipairs;
local type 		= _G.type;
local tostring 	= _G.tostring;
local tinsert 	= _G.tinsert;
local string 	= _G.string;
--[[ STRING METHODS ]]--
local find, format, upper = string.find, string.format, string.upper;
local match, gsub = string.match, string.gsub;
--[[ MUNGLUNCH's FASTER ASSERT FUNCTION ]]--
local assert = enforce;
--[[ LOCALIZED BLIZZ FUNCTIONS ]]--
local NewHook = hooksecurefunc;
--[[
##########################################################
GET ADDON DATA AND TEST FOR oUF
##########################################################
]]--
local SuperVillain, L = unpack(select(2, ...));
local _, ns = ...
local oUF_SuperVillain = ns.oUF
assert(oUF_SuperVillain, "SVUI was unable to locate oUF.")
--[[
##########################################################
MODULE AND INNER CLASSES
##########################################################
]]--
local MOD = {}
MOD.Units = {}
MOD.Headers = {}
MOD.Construct = {}
MOD.FrameUpdate = {}
MOD.HeaderUpdate = {}
--[[
##########################################################
LOCALS
##########################################################
]]--
local LoadedUnitFrames, LoadedGroupHeaders;
--[[
##########################################################
CORE FUNCTIONS
##########################################################
]]--
do
	local dummy = CreateFrame("Frame", nil)
	dummy:Hide()

	local function deactivate(unitName)
		local frame;
		if type(unitName) == "string" then frame = _G[unitName] else frame = unitName end
		if frame then
			frame:UnregisterAllEvents()
			frame:Hide()
			frame:SetParent(dummy)
			if frame.healthbar then frame.healthbar:UnregisterAllEvents() end
			if frame.manabar then frame.manabar:UnregisterAllEvents() end
			if frame.spellbar then frame.spellbar:UnregisterAllEvents() end
			if frame.powerBarAlt then frame.powerBarAlt:UnregisterAllEvents() end
		end
	end

	function oUF_SuperVillain:DisableBlizzard(unit)
		if (not unit) or InCombatLockdown() then return end

		if (unit == "player") then
			deactivate(PlayerFrame)
			PlayerFrame:RegisterUnitEvent("UNIT_ENTERING_VEHICLE", "player")
			PlayerFrame:RegisterUnitEvent("UNIT_ENTERED_VEHICLE", "player")
			PlayerFrame:RegisterUnitEvent("UNIT_EXITING_VEHICLE", "player")
			PlayerFrame:RegisterUnitEvent("UNIT_EXITED_VEHICLE", "player")
			PlayerFrame:RegisterEvent("PLAYER_ENTERING_WORLD")

			PlayerFrame:SetUserPlaced(true)
			PlayerFrame:SetDontSavePosition(true)
			RuneFrame:SetParent(PlayerFrame)
		elseif(unit == "pet") then
			deactivate(PetFrame)
		elseif(unit == "target") then
			deactivate(TargetFrame)
			deactivate(ComboFrame)
		elseif(unit == "focus") then
			deactivate(FocusFrame)
			deactivate(TargetofFocusFrame)
		elseif(unit == "targettarget") then
			deactivate(TargetFrameToT)
		elseif(unit:match("(boss)%d?$") == "boss") then
		local id = unit:match("boss(%d)")
			if(id) then
				deactivate("Boss"..id.."TargetFrame")
			else
				for i = 1, 4 do
					deactivate(("Boss%dTargetFrame"):format(i))
				end
			end
		elseif(unit:match("(party)%d?$") == "party") then
			local id = unit:match("party(%d)")
			if(id) then
				deactivate("PartyMemberFrame"..id)
			else
				for i = 1, 4 do
					deactivate(("PartyMemberFrame%d"):format(i))
				end
			end
		elseif(unit:match("(arena)%d?$") == "arena") then
			local id = unit:match("arena(%d)")
			if(id) then
				deactivate("ArenaEnemyFrame"..id)
				deactivate("ArenaPrepFrame"..id)
				deactivate("ArenaEnemyFrame"..id.."PetFrame")
			else
				for i = 1, 5 do
					deactivate(("ArenaEnemyFrame%d"):format(i))
					deactivate(("ArenaPrepFrame%d"):format(i))
					deactivate(("ArenaEnemyFrame%dPetFrame"):format(i))
				end
			end
		end
	end
end

local StandardUnitStyle = function(self, unit)
	self:SetScript("OnEnter", UnitFrame_OnEnter)
	self:SetScript("OnLeave", UnitFrame_OnLeave)
	self:SetFrameLevel(2)
	self.unit = unit
	local key = unit:gsub("%d", "")
	self.___key = key
	MOD.Construct[key](self);
	return self
end

local EnemyUnitStyle = function(self, unit)
	self:SetScript("OnEnter", UnitFrame_OnEnter)
	self:SetScript("OnLeave", UnitFrame_OnLeave)
	self:SetFrameLevel(2)
	self.unit = unit
	local index = unit:match("(%d)")
	self.index = index
	self:SetID(index)
	local key = unit:gsub("%d", "")
	self.___key = key
	MOD.Construct[key](self);
	return self
end

function MOD:DetachSubFrames(...)
	for i = 1, select("#", ...) do
		local frame = select(i,...)
		frame:ClearAllPoints()
	end
end

function MOD:AllowElement(unitFrame)
	if InCombatLockdown() then return; end
	if not unitFrame.isForced then
		unitFrame.sourceElement = unitFrame.unit;
		unitFrame.unit = "player"
		unitFrame.isForced = true;
		unitFrame.sourceEvent = unitFrame:GetScript("OnUpdate")
	end

	unitFrame:SetScript("OnUpdate", nil)
	unitFrame.forceShowAuras = true;
	UnregisterUnitWatch(unitFrame)
	RegisterUnitWatch(unitFrame, true)

	unitFrame:Show()
	if unitFrame:IsVisible() and unitFrame.Update then
		unitFrame:Update()
	end
end

function MOD:RestrictElement(unitFrame)
	if(InCombatLockdown() or (not unitFrame.isForced)) then return; end

	unitFrame.forceShowAuras = nil
	unitFrame.isForced = nil

	UnregisterUnitWatch(unitFrame)
	RegisterUnitWatch(unitFrame)

	if unitFrame.sourceEvent then
		unitFrame:SetScript("OnUpdate", unitFrame.sourceEvent)
		unitFrame.sourceEvent = nil
	end

	unitFrame.unit = unitFrame.sourceElement or unitFrame.unit;

	if unitFrame:IsVisible() and unitFrame.Update then
		unitFrame:Update()
	end
end

function MOD:RestrictChildren(parentFrame, ...)
	parentFrame.isForced = nil;

	for i=1,select("#",...) do
		local childFrame = select(i,...)
		childFrame:RegisterForClicks(MOD.db.fastClickTarget and 'AnyDown' or 'AnyUp')
		childFrame.TargetGlow:SetAlpha(1)
		self:RestrictElement(childFrame)
	end
end

function MOD:AllowChildren(parentFrame, ...)
	parentFrame.isForced = true;

	for i=1, select("#", ...) do
		local childFrame = select(i, ...)
		childFrame:RegisterForClicks(nil)
		childFrame:SetID(i)
		childFrame.TargetGlow:SetAlpha(0)
		self:AllowElement(childFrame)
	end
end

function MOD:ResetUnitOptions(unit)
	SuperVillain.db:SetDefault("SVUnit", unit)
	self:RefreshUnitFrames()
end

function MOD:RefreshUnitColors()
	local db = SuperVillain.db.media.unitframes
	for i, setting in pairs(db) do
		if setting and type(setting) == "table" then
			if(setting[1]) then
				oUF_SuperVillain.colors[i] = setting
			else
				local bt = {}
				for x, color in pairs(setting) do
					if(color)then
						bt[x] = color
					end
					oUF_SuperVillain.colors[i] = bt
				end
			end
		elseif setting then
			oUF_SuperVillain.colors[i] = setting
		end
	end
	local r, g, b = db.health[1], db.health[2], db.health[3]
	oUF_SuperVillain.colors.smooth = {1, 0, 0, 1, 1, 0, r, g, b}
end

local RefreshUnitMedia = function(self)
	local db = MOD.db
	if(not (db and db.enable) or not self) then return end
	local CURRENT_BAR_TEXTURE = SuperVillain.Shared:Fetch("statusbar", db.statusbar)
	local CURRENT_AURABAR_TEXTURE = SuperVillain.Shared:Fetch("statusbar", db.auraBarStatusbar);
	local CURRENT_FONT = SuperVillain.Shared:Fetch("font", db.font)
	local CURRENT_AURABAR_FONT = SuperVillain.Shared:Fetch("font", db.auraFont);
	local CURRENT_AURABAR_FONTSIZE = db.auraFontSize
	local CURRENT_AURABAR_FONTOUTLINE = db.auraFontOutline
	local unitDB = db[self.___key]
	if(unitDB and unitDB.enable) then
		local panel = self.InfoPanel
		if(panel) then
			if(panel.Name and unitDB.name) then
				panel.Name:SetFont(SuperVillain.Shared:Fetch("font", unitDB.name.font), unitDB.name.fontSize, unitDB.name.fontOutline)
			end
			if(panel.Health) then
				panel.Health:SetFont(CURRENT_FONT, db.fontSize, db.fontOutline)
			end
			if(panel.Power) then
				panel.Power:SetFont(CURRENT_FONT, db.fontSize, db.fontOutline)
			end
			if(panel.Misc) then
				panel.Misc:SetFont(CURRENT_FONT, db.fontSize, db.fontOutline)
			end
		end
		if(self.Health and (unitDB.health and unitDB.health.enable)) then
			self.Health:SetStatusBarTexture(CURRENT_BAR_TEXTURE)
		end
		if(self.Power and (unitDB.power and unitDB.power.enable)) then
			self.Power:SetStatusBarTexture(CURRENT_BAR_TEXTURE)
		end
		if(self.AuraBars and (unitDB.aurabar and unitDB.aurabar.enable)) then
			local ab = self.AuraBars
			ab.auraBarTexture = CURRENT_AURABAR_TEXTURE
			ab.textFont = CURRENT_AURABAR_FONT
			ab.textSize = db.auraFontSize
			ab.textOutline = db.auraFontOutline
		end
		if(self.Buffs and (unitDB.buffs and unitDB.buffs.enable)) then
			local buffs = self.Buffs
			buffs.textFont = CURRENT_AURABAR_FONT
			buffs.textSize = db.auraFontSize
			buffs.textOutline = db.auraFontOutline
		end
		if(self.Debuffs and (unitDB.debuffs and unitDB.debuffs.enable)) then
			local debuffs = self.Debuffs
			debuffs.textFont = CURRENT_AURABAR_FONT
			debuffs.textSize = db.auraFontSize
			debuffs.textOutline = db.auraFontOutline
		end
		if(self.RaidDebuffs) then
		 	local rdebuffs = self.RaidDebuffs;
            if unitDB.rdebuffs.enable then
                rdebuffs.count:SetFontTemplate(CURRENT_AURABAR_FONT, db.auraFontSize, db.auraFontOutline)
                rdebuffs.time:SetFontTemplate(CURRENT_AURABAR_FONT, db.auraFontSize, db.auraFontOutline)
            end
        end
	end
end

function MOD:RefreshAllUnitMedia()
	if(not self.db or (self.db and self.db.enable ~= true)) then return end
	self:RefreshUnitColors()
	for unit,frame in pairs(self.Units)do
		if self.db[frame.___key].enable then
			frame:MediaUpdate()
		end
	end
	for _,group in pairs(self.Headers) do
		group:MediaUpdate()
	end
	collectgarbage("collect")
end

function MOD:RefreshUnitFrames()
	if SuperVillain.db['SVUnit'].enable~=true then return end
	self:RefreshUnitColors()
	for unit,frame in pairs(self.Units)do
		if self.db[frame.___key].enable then
			frame:Enable()
			frame:Update()
		else
			frame:Disable()
		end
	end
	local _,groupType = IsInInstance()
	local raidDebuffs = ns.oUF_RaidDebuffs or oUF_RaidDebuffs;
	if raidDebuffs then
		raidDebuffs:ResetDebuffData()
		if groupType == "party" or groupType == "raid" then
		  raidDebuffs:RegisterDebuffs(SuperVillain.Filters["Raid"])
		else
		  raidDebuffs:RegisterDebuffs(SuperVillain.Filters["CC"])
		end
	end
	for _,group in pairs(self.Headers) do
		group:Update()
		if group.SetConfigEnvironment then
		  group:SetConfigEnvironment()
		end
	end
	if SuperVillain.db.SVUnit.disableBlizzard then
		oUF_SuperVillain:DisableBlizzard('party')
	end
	collectgarbage("collect")
end

function MOD:SetUnitFrame(unitKey)
	if InCombatLockdown() then self:FrameForge() return end
	local unit = unitKey
	local realName = unit:gsub("(.)", upper, 1)
	realName = realName:gsub("t(arget)", "T%1")
	local frame = self.Units[unit]
	if not frame then
		oUF_SuperVillain:SetActiveStyle("SVUI_UnitFrame")
		frame = oUF_SuperVillain:Spawn(unit, "SVUI_"..realName)
		frame.Update = self.FrameUpdate[unitKey]
		frame.MediaUpdate = RefreshUnitMedia
		self.Units[unit] = frame
	end
	if frame:GetParent() ~= SVUI_UnitFrameParent then
		frame:SetParent(SVUI_UnitFrameParent)
	end
	if self.db[unitKey].enable then
		frame:Enable()
		frame:Update()
	else
		frame:Disable()
	end
end

function MOD:SetEnemyFrames(unitKey, maxCount)
	if InCombatLockdown() then self:FrameForge() return end
	for i = 1, maxCount do
		local unit = unitKey..i
		local realName = unit:gsub("(.)", upper, 1)
		realName = realName:gsub("t(arget)", "T%1")
		local frame = self.Units[unit]
		if not frame then
			oUF_SuperVillain:SetActiveStyle("SVUI_EnemyFrame")
			frame = oUF_SuperVillain:Spawn(unit, "SVUI_"..realName)
			frame.___key = unitKey
			frame.Update = self.FrameUpdate[unitKey]
			frame.MediaUpdate = RefreshUnitMedia
			frame.i = i;
			frame:SetID(i)
			self.Units[unit] = frame
		end
		if frame:GetParent() ~= SVUI_UnitFrameParent then
			frame:SetParent(SVUI_UnitFrameParent)
		end
		if frame.isForced then
			self:AllowElement(frame)
		end
		if self.db[unitKey].enable then
			frame:Enable()
			frame:Update()
		else
			frame:Disable()
		end
	end
end

do
	local _POINTMAP = {
		["DOWN_RIGHT"] = {[1]="TOP",[2]="TOPLEFT",[3]="LEFT",[4]="RIGHT",[5]="LEFT",[6]=1,[7]=-1,[8]=false},
		["DOWN_LEFT"] = {[1]="TOP",[2]="TOPRIGHT",[3]="RIGHT",[4]="LEFT",[5]="RIGHT",[6]=1,[7]=-1,[8]=false},
		["UP_RIGHT"] = {[1]="BOTTOM",[2]="BOTTOMLEFT",[3]="LEFT",[4]="RIGHT",[5]="LEFT",[6]=1,[7]=1,[8]=false},
		["UP_LEFT"] = {[1]="BOTTOM",[2]="BOTTOMRIGHT",[3]="RIGHT",[4]="LEFT",[5]="RIGHT",[6]=-1,[7]=1,[8]=false},
		["RIGHT_DOWN"] = {[1]="LEFT",[2]="TOPLEFT",[3]="TOP",[4]="BOTTOM",[5]="TOP",[6]=1,[7]=-1,[8]=true},
		["RIGHT_UP"] = {[1]="LEFT",[2]="BOTTOMLEFT",[3]="BOTTOM",[4]="TOP",[5]="BOTTOM",[6]=1,[7]=1,[8]=true},
		["LEFT_DOWN"] = {[1]="RIGHT",[2]="TOPRIGHT",[3]="TOP",[4]="BOTTOM",[5]="TOP",[6]=-1,[7]=-1,[8]=true},
		["LEFT_UP"] = {[1]="RIGHT",[2]="BOTTOMRIGHT",[3]="BOTTOM",[4]="TOP",[5]="BOTTOM",[6]=-1,[7]=1,[8]=true},
		["UP"] = {[1]="BOTTOM",[2]="BOTTOM",[3]="BOTTOM",[4]="TOP",[5]="TOP",[6]=1,[7]=1,[8]=false},
		["DOWN"] = {[1]="TOP",[2]="TOP",[3]="TOP",[4]="BOTTOM",[5]="BOTTOM",[6]=1,[7]=1,[8]=false},
		["CUSTOM1"] = {
			['TOPTOP'] = 'UP_RIGHT',
			['BOTTOMBOTTOM'] = 'TOP_RIGHT',
			['LEFTLEFT'] = 'RIGHT_UP',
			['RIGHTRIGHT'] = 'LEFT_UP',
			['RIGHTTOP'] = 'LEFT_DOWN',
			['LEFTTOP'] = 'RIGHT_DOWN',
			['LEFTBOTTOM'] = 'RIGHT_UP',
			['RIGHTBOTTOM'] = 'LEFT_UP',
			['BOTTOMRIGHT'] = 'UP_LEFT',
			['BOTTOMLEFT'] = 'UP_RIGHT',
			['TOPRIGHT'] = 'DOWN_LEFT',
			['TOPLEFT'] = 'DOWN_RIGHT'
		}
	};

	local _GSORT = {
		['CLASS']=function(self)
			self:SetAttribute("groupingOrder","DEATHKNIGHT,DRUID,HUNTER,MAGE,PALADIN,PRIEST,SHAMAN,WARLOCK,WARRIOR,MONK")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'CLASS')
		end,
		['MTMA']=function(self)
			self:SetAttribute("groupingOrder","MAINTANK,MAINASSIST,NONE")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'ROLE')
		end,
		['ROLE']=function(self)
			self:SetAttribute("groupingOrder","TANK,HEALER,DAMAGER,NONE")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'ASSIGNEDROLE')
		end,
		['ROLE_TDH']=function(self)
			self:SetAttribute("groupingOrder","TANK,DAMAGER,HEALER,NONE")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'ASSIGNEDROLE')
		end,
		['ROLE_HTD']=function(self)
			self:SetAttribute("groupingOrder","HEALER,TANK,DAMAGER,NONE")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'ASSIGNEDROLE')
		end,
		['ROLE_HDT']=function(self)
			self:SetAttribute("groupingOrder","HEALER,DAMAGER,TANK,NONE")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",'ASSIGNEDROLE')
		end,
		['NAME']=function(self)
			self:SetAttribute("groupingOrder","1,2,3,4,5,6,7,8")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",nil)
		end,
		['GROUP']=function(self)
			self:SetAttribute("groupingOrder","1,2,3,4,5,6,7,8")
			self:SetAttribute('sortMethod','INDEX')
			self:SetAttribute("sortMethod",'GROUP')
		end,
		['PETNAME']=function(self)
			self:SetAttribute("groupingOrder","1,2,3,4,5,6,7,8")
			self:SetAttribute('sortMethod','NAME')
			self:SetAttribute("sortMethod",nil)
			self:SetAttribute("filterOnPet",true)
		end
	};

	local function dbMapping(self)
		local db = MOD.db[self.___key]
		if(db.showBy == "UP") then
			db.showBy = "UP_RIGHT"
		end
		if(db.showBy == "DOWN") then
			db.showBy = "DOWN_RIGHT"
		end
	end

	local SecureHeaderMediaUpdate = function(self)
		local index = 1;
		local childFrame = self:GetAttribute("child"..index)
		while childFrame do
			RefreshUnitMedia(childFrame)
			index = index + 1;
			childFrame = self:GetAttribute("child"..index)
		end
	end

	local SecureHeaderClear = function(self)
		self:Hide()
		self:SetAttribute("showPlayer", true)
		self:SetAttribute("showSolo", true)
		self:SetAttribute("showParty", true)
		self:SetAttribute("showRaid", true)
		self:SetAttribute("columnSpacing", nil)
		self:SetAttribute("columnAnchorPoint", nil)
		self:SetAttribute("sortMethod", nil)
		self:SetAttribute("groupFilter", nil)
		self:SetAttribute("groupingOrder", nil)
		self:SetAttribute("maxColumns", nil)
		self:SetAttribute("nameList", nil)
		self:SetAttribute("point", nil)
		self:SetAttribute("sortDir", nil)
		self:SetAttribute("sortMethod", "NAME")
		self:SetAttribute("startingIndex", nil)
		self:SetAttribute("strictFiltering", nil)
		self:SetAttribute("unitsPerColumn", nil)
		self:SetAttribute("xOffset", nil)
		self:SetAttribute("yOffset", nil)
	end

	function MOD:SpawnGroupHeader(parentFrame, filter, realName, template1, headerName, template2)
		local db = MOD.db[headerName]
		local selfName = headerName:gsub("(.)", upper, 1)
		oUF_SuperVillain:SetActiveStyle("SVUI_" .. selfName)
		local groupUnit = oUF_SuperVillain:SpawnHeader(realName, template2, nil,
			"oUF-initialConfigFunction", ("self:SetWidth(%d); self:SetHeight(%d); self:SetFrameLevel(5)"):format(db.width, db.height),
			"groupFilter", filter,
			"showParty", true,
			"showRaid", true,
			"showSolo", true,
			template1 and "template", template1
		);
		groupUnit.___key = headerName;
		groupUnit:SetParent(parentFrame)
		groupUnit:Show()
		groupUnit.Update = self.HeaderUpdate[headerName]
		groupUnit.MediaUpdate = SecureHeaderMediaUpdate
		groupUnit.ClearAllAttributes = SecureHeaderClear
		return groupUnit
	end

	local GroupSetConfigEnvironment = function(self)
		local db = MOD.db[self.___key]
		local anchorPoint;
		local widthCalc, heightCalc, xCalc, yCalc = 0, 0, 0, 0;
		local sorting = db.showBy;
		local pointMap = _POINTMAP[sorting]
		local point1, point2, point3, point4, point5, horizontal, vertical, isHorizontal = pointMap[1], pointMap[2], pointMap[3], pointMap[4], pointMap[5], pointMap[6], pointMap[7], pointMap[8];
		for i = 1, db.groupCount do
			local frame = self.subunits[i] --<<
			if frame then
				dbMapping(frame)
				if isHorizontal then
					frame:SetAttribute("xOffset", db.wrapXOffset * horizontal)
					frame:SetAttribute("yOffset", 0)
					frame:SetAttribute("columnSpacing", db.wrapYOffset)
				else
					frame:SetAttribute("xOffset", 0)
					frame:SetAttribute("yOffset", db.wrapYOffset * vertical)
					frame:SetAttribute("columnSpacing", db.wrapXOffset)
				end
				if not frame.isForced then
					if not frame.initialized then
						frame:SetAttribute("startingIndex", db.customSorting and (-min(db.groupCount * db.gRowCol * 5, MAX_RAID_MEMBERS) + 1) or -4)
						frame:Show()
						frame.initialized = true
					end
					frame:SetAttribute("startingIndex", 1)
				end
				frame:ClearAllPoints()
				if db.customSorting and db.invertGroupingOrder then
					frame:SetAttribute("columnAnchorPoint", point4)
				else
					frame:SetAttribute("columnAnchorPoint", point3)
				end
				MOD:DetachSubFrames(frame:GetChildren())
				frame:SetAttribute("point", point1)
				if not frame.isForced then
					frame:SetAttribute("maxColumns", db.customSorting and db.groupCount or 1)
					frame:SetAttribute("unitsPerColumn", db.customSorting and (db.gRowCol * 5) or 5)
					_GSORT[db.sortMethod](frame)
					frame:SetAttribute("sortDir", db.sortDir)
					frame:SetAttribute("showPlayer", db.showPlayer)
				end
				if i == 1 and db.customSorting then
					frame:SetAttribute("groupFilter", "1, 2, 3, 4, 5, 6, 7, 8")
				else
					frame:SetAttribute("groupFilter", tostring(i))
				end
			end
			local anchorPoint = point2
			if db.customSorting and db.startFromCenter then
				anchorPoint = point5
			end
			if (i - 1) % db.gRowCol == 0 then
				if isHorizontal then
					if frame then
						frame:SetPoint(anchorPoint, self, anchorPoint, 0, heightCalc * vertical)
					end
					heightCalc = heightCalc + db.height + db.wrapYOffset;
					yCalc = yCalc + 1
				else
					if frame then
						frame:SetPoint(anchorPoint, self, anchorPoint, widthCalc * horizontal, 0)
					end
					widthCalc = widthCalc + db.width + db.wrapXOffset;
					xCalc = xCalc + 1
				end
			else
				if isHorizontal then
					if yCalc == 1 then
						if frame then
							frame:SetPoint(anchorPoint, self, anchorPoint, widthCalc * horizontal, 0)
						end
						widthCalc = widthCalc + (db.width + db.wrapXOffset) * 5;
						xCalc = xCalc + 1
					elseif frame then
						frame:SetPoint(anchorPoint, self, anchorPoint, (((db.width + db.wrapXOffset) * 5) * ((i - 1) % db.gRowCol)) * horizontal, ((db.height + db.wrapYOffset) * (yCalc - 1)) * vertical)
					end
				else
					if xCalc == 1 then
						if frame then
							frame:SetPoint(anchorPoint, self, anchorPoint, 0, heightCalc * vertical)
						end
						heightCalc = heightCalc + (db.height + db.wrapYOffset) * 5;
						yCalc = yCalc + 1
					elseif frame then
						frame:SetPoint(anchorPoint, self, anchorPoint, ((db.width + db.wrapXOffset) * (xCalc - 1)) * horizontal, (((db.height + db.wrapYOffset) * 5) * ((i - 1) % db.gRowCol)) * vertical)
					end
				end
			end
			if heightCalc == 0 then
				heightCalc = heightCalc + (db.height + db.wrapYOffset) * 5
			elseif widthCalc == 0 then
				widthCalc = widthCalc + (db.width + db.wrapXOffset) * 5
			end
		end
		self:SetSize(widthCalc - db.wrapXOffset, heightCalc - db.wrapYOffset)
	end

	local GroupUpdate = function(group) --<<
		local key = group.___key
		if MOD.db[key].enable ~= true then
			UnregisterAttributeDriver(group, "state-visibility")
			group:Hide()
			return
		end
		for i=1,#group.subunits do
			group.subunits[i]:Update()
		end
	end

	local GroupMediaUpdate = function(group)
		for i=1,#group.subunits do
			group.subunits[i]:MediaUpdate()
		end
	end

	local GroupSetActiveState = function(self)
		if not self.isForced then
			local db = MOD.db[self.___key]
			if(db) then
				for i=1,#self.subunits do
					local self = self.subunits[i]
					if i <= db.groupCount and db.customSorting and i <= 1 or not db.customSorting then
						self:Show()
					else
						if self.forceShow then
							self:Hide()
							MOD:RestrictChildren(self, self:GetChildren())
							self:SetAttribute('startingIndex',1)
						else
							self:ClearAllAttributes()
						end
					end
				end
			end
		end
	end

	function MOD:SetGroupFrame(header, filter, template1, forceUpdate, template2)
		if not self.db[header] then return end
		local db = self.db[header]

		local realName = header:gsub("(.)", upper, 1)

		local frame = self.Headers[header]
		if(not frame) then
			oUF_SuperVillain:RegisterStyle("SVUI_"..realName, function(self, unit)
				self.unit = unit
				self.___key = header
				MOD.Construct[header](self)
				return self
			end)
			oUF_SuperVillain:SetActiveStyle("SVUI_"..realName)

			if(header == "tank" or header == "assist") then
				frame = self:SpawnGroupHeader(SVUI_UnitFrameParent, filter, "SVUI_"..realName, template1, header, template2)
				frame.___key = header
				frame.Update = self.HeaderUpdate[header]
				frame.MediaUpdate = RefreshUnitMedia
			else
				frame = CreateFrame("Frame", "SVUI_"..realName, SVUI_UnitFrameParent, "SecureHandlerStateTemplate")
				frame.subunits = {}
				if db.customSorting then
					if not frame.subunits[1] then
						frame.subunits[1] = self:SpawnGroupHeader(frame, 1, "SVUI_" .. realName .. "Group1", template1, header, template2)
					end
				else
					for i = 1, db.groupCount do
						if(not frame.subunits[i]) then
							frame.subunits[i] = self:SpawnGroupHeader(frame, i, "SVUI_" .. realName .. "Group"..i, template1, header, template2)
						end
					end
				end
				frame.___key = header;
				frame.SetConfigEnvironment = GroupSetConfigEnvironment
				frame.Update = GroupUpdate
				frame.MediaUpdate = GroupMediaUpdate
				frame.SetActiveState = GroupSetActiveState
			end
			frame:Show()
			self.Headers[header] = frame
		end

		if(header == "tank" or header == "assist") then
			frame:Update()
		else
			if(db.enable ~= true and header ~= "raidpet") then
				UnregisterStateDriver(frame, "visibility")
				frame:Hide()
				return
			end

			if frame.SetActiveState then
				frame:SetActiveState()
			end

			if forceUpdate or not frame.Avatar then
				frame:SetConfigEnvironment()
				if not frame.isForced and not frame.blockVisibilityChanges then
					RegisterStateDriver(frame, "visibility", db.visibility)
				end
			else
				frame:SetConfigEnvironment()
				frame:Update()
			end

			if(db.enable ~= true and header == "raidpet") then
				UnregisterStateDriver(frame, "visibility")
				frame:Hide()
				return
			end
		end
	end
end

function MOD:FrameForge()
	if not LoadedUnitFrames then
		self:SetUnitFrame("player")
		self:SetUnitFrame("pet")
		self:SetUnitFrame("pettarget")
		self:SetUnitFrame("target")
		self:SetUnitFrame("targettarget")
		self:SetUnitFrame("focus")
		self:SetUnitFrame("focustarget")
		self:SetEnemyFrames("boss", MAX_BOSS_FRAMES)
		self:SetEnemyFrames("arena", 5)
		LoadedUnitFrames = true;
	end

	if not LoadedGroupHeaders then
		self:SetGroupFrame("raid10")
		self:SetGroupFrame("raid25")
		self:SetGroupFrame("raid40")
		self:SetGroupFrame("raidpet", nil, "SVUI_UNITPET", nil, "SecureGroupPetHeaderTemplate")
		self:SetGroupFrame("party", nil, "SVUI_UNITPET, SVUI_UNITTARGET")
		self:SetGroupFrame("tank", "MAINTANK", "SVUI_UNITTARGET")
		self:SetGroupFrame("assist", "MAINASSIST", "SVUI_UNITTARGET")
		LoadedGroupHeaders = true
	end

	self:UnProtect("FrameForge");
	self:Protect("RefreshUnitFrames");
end

function MOD:KillBlizzardRaidFrames()
	CompactRaidFrameManager:MUNG()
	CompactRaidFrameContainer:MUNG()
	CompactUnitFrameProfiles:MUNG()
	local crfmTest = CompactRaidFrameManager_GetSetting("IsShown")
	if crfmTest and crfmTest ~= "0" then
		CompactRaidFrameManager_SetSetting("IsShown","0")
	end
end

function MOD:PLAYER_REGEN_DISABLED()
	for _,frame in pairs(self.Headers) do
		if frame and frame.forceShow then
			self:UpdateGroupConfig(frame)
		end
	end

	for _,frame in pairs(self.Units) do
		if frame and frame.forceShow then
			self:RestrictElement(frame)
		end
	end
end

function MOD:ADDON_LOADED(event, addon)
	if addon ~= 'Blizzard_ArenaUI' then return end
	oUF_SuperVillain:DisableBlizzard('arena')
	self:UnregisterEvent("ADDON_LOADED")
end

function MOD:PLAYER_ENTERING_WORLD()
	self:RefreshUnitFrames()
	if SuperVillain.class == 'WARLOCK' and not self.QualifiedShards then
		self:QualifyWarlockShards()
		self.QualifiedShards = true
	end
end

local UnitFrameThreatIndicator_Hook = function(unit, unitFrame)
	unitFrame:UnregisterAllEvents()
end
--[[
##########################################################
BUILD FUNCTION / UPDATE
##########################################################
]]--
function MOD:UpdateThisPackage()
	--self:RefreshUnitFrames()
end

function MOD:ConstructThisPackage()
	self:RefreshUnitColors()

	local SVUI_UnitFrameParent = CreateFrame("Frame", "SVUI_UnitFrameParent", SuperVillain.UIParent, "SecureHandlerStateTemplate")
	RegisterStateDriver(SVUI_UnitFrameParent, "visibility", "[petbattle] hide; show")

	oUF_SuperVillain:RegisterStyle("SVUI_UnitFrame", StandardUnitStyle)
	oUF_SuperVillain:RegisterStyle("SVUI_EnemyFrame", EnemyUnitStyle)

	self:Protect("FrameForge", true)
	self:RegisterEvent("PLAYER_ENTERING_WORLD")
	self:RegisterEvent("PLAYER_REGEN_DISABLED")

	if SuperVillain.db.SVUnit.disableBlizzard then
		self:Protect("KillBlizzardRaidFrames", true);
		NewHook("CompactUnitFrame_RegisterEvents", CompactUnitFrame_UnregisterEvents)
		NewHook("UnitFrameThreatIndicator_Initialize", UnitFrameThreatIndicator_Hook)

		InterfaceOptionsFrameCategoriesButton10:SetScale(0.0001)
		InterfaceOptionsFrameCategoriesButton11:SetScale(0.0001)
		InterfaceOptionsStatusTextPanelPlayer:SetScale(0.0001)
		InterfaceOptionsStatusTextPanelTarget:SetScale(0.0001)
		InterfaceOptionsStatusTextPanelParty:SetScale(0.0001)
		InterfaceOptionsStatusTextPanelPet:SetScale(0.0001)
		InterfaceOptionsStatusTextPanelPlayer:SetAlpha(0)
		InterfaceOptionsStatusTextPanelTarget:SetAlpha(0)
		InterfaceOptionsStatusTextPanelParty:SetAlpha(0)
		InterfaceOptionsStatusTextPanelPet:SetAlpha(0)
		InterfaceOptionsCombatPanelEnemyCastBarsOnPortrait:SetAlpha(0)
		InterfaceOptionsCombatPanelEnemyCastBarsOnPortrait:EnableMouse(false)
		InterfaceOptionsCombatPanelTargetOfTarget:SetScale(0.0001)
		InterfaceOptionsCombatPanelTargetOfTarget:SetAlpha(0)
		InterfaceOptionsCombatPanelEnemyCastBarsOnNameplates:ClearAllPoints()
		InterfaceOptionsCombatPanelEnemyCastBarsOnNameplates:SetPoint(InterfaceOptionsCombatPanelEnemyCastBarsOnPortrait:GetPoint())
		InterfaceOptionsDisplayPanelShowAggroPercentage:SetScale(0.0001)
		InterfaceOptionsDisplayPanelShowAggroPercentage:SetAlpha(0)

		if not IsAddOnLoaded("Blizzard_ArenaUI") then
			self:RegisterEvent("ADDON_LOADED")
		else
			oUF_SuperVillain:DisableBlizzard("arena")
		end

		self:RegisterEvent("GROUP_ROSTER_UPDATE", "KillBlizzardRaidFrames")
		UIParent:UnregisterEvent("GROUP_ROSTER_UPDATE")
	else
		CompactUnitFrameProfiles:RegisterEvent("VARIABLES_LOADED")
	end

	local rDebuffs = ns.oUF_RaidDebuffs or oUF_RaidDebuffs;
	if not rDebuffs then return end
	rDebuffs.ShowDispelableDebuff = true;
	rDebuffs.FilterDispellableDebuff = true;
	rDebuffs.MatchBySpellName = true;
end

SuperVillain.Registry:NewPackage(MOD, "SVUnit", "pre");