local XPerl_Raid_Events = {}
local RaidGroupCounts = {0,0,0,0,0,0,0,0,0}
local FrameArray = {}		-- List of raid frames indexed by raid ID
local RaidPositions = {}	-- Back-matching of unit names to raid ID
local ResArray = {}		-- List of currently active resserections in progress
local XUpdated = nil		-- Throttles unit updates to once per frame (for cases where attributes are set many times in 1 frame)

local conf
XPerl_RequestConfig(function(new) conf = new end)

XPERL_RAIDGRP_PREFIX	= "XPerl_Raid_Grp"

-- Hold some raid roster information (AFK, DND etc.)
-- Is also stored between sessions to maintain timers and flags
XPerl_Roster = {}
local Last_Roster = {}

-- Uses some variables from FrameXML\RaidFrame.lua:
-- MAX_RAID_MEMBERS = 40
-- NUM_RAID_GROUPS = 8
-- MEMBERS_PER_RAID_GROUP = 5

-- This array will be built up on the fly whenever we pick up a localized class name that we don't already have.
-- The UnitClass function returns both english and localized versions, so it won't matter where the mod is run.
local ClassNames = {}
function XPerl_Raid_GetLocalisedClassName(class)
	return ClassNames[class]
end

local groups = {"WARRIOR", "MAGE", "PRIEST", "WARLOCK", "DRUID", "ROGUE", "HUNTER", "SHAMAN", "PALADIN"}
local localGroups = {WARRIOR = XPERL_LOC_CLASS_WARRIOR, MAGE = XPERL_LOC_CLASS_MAGE, PRIEST = XPERL_LOC_CLASS_PRIEST, WARLOCK = XPERL_LOC_CLASS_WARLOCK, DRUID = XPERL_LOC_CLASS_DRUID, ROGUE = XPERL_LOC_CLASS_ROGUE, HUNTER = XPERL_LOC_CLASS_HUNTER, SHAMAN = XPERL_LOC_CLASS_SHAMAN, PALADIN = XPERL_LOC_CLASS_PALADIN}
local groupIndex = {WARRIOR = 1, MAGE = 2, PRIEST = 3, WARLOCK = 4, DRUID = 5, ROGUE = 6, HUNTER = 7, SHAMAN = 8, PALADIN = 9}

-- Spell_Holy_Resurrection, Spell_Nature_Regenerate, Spell_Nature_Reincarnation, Spell_Holy_Resurrection
local resSpells  = {["Resurrection"] = true, ["Ancestral Spirit"] = true, ["Rebirth"] = true, ["Redemption"] = true}
if (GetLocale() == "deDE") then
	resSpells = {["Auferstehung"] = true, ["Geist der Ahnen"] = true, ["Wiedergeburt"] = true, ["Erl\195\182sung"] = true}
elseif (GetLocale() == "frFR") then
	resSpells = {["R\195\169surrection"] = true, ["Esprit ancestral"] = true, ["Renaissance"] = true, ["R\195\169demption"] = true}
elseif (GetLocale() == "zhCN") then
	resSpells = {["复活"] = true, ["先祖之魂"] = true, ["复生"] = true, ["救赎"] = true}
elseif (GetLocale() == "zhTW") then
	resSpells = {["復活術"] = true, ["先祖之魂"] = true, ["複生"] = true, ["救贖"] = true}
elseif (GetLocale() == "koKR") then
	resSpells = {["부활"] = true, ["고대의 영혼"] = true, ["환생"] = true, ["구원"] = true}
end

----------------------
-- Loading Function --
----------------------

-- XPerl_Raid_OnLoad
function XPerl_Raid_OnLoad(self)
	local events = {"CHAT_MSG_ADDON",	-- "CHAT_MSG_RAID", "CHAT_MSG_RAID_LEADER", "CHAT_MSG_PARTY",
			"PLAYER_ENTERING_WORLD", "PLAYER_LEAVING_WORLD", "VARIABLES_LOADED", "RAID_ROSTER_UPDATE",
			"UNIT_FACTION", "UNIT_DYNAMIC_FLAGS", "UNIT_FLAGS", "UNIT_AURA", "UNIT_RAGE", "UNIT_MAXRAGE",
			"UNIT_ENERGY", "UNIT_MAXENERGY", "UNIT_MANA", "UNIT_MAXMANA", "UNIT_HEALTH", "UNIT_MAXHEALTH",
			"UNIT_DISPLAYPOWER", "UNIT_NAME_UPDATE", "PLAYER_FLAGS_CHANGED", "UNIT_COMBAT",
			"UNIT_SPELLCAST_START", "UNIT_SPELLCAST_STOP", "UNIT_SPELLCAST_FAILED",
			"UNIT_SPELLCAST_INTERRUPTED"}
	for i,event in pairs(events) do
		self:RegisterEvent(event)
	end

	self.time = 0
	self.Array = {}

	XPerl_RegisterOptionChanger(function()
		XPerl_RaidTitles()
		XPerl_Raid_Set_Bits()
		XPerl_Raid_UpdateDisplayAll()
	end, "Raid")
end

-- Setup1RaidFrame
local function Setup1RaidFrame(self)
	if (conf.RaidMana == 1) then
		if (not InCombatLockdown()) then
			self:SetHeight(44)
		end
		self.statsFrame:SetHeight(30)
		self.statsFrame.manaBar:Show()
	else
		if (not InCombatLockdown()) then
			self:SetHeight(39)
		end
		self.statsFrame:SetHeight(25)
		self.statsFrame.manaBar:Hide()
	end
end

-- ShowPopup
function XPerl_Raid_ShowPopup(self)
	local me = self
	if (not self.nameFrame and self:GetParent().nameFrame == self) then
		me = self:GetParent()
	end

	HideDropDownMenu(1)
	FriendsDropDown.initialize = XPerl_RaidFrameDropDown_Initialize
	FriendsDropDown.displayMode = "MENU"

	-- Must use 'this' for these:
	this.unit = SecureButton_GetUnit(me)
	this.name, this.server = UnitName(this.unit)
	this.id = strmatch(this.unit, "(%d+)")

	ToggleDropDownMenu(1, nil, FriendsDropDown, me.statsFrame:GetName(), 0, 0)

	-- this.unit, this.name, this.server, this.id = nil, nil, nil, nil
end

-- SetFrameArray
local function SetFrameArray(value, self)
	for k,v in pairs(FrameArray) do
		if (v == self) then
			FrameArray[k] = nil
		end
	end

	FrameArray[value] = self
end

-- onAttrChanged
local function onAttrChanged(self, name, value)
	if (name == "unit") then
		if (value) then
			SetFrameArray(value, self)

			if (not XUpdated) then		-- Throttle updates caused by over abundant attribute settings.
				XUpdated = {}
			end
			if (not XUpdated[value]) then
				XUpdated[value] = true

				if (self.lastID ~= value or self.lastName ~= UnitName(value)) then
					XPerl_Raid_UpdateDisplay(self)
				end
			end
		end
	end
end

-- XPerl_Raid_Single_OnLoad
function XPerl_Raid_Single_OnLoad(self)
	XPerl_SetChildMembers(self)
	XPerl_RegisterHighlight(self.highlight, 2)

	XPerl_RegisterPerlFrames(self, {self.nameFrame, self.statsFrame})
	self.FlashFrames = {self.nameFrame, self.statsFrame}
	self:SetScript("OnAttributeChanged", onAttrChanged)

	XPerl_RegisterClickCastFrame(self)
	XPerl_RegisterClickCastFrame(self.nameFrame)
end

-- XPerl_Raid_UpdateName
local function XPerl_Raid_UpdateName(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			self.lastName, self.lastID = nil, nil
			return
		end
	end

	local name = UnitName(partyid)
	self.lastName, self.lastID = name, partyid -- These stored, so we can at least make a small effort in reducing workload on attribute changes.

	if (name) then
                self.nameFrame.text:SetText(name)

		local remCount = 1
		while ((self.nameFrame.text:GetStringWidth() >= (self.nameFrame:GetWidth() - 6)) and (string.len(name) > remCount)) do
			name = string.sub(name, 1, string.len(name) - remCount)..".."
			remCount = 3
			self.nameFrame.text:SetText(name)
		end

		if (self.pet) then
			local color = conf.ColourReactionNone
			self.nameFrame.text:SetTextColor(color.r, color.g, color.b)
		else
			XPerl_ColourFriendlyUnit(self.nameFrame.text, partyid)
		end
	end
end

-- XPerl_Raid_CheckFlags
local function XPerl_Raid_CheckFlags(partyid)

	local unitName = UnitName(partyid)
	local resser

	for i,name in pairs(ResArray) do
		if (name == unitName) then
			resser = i
			break
		end
	end

	if (resser) then
		-- Verify they're dead..
		if (UnitIsDeadOrGhost(partyid)) then
			return {flag = resser..XPERL_RAID_RESSING, bgcolor = {r = 0, g = 0.5, b = 1}}
		end

		ResArray[resser] = nil
	end

	local unitInfo = XPerl_Roster[unitName]
	if (unitInfo) then
		if (unitInfo.ressed) then
			if (UnitIsDead(partyid)) then
				if (unitInfo.ressed == 2) then
					return {flag = XPERL_LOC_SS_AVAILABLE, bgcolor = {r = 0, g = 1, b = 0.5}}
				elseif (unitInfo.ressed == 3) then
					return {flag = XPERL_LOC_ACCEPTEDRES, bgcolor = {r = 0, g = 0.5, b = 1}}
				else
					return {flag = XPERL_LOC_RESURRECTED, bgcolor = {r = 0, g = 0.5, b = 1}}
				end
			else
				unitInfo.ressed = nil
				XPerl_Raid_UpdateManaType(FrameArray[partyid], true)
			end

		elseif (unitInfo.afk) then
			if (UnitIsAFK(partyid)) then
				return {flag = XPERL_RAID_AFK}
			end
			unitInfo.afk = nil

		--elseif (unitInfo.dnd) then
		--	if (UnitIsDND(partyid)) then
		--		return {flag = XPERL_RAID_DND}
		--	end
		--	unitInfo.dnd = nil

		end
	end
end

-- XPerl_Raid_UpdateManaType
function XPerl_Raid_UpdateManaType(self, skipFlags)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
		return
	end

	local flags
	if (not skipFlags) then
		flags = XPerl_Raid_CheckFlags(partyid)
	end
	if (not flags) then
		if (conf.RaidMana == 1) then
			XPerl_SetManaBarType(self)
		end
	end
end

-- XPerl_Raid_ShowFlags
local function XPerl_Raid_ShowFlags(self, flags)
	local r, g, b
	if (flags.bgcolor) then
		r, g, b = flags.bgcolor.r, flags.bgcolor.g, flags.bgcolor.b
	else
		r, g, b = 0.5, 0.5, 0.5
	end

	self.statsFrame.healthBar:SetStatusBarColor(r, g, b)
	self.statsFrame.healthBar.bg:SetVertexColor(r, g, b, 0.5)
	self.statsFrame.manaBar:SetStatusBarColor(r, g, b)
	self.statsFrame.manaBar.bg:SetVertexColor(r, g, b, 0.5)

	if (flags.color) then
		r, g, b = flags.color.r, flags.color.g, flags.color.b
	else
		r, g, b = 1, 1, 1
	end

	self.statsFrame.healthBar.text:SetText(flags.flag)
	self.statsFrame.healthBar.text:SetTextColor(r, g, b)
	self.statsFrame.healthBar.text:Show()
end

-- XPerl_Raid_UpdateHealth
local function XPerl_Raid_UpdateHealth(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
		return
	end

	local health = UnitHealth(partyid)
	local healthmax = UnitHealthMax(partyid)

	if (health > healthmax) then
		-- New glitch with 1.12.1
		if (UnitIsDeadOrGhost(partyid)) then
			health = 0
		else
			health = healthmax
		end
	end

	self.statsFrame.healthBar:SetMinMaxValues(0, healthmax)
	if (conf.InverseBars == 1) then
		self.statsFrame.healthBar:SetValue(healthmax - health)
	else
		self.statsFrame.healthBar:SetValue(health)
	end

	if (conf.ShowRaidPercents == 0) then
		self.statsFrame.healthBar.text:Hide()
	end

	local name = UnitName(partyid)
	local myRoster = XPerl_Roster[name]
	--local feigning
	if (name and UnitIsConnected(partyid)) then
		if (myRoster and myRoster.fd) then
			if (not XPerl_IsFeignDeath(partyid)) then
				myRoster.fd = nil
			end
		end

		local flags = XPerl_Raid_CheckFlags(partyid)
		if (flags) then
			XPerl_Raid_ShowFlags(self, flags)

			if (UnitIsDeadOrGhost(partyid)) then
				self.dead = true
				XPerl_Raid_UpdateName(self)
			end
			return

		elseif (UnitIsDead(partyid) or myRoster and myRoster.fd) then
			if (myRoster and myRoster.fd) then		--XPerl_IsFeignDeath(partyid)) then
				--feigning = true

				XPerl_NoFadeBars(true)
	        		self.statsFrame.healthBar:SetMinMaxValues(0, 1)
	        		self.statsFrame.healthBar:SetValue(0)
				self.statsFrame.healthBar.text:SetText(XPERL_LOC_FEIGNDEATH)
				XPerl_SetSmoothBarColor(self.statsFrame.healthBar, 0)
				XPerl_NoFadeBars()
			else
				self.dead = true
				XPerl_Raid_ShowFlags(self, {flag = XPERL_LOC_DEAD})
				XPerl_Raid_UpdateName(self)
			end

		elseif (UnitIsGhost(partyid)) then
			self.dead = true
			XPerl_Raid_ShowFlags(self, {flag = XPERL_LOC_GHOST})
			XPerl_Raid_UpdateName(self)

		else
			if (self.dead or (myRoster and (myRoster.fd or myRoster.ressed))) then
				XPerl_Raid_UpdateManaType(self, true)
			end
			self.dead = nil

			local percentHp = health / healthmax
			local phealthPct = format("%3.0f", percentHp * 100)

			if (conf.HealerModeRaid == 1) then
				self.statsFrame.healthBar.text:SetText(-(healthmax - health))
			else
				self.statsFrame.healthBar.text:SetText(phealthPct.."%")
			end

			XPerl_SetSmoothBarColor(self.statsFrame.healthBar, percentHp)

			if (myRoster) then
				myRoster.resCount = nil
				if (myRoster.ressed) then
					myRoster.ressed = nil
					XPerl_Raid_UpdateManaType(self, true)
				end
			end
		end
	else
		self.dead = nil
		XPerl_Raid_ShowFlags(self, {flag = XPERL_LOC_OFFLINE})

		if (name and myRoster and not myRoster.offline) then
			myRoster.offline = GetTime()
			myRoster.afk = nil
			myRoster.dnd = nil
		end
	end

	--if (myRoster) then
	--	if (not feigning) then
	--		myRoster.fd = nil
	--	end
	--end
end

-- XPerl_Raid_UpdateMana
local function XPerl_Raid_UpdateMana(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
		return
	end

	local mana = UnitMana(partyid)
	local manamax = UnitManaMax(partyid)

	if (UnitPowerType(partyid) == 0 and not self.pet) then
		local pmanaPct = (mana * 100.0) / manamax
		self.statsFrame.manaBar.text:SetText(format("%3.0f"..PERCENT_SYMBOL, pmanaPct))
	else
		self.statsFrame.manaBar.text:SetText("")
	end

	self.statsFrame.manaBar:SetMinMaxValues(0, manamax)
	self.statsFrame.manaBar:SetValue(mana)
end

-- XPerl_Raid_CombatFlash
local function XPerl_Raid_CombatFlash(self, elapsed, argNew, argGreen)
	if (XPerl_CombatFlashSet (elapsed, self, argNew, argGreen)) then
		XPerl_CombatFlashSetFrames(self)
	end
end

-- XPerl_GetRaidPosition
function XPerl_GetRaidPosition(findName)
	return RaidPositions[findName]
end

-- XPerl_Raid_GetUnitFrameByName
function XPerl_Raid_GetUnitFrameByName(findName)
	local id = RaidPositions[findName]
	if (id) then
		return FrameArray[id]
	end
end

-- UpdateUnitByName
local function UpdateUnitByName(name,flagsOnly)
	local id = RaidPositions[name]
	if (id) then
		local frame = FrameArray[id]
		if (frame and frame:IsShown()) then
			if (flagsOnly) then
				XPerl_Raid_UpdateHealth(frame)
			else
				XPerl_Raid_UpdateDisplay(frame)
			end
		end
	end
end

-- XPerl_Raid_OnUpdate
function XPerl_Raid_OnUpdate(self, elapsed)

	local updateHighlights, someUpdate
	local enemyUnitList
	XUpdated = nil

	self.time = self.time + elapsed
	if (self.time >= 0.2) then
		self.time = 0
		someUpdate = true
	end

	for i,frame in pairs(FrameArray) do
		if (frame:IsShown()) then
			if (frame.PlayerFlash) then
				XPerl_Raid_CombatFlash(frame, arg1, false)
			end

			if (someUpdate) then
				XPerl_UpdateSpellRange(frame)
			end
		end
	end
end

-- XPerl_Raid_HighlightCallback(updateName)
local function XPerl_Raid_HighlightCallback(updateName)
	local id = RaidPositions[updateName]
	if (id) then
		local frame = FrameArray[id]
		if (frame) then
			XPerl_Highlight:SetHighlight(frame, updateName)
		end
	end
end

-- GetBuffButton(self, buffnum, debuff, createIfAbsent)
-- debuff must be 1 or 0, as it's used in size calc
local buffIconCount = 0
local function GetBuffButton(self, buffnum, createIfAbsent)

	local button = self.buffFrame.buff and self.buffFrame.buff[buffnum]

	if (not button and createIfAbsent) then
		buffIconCount = buffIconCount + 1
		button = CreateFrame("Button", "XPerlRBuff"..buffIconCount, self.buffFrame, "XPerl_BuffTemplate")
		button:SetAlpha(self:GetAlpha())
		button:SetID(buffnum)

		if (not self.buffFrame.buff) then
			self.buffFrame.buff = {}
		end
		self.buffFrame.buff[buffnum] = button

		button:SetHeight(10)
		button:SetWidth(10)

		button.icon:SetTexCoord(0.078125, 0.921875, 0.078125, 0.921875)

		button:SetScript("OnEnter", XPerl_Raid_SetBuffTooltip)
		button:SetScript("OnLeave", XPerl_PlayerTipHide)
	end

	return button
end

-- GetShowCast
local function GetShowCast(self)
	if (conf.RaidBuffs == 1) then
		return "b", conf.BuffsCastableCurable
	elseif (conf.RaidDebuffs == 1) then
		return "d", conf.BuffsCastableCurable
	end
end

-- UpdateBuffs
local function UpdateBuffs(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
		return
	end

	local bf = self.buffFrame

	XPerl_CheckDebuffs(self, partyid)
	XPerl_ColourFriendlyUnit(self.nameFrame.text, partyid)

	local buffCount = 0
	local maxBuff = 8 - ((abs(1 - conf.RaidMana) * 2) * conf.RaidBuffsRight)

	local show, cureCast = GetShowCast(self)
	self.debuffsForced = nil
	if (show) then
		if (show == "b") then
			if (conf.RaidBuffsUntilDebuffed == 1) then
				local name, rank, buff = XPerl_UnitDebuff(partyid, 1, cureCast)
				if (name) then
					self.debuffsForced = true
					show = "d"
				end
			end
		end

		for buffnum=1,maxBuff do
			local name, rank, buff, count
			if (show == "b") then
				name, rank, buff = XPerl_UnitBuff(partyid, buffnum, cureCast)
			else
				name, rank, buff = XPerl_UnitDebuff(partyid, buffnum, cureCast)
			end
			local button = GetBuffButton(self, buffnum, buff)	-- 'buff' flags whether to create icon
			if (button) then
				if (buff) then
					buffCount = buffCount + 1

					button.icon:SetTexture(buff)
					button:Show()
				else
					button:Hide()
				end
			end
		end
		for buffnum=maxBuff+1,8 do
                        local button = bf.buff and bf.buff[buffnum]
			if (button) then
				button:Hide()
			end
		end
	end

	if (buffCount > 0) then
		bf:ClearAllPoints()
		bf:Show()
		local id = self:GetID()

		if (conf.RaidBuffsRight == 1) then
			bf:SetPoint("BOTTOMLEFT", self.statsFrame, "BOTTOMRIGHT", -2, 2)

			if (conf.RaidBuffsInside == 1) then
				if (buffCount > 3 + conf.RaidMana) then
					self.statsFrame:SetWidth(60)
				else
					self.statsFrame:SetWidth(70)
				end
			else
				self.statsFrame:SetWidth(80)
			end

			bf.buff[1]:ClearAllPoints()
			bf.buff[1]:SetPoint("BOTTOMLEFT", 0, 0)
			for i = 2,buffCount do
				if (i > buffCount) then break end

				local buffI = bf.buff[i]
				buffI:ClearAllPoints()

				if (i == 4 + conf.RaidMana) then
					if (conf.RaidBuffsInside == 1) then
						buffI:SetPoint("BOTTOMLEFT", 0, 0)
						bf.buff[1]:SetPoint("BOTTOMLEFT", buffI, "BOTTOMRIGHT", 0, 0)
					else
						buffI:SetPoint("BOTTOMLEFT", bf.buff[i-(4 - abs(1 - conf.RaidMana))], "BOTTOMRIGHT", 0, 0)
					end
				else
					buffI:SetPoint("BOTTOMLEFT", bf.buff[i - 1], "TOPLEFT", 0, 0)
				end
			end
		else
			self.statsFrame:SetWidth(80)

			bf:SetPoint("TOPLEFT", self.statsFrame, "BOTTOMLEFT", 0, 2)

			local prevBuff
			for i = 1,buffCount do
				local buff = bf.buff[i]
				buff:ClearAllPoints()
				if (prevBuff) then
					buff:SetPoint("TOPLEFT", prevBuff, "TOPRIGHT", 0, 0)
				else
					buff:SetPoint("TOPLEFT", 0, 0)
				end
				prevBuff = buff
			end
		end
	else
		self.statsFrame:SetWidth(80)
		bf:Hide()
	end

	local myRoster = XPerl_Roster[UnitName(partyid)]
	if (myRoster) then
		local _,class = UnitClass(partyid)
		if (class == "HUNTER") then
			if (XPerl_IsFeignDeath(partyid)) then
				if (not myRoster.fd) then
					myRoster.fd = GetTime()
					XPerl_Raid_UpdateHealth(self)
				end
			elseif (myRoster.fd) then
				myRoster.fd = nil
				XPerl_Raid_UpdateManaType(self, true)
				XPerl_Raid_UpdateHealth(self)
			end
		end
	end
end

------------------
-- Buffs stuffs --
------------------

-- Scans a unit's buffs and sets up initial buff time remaining entries
local debuffTimers = {	Spell_Nature_SpiritArmor = 15 * 60,	-- Mark of Nature
			Spell_Frost_ChainsOfIce = 15 * 60,	-- Mark of Frost
			INV_Misc_Bandage_08 = 60,		-- Recently Bandaged
			Spell_Shadow_DeathScream = 10 * 60,	-- Resurrection Sickness
}

-- CheckRosterBuffs
-- Check buffs exist in roster that we've received, if not then strip them out
local function CheckRosterBuffs(unitName)
	local id = RaidPositions[unitName]
	if (id) then
		if (UnitIsConnected(id)) then
			local buffs = XPerl_Roster[unitName].Buffs
			if (buffs) then
				for buffName,buffTimeEnd in pairs(buffs) do
					if (buffTimeEnd < GetTime()) then
						buffs[buffName] = nil
					end
				end
			end

			buffs = XPerl_Roster[unitName].Debuffs
			if (buffs) then
				for buffName,buffTimeEnd in pairs(buffs) do
					if (buffTimeEnd < GetTime()) then
						buffs[buffName] = nil
					end
				end
			end
		end
	end
end

-- UpdateRosterBuffs
local function UpdateRosterBuffs(self)

	local unit = self:GetAttribute("unit")
	if (not unit) then
		return
	end

	local myRoster = XPerl_Roster[UnitName(unit)]

	if (myRoster) then
		if (not myRoster.Buffs) then
			myRoster.Buffs = {}
		end
		if (not myRoster.Debuffs) then
			myRoster.Debuffs = {}
		end

		local oldAuras = {}
		local oldDebuffs = {}
		for k, v in pairs(myRoster.Buffs) do
			oldAuras[k] = 1
		end
		for k,v in pairs(myRoster.Debuffs) do
			oldDebuffs[k] = 1
		end

		local buffName, rank, buff = UnitBuff(unit, 1)
		local num = 1
		while (buffName) do
			oldAuras[buffName] = nil
			if (not myRoster.Buffs[buffName]) then
				-- We store the fade time of raid member's buffs

				local ctBuff = XPerl_CTBuffTextures[buffName]
				if (ctBuff) then
					local duration = ctBuff[2]

					if (not duration) then
						duration = 5*60
					end

					myRoster.Buffs[buffName] = GetTime() + duration
				end
			end

			num = num + 1
			buffName, rank, buff = UnitBuff(unit, num)
		end

		buffName, rank, buff = UnitDebuff(unit, 1)
		num = 1
		while (buffName) do
			buff = strsub(buff, 17)
			local duration = debuffTimers[buff]

			if (duration) then
				oldDebuffs[buff] = nil
				if (not myRoster.Debuffs[buff]) then
					myRoster.Debuffs[buff] = GetTime() + duration
				end
			end

			num = num + 1
			buffName, rank, buff = UnitDebuff(unit, num)
		end

		for k, v in pairs(oldAuras) do
			myRoster.Buffs[k] = nil
		end
		for k,v in pairs(oldDebuffs) do
			myRoster.Debuffs[k] = nil
		end

		local any
		for k,v in pairs(myRoster.Debuffs) do any = true break end
		if (not any) then myRoster.Debuffs = nil end

		any = nil
		for k,v in pairs(myRoster.Buffs) do any = true break end
		if (not any) then myRoster.Buffs = nil end
	end

	CheckRosterBuffs(unit)
end

-- XPerl_Raid_Update_Control
local function XPerl_Raid_Update_Control(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
	end

	if (UnitIsVisible(partyid) and UnitIsCharmed(partyid)) then
		self.nameFrame.warningIcon:Show()
	else
		self.nameFrame.warningIcon:Hide()
	end
end

-- XPerl_Raid_UpdateCombat
local function XPerl_Raid_UpdateCombat(self)
	local partyid = self:GetAttribute("unit")
	if (not partyid) then
		partyid = SecureButton_GetUnit(self)
		if (not partyid) then
			return
		end
	end
	if (UnitExists(partyid) and UnitAffectingCombat(partyid)) then
		self.nameFrame.combatIcon:Show()
	else
		self.nameFrame.combatIcon:Hide()
	end
end

-- XPerl_Raid_UpdatePlayerFlags(self)
local function XPerl_Raid_UpdatePlayerFlags(self, partyid)
--ChatFrame7:AddMessage("XPerl_Raid_UpdatePlayerFlags("..self:GetName()..", "..tostring(partyid))
	local f = FrameArray[partyid]
	if (f) then
		self = f

		local unitName = UnitName(partyid)
		if (unitName) then
--ChatFrame7:AddMessage("PlayerFlags change for "..unitName)
			local unitInfo = XPerl_Roster[unitName]
			if (unitInfo) then
				local change
				if (UnitIsAFK(partyid)) then
					if (not unitInfo.afk) then
--ChatFrame7:AddMessage(" |c0080FF80"..unitName.." is now AFK")
						change = true
						unitInfo.afk = GetTime()
						unitInfo.dnd = nil
					end
				elseif (UnitIsDND(partyid)) then
					if (not unitInfo.dnd) then
--ChatFrame7:AddMessage(" |c008080FF"..unitName.." is now DND")
						change = true
						unitInfo.dnd = GetTime()
						unitInfo.afk = nil
					end
				else
					if (unitInfo.afk or unitInfo.dnd) then
--ChatFrame7:AddMessage(" |c00FF8080"..unitName.." is now not AFK/DND")
						unitInfo.afk, unitInfo.dnd = nil, nil
						change = true
					end
				end

				if (change) then
					local flags = XPerl_Raid_CheckFlags(partyid)
					if (flags) then
						XPerl_Raid_ShowFlags(self, flags)
					else
						XPerl_Raid_UpdateMana(self)
						XPerl_Raid_UpdateHealth(self)
					end
				end
			end
		end
	end
end

-------------------------
-- The Update Function --
-------------------------
function XPerl_Raid_UpdateDisplayAll()
	for k,v in pairs(FrameArray) do
		if (v:IsShown()) then
			XPerl_Raid_UpdateDisplay(v)
		end
	end
end

-- XPerl_Raid_UpdateDisplay
function XPerl_Raid_UpdateDisplay(self)
	if (conf.ShowRaidPercents == 0) then
		self.statsFrame.healthBar.text:Hide()
		self.statsFrame.manaBar.text:Hide()
	else
		self.statsFrame.healthBar.text:Show()
		self.statsFrame.manaBar.text:Show()
	end

	-- Health must be updated after mana, since ctra flag checks are done here.
	XPerl_Raid_UpdateManaType(self)
	XPerl_Raid_UpdateMana(self)
	XPerl_Raid_UpdateHealth(self)		-- <<< -- AFTER MANA -- <<< --
	XPerl_Raid_UpdateName(self)
	XPerl_Raid_UpdateCombat(self)
	XPerl_Raid_Update_Control(self)
	UpdateBuffs(self)
	XPerl_UpdateSpellRange(self)

	XPerl_Highlight:SetHighlight(self)
end

-- HideShowRaid
function XPerl_Raid_HideShowRaid()
	for i = 1,9 do
		local frame = getglobal(XPERL_RAIDGRP_PREFIX..i)

		if (conf["ShowGroup"..i] == 1 and conf.ShowRaid == 1 and (i < 9 or conf.SortRaidByClass == 1)) then
			frame:Show()
			--SecureRaidGroupHeader_Update(frame)		-- Needed?
		else
			frame:Hide()
		end
	end
end

-------------------
-- Event Handler --
-------------------

-- XPerl_Raid_OnEvent
function XPerl_Raid_OnEvent(self, event, unit, a, b, c)
--if (strfind(event, "SPELL")) then XPerl_ShowMessage() end
	local func = XPerl_Raid_Events[event]
	if (func) then
		if (strmatch(event, "^UNIT_")) then
			local f = FrameArray[unit]
			if (f) then
				func(f, unit, a, b, c)
			end
		else
			func(self, unit, a, b, c)
		end
	else
XPerl_ShowMessage("EXTRA EVENT")
	end
end

-- VARIABLES_LOADED
function XPerl_Raid_Events:VARIABLES_LOADED()
	self:UnregisterEvent("VARIABLES_LOADED")

	if (GetNumRaidMembers() == 0) then
		ResArray = {}
		XPerl_Roster = {}
	else
		local myRoster = XPerl_Roster[UnitName("player")]
		if (myRoster) then
			myRoster.afk, myRoster.dnd, myRoster.dnd, myRoster.ressed, myRoster.resCount = nil, nil, nil, nil, nil
		end
	end

	XPerl_Highlight:Register(XPerl_Raid_HighlightCallback)

	XPerl_Raid_ChangeAttributes()
	XPerl_RaidTitles()
end

-- PLAYER_ENTERING_WORLD
function XPerl_Raid_Events:PLAYER_ENTERING_WORLD()
	if (not XPerl_raid1 and GetNumRaidMembers() > 0) then
		XPerl_Raid_Position(self)
	end
end

-- PLAYER_LEAVING_WORLD
function XPerl_Raid_Events:PLAYER_LEAVING_WORLD()
end

-- RAID_ROSTER_UPDATE
function XPerl_Raid_Events:RAID_ROSTER_UPDATE()
	XPerl_Raid_Position(self)
	if (GetNumRaidMembers() == 0) then
		ResArray = {}
		XPerl_Roster = {}
	end
	if (XPerl_SendModules) then
		XPerl_SendModules()			-- Let other X-Perl users know which version we're running
	end
end

-- UNIT_FLAGS
function XPerl_Raid_Events:UNIT_FLAGS()
	XPerl_Raid_UpdateCombat(self)
end

XPerl_Raid_Events.UNIT_DYNAMIC_FLAGS = XPerl_Raid_Events.UNIT_FLAGS

function XPerl_Raid_Events:PLAYER_FLAGS_CHANGED(unit)
	XPerl_Raid_UpdatePlayerFlags(self, unit)
end

-- UNIT_FACTION
function XPerl_Raid_Events:UNIT_FACTION()
	XPerl_Raid_Update_Control(self)
	XPerl_Raid_UpdateName(self)
end

-- UNIT_COMBAT
function XPerl_Raid_Events:UNIT_COMBAT()
	if (arg2 == "HEAL") then
		XPerl_Raid_CombatFlash(self, 0, true, true)
	elseif (arg4 and arg4 > 0) then
		XPerl_Raid_CombatFlash(self, 0, true)
	end
end

-- UNIT_HEALTH
function XPerl_Raid_Events:UNIT_HEALTH()
	XPerl_Raid_UpdateHealth(self)
	XPerl_Raid_UpdateCombat(self)
end
XPerl_Raid_Events.UNIT_MAXHEALTH = XPerl_Raid_Events.UNIT_HEALTH

function XPerl_Raid_Events:UNIT_DISPLAYPOWER()
	XPerl_Raid_UpdateManaType(self)
	XPerl_Raid_UpdateMana(self)
end

-- UNIT_MANA
function XPerl_Raid_Events:UNIT_MANA()
	if (conf.RaidMana == 1) then
		XPerl_Raid_UpdateMana(self)
	end
end
XPerl_Raid_Events.UNIT_MAXMANA   = XPerl_Raid_Events.UNIT_MANA
XPerl_Raid_Events.UNIT_RAGE      = XPerl_Raid_Events.UNIT_MANA
XPerl_Raid_Events.UNIT_MAXRAGE   = XPerl_Raid_Events.UNIT_MANA
XPerl_Raid_Events.UNIT_ENERGY    = XPerl_Raid_Events.UNIT_MANA
XPerl_Raid_Events.UNIT_MAXENERGY = XPerl_Raid_Events.UNIT_MANA

-- UNIT_NAME_UPDATE
function XPerl_Raid_Events:UNIT_NAME_UPDATE()
	XPerl_Raid_UpdateName(self)
end

-- UNIT_AURA
function XPerl_Raid_Events:UNIT_AURA()
	UpdateRosterBuffs(self)
	UpdateBuffs(self)
end

-- SetRes
local function SetResStatus(resserName, resTargetName, ignoreCounter)

	--frame.beingRessed = true
	local resEnd

	if (resTargetName) then
		ResArray[resserName] = resTargetName
	else
		resEnd = true

		for i,name in pairs(ResArray) do
			if (i == resserName) then
				resTargetName = name
				break
			end
		end

		ResArray[resserName] = nil
	end

	if (resTargetName) then
		local myRoster = XPerl_Roster[resTargetName]
		if (myRoster) then
			if (resEnd and not ignoreCounter) then
				myRoster.ressed = 1
				myRoster.resCount = (myRoster.resCount or 0) + 1
			end
			UpdateUnitByName(resTargetName, true)
		end
	end
end

-- UNIT_SPELLCAST_SENT
function XPerl_Raid_Events:UNIT_SPELLCAST_START(unit, spell, rank)
	if (ResArray[UnitName(unit)]) then
		-- Flagged as ressing, finish their old cast
		SetResStatus(UnitName(unit))
	end

	local name, nameSubtext, text, texture, startTime, endTime, isTradeSkill = UnitCastingInfo(unit)
	if (resSpells[name]) then
		if (UnitName(unit.."target") and UnitIsDead(unit.."target")) then
			SetResStatus(UnitName(unit), UnitName(unit.."target"))
		end
	end
end

-- UNIT_SPELLCAST_STOP
function XPerl_Raid_Events:UNIT_SPELLCAST_STOP(unit)
	if (unit) then
		SetResStatus(UnitName(unit))
	end
end

-- UNIT_SPELLCAST_FAILED
function XPerl_Raid_Events:UNIT_SPELLCAST_FAILED(unit)
	if (unit) then
		SetResStatus(UnitName(unit), nil, true)
	end
end

XPerl_Raid_Events.UNIT_SPELLCAST_INTERRUPTED = XPerl_Raid_Events.UNIT_SPELLCAST_FAILED

-- Direct string matches can be done via table lookup
local QuickFuncs = {
	--AFK	= function(m)	m.afk = GetTime(); m.dnd = nil; end,
	--UNAFK	= function(m)	m.afk = nil; end,
	--DND	= function(m)	m.dnd = GetTime(); m.afk = nil; end,
	--UNDND	= function(m)	m.dnd = nil; end,
	RESNO	= function(m,n) SetResStatus(n) end,
	RESSED	= function(m)	m.ressed = 1; end,
	CANRES	= function(m)	m.ressed = 2; end,
	NORESSED= function(m)
		if (m.ressed) then
			m.ressed = 3
		else
			m.ressed = nil
		end
		m.resCount = nil
	end,
	SR	= XPerl_SendModules
}

-- ProcessCTRAMessage
local function ProcessCTRAMessage(unitName, msg)
	local myRoster = XPerl_Roster[unitName]

	if (not myRoster) then
		return
	end

	local update = true

	local func = QuickFuncs[msg]
	if (func) then
--ChatFrame7:AddMessage("QuickFuncs["..msg.."]")
		func(myRoster, unitName)
	else
		if (strsub(msg, 1, 4) == "RES ") then
			SetResStatus(unitName, strsub(msg, 5))
			return

 		elseif (strsub(msg, 1, 3) == "RN ") then
			-- Buff durations
			local secsLeft, buffIndex, buffSub = strmatch(msg, "^RN ([^%s]+) ([^%s]+) ([^%s]+)$")

			if (secsLeft and buffIndex and buffSub) then
				secsLeft = tonumber(secsLeft)
				buffIndex = tonumber(buffIndex)
				buffSub = tonumber(buffSub)

				local buff = XPerl_CTBuffArray[buffIndex]

				if (buff) then
					local buffName, delete
					-- Shouldn't come with string and sub index > 0, but it does.. Will investigate when and why
					-- Druid Thorns... /shrug
					if (buffSub == 0 or type(buff.name) == "string") then
						buffName = buff.name
					else
						buffName = buff.name[buffSub]
						if (buffSub == 1) then
							delete = buff.name[2]
						else
							delete = buff.name[1]
						end
					end

					if (buffName) then
						if (not myRoster.Buffs) then
							myRoster.Buffs = {}
						end
						myRoster.Buffs[buffName] = GetTime() + secsLeft
						if (delete) then
							-- Clear a buff's partner buff (can only have 1 of PWF group vs single for example)
							myRoster.Buffs[delete] = nil
						end
						CheckRosterBuffs(unitName)
					end
				end
			end
			update = nil

		elseif (strsub(msg, 1, 3) == "CD ") then
			local num, cooldown = strmatch(msg, "^CD (%d+) (%d+)$")
			if ( num == "1" ) then
				myRoster.Rebirth = GetTime() + tonumber(cooldown)*60
			elseif ( num == "2" ) then
				myRoster.Reincarnation = GetTime() + tonumber(cooldown)*60
			elseif ( num == "3" ) then
				myRoster.Soulstone = GetTime() + tonumber(cooldown)*60
			end
			update = nil

		elseif (strsub(msg, 1, 2) == "V ") then
			myRoster.version = strsub(msg, 3)
			update = nil
		else
			update = nil
		end
	end

	if (update) then
--ChatFrame7:AddMessage("  UpdateUnitByName("..unitName..")")
		UpdateUnitByName(unitName, true)
	end
end

-- ProcessoRAMessage
local function ProcessoRAMessage(unitName, msg)
	local myRoster = XPerl_Roster[unitName]

	if (not myRoster) then
		return
	end

	if (strsub(msg, 1, 5) == "oRAV ") then
		myRoster.oRAversion = strsub(msg, 6)
	end
end

-- XPerl_Raid_Events:CHAT_MSG_RAID
-- Check for AFK/DND flags in chat
--function XPerl_Raid_Events:CHAT_MSG_RAID()
--	local myRoster = XPerl_Roster[arg4]
--	if (myRoster) then
--		if (arg6 == "AFK") then
--			if (not myRoster.afk) then
--				myRoster.afk = GetTime()
--				myRoster.dnd = nil
--			end
--		elseif (arg6 == "DND") then
--			if (not myRoster.dnd) then
--				myRoster.dnd = GetTime()
--				myRoster.afk = nil
--			end
--		else
--			myRoster.dnd, myRoster.afk = nil, nil
--		end
--	end
--end
--XPerl_Raid_Events.CHAT_MSG_RAID_LEADER = XPerl_Raid_Events.CHAT_MSG_RAID
--XPerl_Raid_Events.CHAT_MSG_PARTY = XPerl_Raid_Events.CHAT_MSG_RAID

-- CHAT_MSG_ADDON
function XPerl_Raid_Events:CHAT_MSG_ADDON()
	--XPerl_Raid_Events.CHAT_MSG_RAID()
	if (arg3 == "RAID") then
		if (arg1 == "CTRA") then
			XPerl_ParseCTRA(arg4, arg2, ProcessCTRAMessage)
		elseif (arg1 == "oRA") then
			XPerl_ParseCTRA(arg4, arg2, ProcessoRAMessage)
		end
	end
end

-- SetRaidRoster
local function SetRaidRoster()

	local NewRoster = {}
	Last_Roster = XPerl_Roster

	RaidPositions = {}

	RaidGroupCounts = {0,0,0,0,0,0,0,0,0}
	for i = 1,GetNumRaidMembers() do
		local name, rank, group, level, class, fileName = GetRaidRosterInfo(i)

		if (name) then
			RaidPositions[name] = "raid"..i

			if (conf.SortRaidByClass == 1) then
				for j = 1,9 do
					if (conf["RaidClass"..j] == fileName) then
						RaidGroupCounts[j] = RaidGroupCounts[j] + 1
						break
					end
				end
			else
				RaidGroupCounts[group] = RaidGroupCounts[group] + 1
			end

			if (fileName and not ClassNames[fileName]) then
				ClassNames[fileName] = class
			end

			if (XPerl_Roster[name]) then
				NewRoster[name] = XPerl_Roster[name]

				if (NewRoster[name].afk and not UnitIsAFK("raid"..i)) then
					NewRoster[name].afk = nil
				end
				if (NewRoster[name].dnd and not UnitIsDND("raid"..i)) then
					NewRoster[name].dnd = nil
				end
			else
				NewRoster[name] = {}
			end
		end
	end

	if (GetNumRaidMembers() > 0) then
		XPerl_Raid_Frame:Show()
	else
		XPerl_Raid_Frame:Hide()
	end

	XPerl_Roster = NewRoster
end

-- XPerl_Raid_Position
function XPerl_Raid_Position(self)
	SetRaidRoster()
	XPerl_RaidTitles()
end

--------------------
-- Click Handlers --
--------------------
function XPerl_RaidFrameDropDown_Initialize(ct)
	if (type(ct) ~= "table") then
		ct = nil
	end

	if (type(UIDROPDOWNMENU_MENU_VALUE) == "table" and UIDROPDOWNMENU_MENU_VALUE[1] == "Main Tanks") then
		local info = {}
		info.text = XPERL_RAID_DROPDOWN_MAINTANKS
		info.isTitle = 1
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL)
		for i = 1,10 do
			info = {}
			if (XPerl_MainTanks[i] and XPerl_MainTanks[i][2] == UIDROPDOWNMENU_MENU_VALUE[2]) then
				info.text = format("|c00FFFF80"..XPERL_RAID_DROPDOWN_REMOVEMT.."|r", i)
				info.value = {UIDROPDOWNMENU_MENU_VALUE[1], UIDROPDOWNMENU_MENU_VALUE[2], i, 1}
			else
				info.text = format(XPERL_RAID_DROPDOWN_SETMT, i)
				info.value = {UIDROPDOWNMENU_MENU_VALUE[1], UIDROPDOWNMENU_MENU_VALUE[2], i}
			end
			info.func = XPerl_MainTankSet_OnClick
			UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL)
		end
		return
	end

	RaidFrameDropDown_Initialize()

	if (GetBuildInfo() == "2.0.3" and not NoXPerlHack) then
		-- Workaround the stupid raid dropdown problems:
		for i = 1,20 do
			local f = getglobal("DropDownList1Button"..i)
			if (not f) then
				break
			end

			if (f.value == "RAID_PROMOTE" or f.value == "RAID_LEADER" or f.value == "RAID_MAINTANK" or
					f.value == "RAID_MAINASSIST" or f.value == "RAID_REMOVE" or f.value == "RAID_DEMOTE" or
					f.value == "LOOT_PROMOTE" or f.value == "UNINVITE" or f.value == "PROMOTE") then
				f.func = XPerl_UnitPopup_OnClick
			end
		end
	end

	local titleDone
	if (DropDownList1.numButtons == 0 and (IsRaidOfficer() or (ct and CT_RATab_AutoPromotions))) then
		titleDone = true
		info = {}
		info.text = this.name
		if (this.server) then
			info.text = info.text.."-"..this.server
		end
		info.isTitle = 1
		info.notCheckable = 1
		UIDropDownMenu_AddButton(info)
	end

	if (IsRaidOfficer()) then
		if (not titleDone and DropDownList1.numButtons > 0) then
			-- We want our MT option above the Cancel option, so we trick the menu into thinking it's got 1 less button
			DropDownList1.numButtons = DropDownList1.numButtons - 1
		end

		info = {}
		info.text = XPERL_RAID_DROPDOWN_MAINTANKS
		info.value = {"Main Tanks", this.name}			-- Must be 'this'
		info.hasArrow = 1
		info.notCheckable = 1
		UIDropDownMenu_AddButton(info)

		-- Re-add the cancel button after our MT option
		info = {}
		info.text = CANCEL
		info.value = "CANCEL"
		info.owner = "RAID"
		info.func = UnitPopup_OnClick
		info.notCheckable = 1
		UIDropDownMenu_AddButton(info)
	end

	if (ct and CT_RATab_AutoPromotions) then
		local info = {}
		info.text = XPERL_RAID_AUTOPROMOTE
		info.checked = CT_RATab_AutoPromotions[this.name]	-- Must be 'this'
		info.value = this.id					-- Must be 'this'
		info.func = CT_RATab_AutoPromote_OnClick
		UIDropDownMenu_AddButton(info)
	end
end

if (GetBuildInfo() == "2.0.3") then
function XPerl_UnitPopup_OnClick(self)
	local dropdownFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
	local value = this.value
	local unit = dropdownFrame.unit
	local name = dropdownFrame.name
	local server = dropdownFrame.server

--	ChatFrame7:AddMessage(tostring(value).." - unit:"..unit..", name: "..name)

	if (value == "RAID_PROMOTE") then
		PromoteToAssistant(unit)

	elseif (value == "RAID_LEADER") then
		PromoteToLeader(unit)

	elseif (value == "RAID_MAINTANK" ) then
		SetPartyAssignment("maintank", unit)

	elseif (value == "RAID_MAINASSIST" ) then
		SetPartyAssignment("mainassist", unit)

	elseif (value == "RAID_REMOVE" ) then
		UninviteUnit(unit)

	elseif (value == "RAID_DEMOTE" ) then
		DemoteAssistant(unit)
		if (GetPartyAssignment("maintank") == name) then
			SetPartyAssignment("maintank", nil)
		elseif (GetPartyAssignment("mainassist") == name) then
			SetPartyAssignment("mainassist", nil)
		end

	elseif (value == "LOOT_PROMOTE") then
		SetLootMethod("master", unit, 1)

	elseif (value == "UNINVITE" ) then
		UninviteUnit(unit)

	elseif (value == "PROMOTE" ) then
		PromoteToLeader(unit)

	end
end
end

-- XPerl_MainTankSet_OnClick
function XPerl_MainTankSet_OnClick()
	if (this.value[1] == "Main Tanks") then				-- Must be 'this'
		if (this.value[4]) then
			SendAddonMessage("CTRA", "R "..this.value[2], "RAID")
		else
			SendAddonMessage("CTRA", "SET "..this.value[3].." "..this.value[2], "RAID")
		end
	end
	CloseMenus()
end

-- XPerl_ScaleRaid
function XPerl_ScaleRaid()
	for frame = 1,9 do
		local f = getglobal("XPerl_Raid_Title"..frame)
		if (f) then
			f:SetScale(conf.Scale_Raid)
		end
	end
end

-- XPerl_RaidTitles
function XPerl_RaidTitles()
	for i = 1,9 do		-- NUM_RAID_GROUPS do
		local frame = getglobal("XPerl_Raid_Title"..i)
		local titleFrame = getglobal(frame:GetName().."TitleText")
		local virtualFrame = getglobal(frame:GetName().."Virtual")

		if (conf.SortRaidByClass == 1) then
			if (ClassNames[conf["RaidClass"..i]]) then
				titleFrame:SetText(ClassNames[conf["RaidClass"..i]])
			else
				titleFrame:SetText(localGroups[conf["RaidClass"..i]])
			end
		else
			titleFrame:SetText(format(XPERL_RAID_GROUP, i))
		end

		if (XPerlLocked == 0 or (RaidGroupCounts[i] > 0 and conf.ShowRaid == 1 and conf["ShowGroup"..i] == 1)) then
			if (XPerlLocked == 0 or conf.ShowRaidTitles == 1) then
				titleFrame:Show()
			else
				titleFrame:Hide()
			end

			if (XPerlLocked == 0) then
				virtualFrame:ClearAllPoints()
				if (conf.RaidAnchor == "TOP") then
					virtualFrame:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, 0)
					virtualFrame:SetHeight((conf.RaidMana * 5 + 39) * 5 + (conf.RaidVerticalSpacing * 4))
					virtualFrame:SetWidth(80)

				elseif (conf.RaidAnchor == "LEFT") then
					virtualFrame:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, 0)
					virtualFrame:SetHeight(conf.RaidMana * 5 + 39)
					virtualFrame:SetWidth(80 * 5 + (conf.RaidVerticalSpacing * 4))

				elseif (conf.RaidAnchor == "BOTTOM") then
					virtualFrame:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 0, 0)
					virtualFrame:SetHeight((conf.RaidMana * 5 + 39) * 5 + (conf.RaidVerticalSpacing * 4))
					virtualFrame:SetWidth(80)

				elseif (conf.RaidAnchor == "RIGHT") then
					virtualFrame:SetPoint("TOPRIGHT", frame, "BOTTOMRIGHT", 0, 0)
					virtualFrame:SetHeight(conf.RaidMana * 5 + 39)
					virtualFrame:SetWidth(80 * 5 + (conf.RaidVerticalSpacing * 4))
				end

				virtualFrame:SetBackdropColor(conf.BackColour.r, conf.BackColour.g, conf.BackColour.b, conf.BackColour.a)
				virtualFrame:SetBackdropBorderColor(conf.BorderColour.r, conf.BorderColour.g, conf.BorderColour.b, 1)
				virtualFrame:Show()
			else
				virtualFrame:Hide()
			end
		else
			virtualFrame:Hide()
			titleFrame:Hide()
		end
	end

	XPerl_ProtectedCall(XPerl_EnableRaidMouse)
end

-- XPerl_EnableRaidMouse()
function XPerl_EnableRaidMouse()
	for i = 1,9 do
		local frame = getglobal("XPerl_Raid_Title"..i)
		if (XPerlLocked == 0) then
			frame:EnableMouse(true)
		else
			frame:EnableMouse(false)
		end
	end
end

-- Moving stuff
function XPerl_Raid_GetGap()
	if (conf.RaidAnchor == "TOP" or conf.RaidAnchor == "BOTTOM") then
		return floor((floor((XPerl_Raid_Grp2:GetLeft() - XPerl_Raid_Grp1:GetRight() + 0.01) * 100) / 100) + 4)
	else
		return floor((floor((XPerl_Raid_Grp1:GetTop() - XPerl_Raid_Grp2:GetBottom() + 0.01) * 100) / 100) - 46)
	end

	return (0)
end

-- InterestingFrames
local function InterestingFrames()
	local interest = XPerl_Options.raidAlign
	local ret = {}

	if (interest == "all") then
		for i = 1,9 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	elseif (interest == "odd") then
		for i = 1,9,2 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	elseif (interest == "even") then
		for i = 2,9,2 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	elseif (interest == "first4") then
		for i = 1,4 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	elseif (interest == "last4") then
		for i = 5,9 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	end
	return ret
end

-- XPerl_Raid_SetGap
function XPerl_Raid_SetGap(newGap)
	if (type(newGap) == "number") then
		local frames = InterestingFrames()

		if (conf.RaidAnchor == "TOP" or conf.RaidAnchor == "BOTTOM") then
			local framePrev

			for i,frame in pairs(frames) do
				if (framePrev and frame) then
					local right = framePrev:GetRight()
					local top = frame:GetTop()

					frame:ClearAllPoints()
					frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", right + newGap - 4, top)
	                                frame:SetUserPlaced(true)
				end

				framePrev = frame
			end
		else
			local framePrev

			for i,frame in pairs(frames) do
				if (framePrev and frame) then
					local bottom = framePrev:GetBottom()
					local left = frame:GetLeft()

					frame:ClearAllPoints()
					frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, bottom + newGap - 26)
	                                frame:SetUserPlaced(true)
				end

				framePrev = frame
			end
		end
	end
end

-- XPerl_Raid_AlignTop
function XPerl_Raid_AlignTop()
	if (not XPerl_Raid_Grp1) then
		return
	end

	local frames = InterestingFrames()

	local top = frames[1]:GetTop()

	for i,frame in pairs(frames) do
		local left = frame:GetLeft()

		frame:ClearAllPoints()
		frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
        	frame:SetUserPlaced(true)
	end
end

-- XPerl_Raid_AlignLeft
function XPerl_Raid_AlignLeft()
	if (not XPerl_Raid_Grp1) then
		return
	end

	local frames = InterestingFrames()

	local left = frames[1]:GetLeft()

	for i,frame in pairs(frames) do
		local top = frame:GetTop()

		frame:ClearAllPoints()
		frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top)
        	frame:SetUserPlaced(true)
	end
end

-- XPerl_Raid_SetBuffTooltip
function XPerl_Raid_SetBuffTooltip(self)
	local parentUnit = self:GetParent():GetParent()
	local partyid = SecureButton_GetUnit(parentUnit)
	if (not partyid) then
		return
	end

	GameTooltip:SetOwner(self,"ANCHOR_BOTTOMRIGHT",30,0)

	local show, cureCast = GetShowCast(parentUnit)
	if (parentUnit.debuffsForced) then
		show = "d"
	end
	if (show == "b") then
		XPerl_TooltipSetUnitBuff(GameTooltip, partyid, self:GetID(), cureCast)
		XPerl_Raid_AddBuffDuration(partyid)
	elseif (show == "d") then
		XPerl_TooltipSetUnitDebuff(GameTooltip, partyid, self:GetID(), cureCast)
		XPerl_Raid_AddDebuffDuration(partyid, GameTooltip, self:GetID(), cureCast)
	end
end

------- XPerl_ToggleRaidBuffs -------
-- Raid Buff Key Binding function --
function XPerl_ToggleRaidBuffs(castable)

	if (castable) then
		if (conf.BuffsCastableCurable == 1) then
			conf.BuffsCastableCurable = 0
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_BUFFANY)
		else
			conf.BuffsCastableCurable = 1
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_BUFFCURECAST)
		end
	else
		if (conf.RaidBuffs == 1) then
			conf.RaidBuffs = 0
			conf.RaidDebuffs = 1
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_DEBUFFS)

		elseif (conf.RaidDebuffs == 1) then
			conf.RaidDebuffs = 0
			conf.RaidBuffs = 0
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_NOBUFFS)

		else
			conf.RaidBuffs = 1
			conf.RaidDebuffs = 0
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_BUFFS)
		end
	end

	for k,v in pairs(FrameArray) do
		if (v:IsShown()) then
			XPerl_Raid_UpdateDisplay(v)
		end
	end
end

-- GetCombatRezzerList()
local normalRezzers = {PRIEST = true, SHAMAN = true, PALADIN = true}
local function GetCombatRezzerList()

	local anyCombat = 0
	local anyAlive = 0
	for i = 1,GetNumRaidMembers() do
		local _, class = UnitClass("raid"..i)
		if (normalRezzers[class]) then
			if (UnitAffectingCombat("raid"..i)) then
				anyCombat = anyCombat + 1
			end
			if (not UnitIsDeadOrGhost("raid"..i) and UnitIsConnected("raid"..i)) then
				anyAlive = anyAlive + 1
			end
		end
	end

	-- We only need to know about battle rezzers if any normal rezzers are in combat
	if (anyCombat > 0 or anyAlive < 3) then
		local ret = {}
		local t = GetTime()

		for i = 1,GetNumRaidMembers() do
         		if (not UnitIsDeadOrGhost("raid"..i) and UnitIsVisible("raid"..i)) then
				local name, rank, subgroup, level, _, fileName, zone, online, isDead = GetRaidRosterInfo(i)

				local good
				if (not UnitAffectingCombat("raid"..i)) then
					if (fileName == "PRIEST" or fileName == "SHAMAN" or fileName == "PALADIN") then
						tinsert(ret, {["name"] = name, class = fileName, cd = 0})
					end
				else
					if (fileName == "DRUID") then
						local myRoster = XPerl_Roster[name]

						if (myRoster) then
							if (myRoster.Rebirth and myRoster.Rebirth - t <= 0) then
								myRoster.Rebirth = nil		-- Check for expired cooldown
							end
							if (myRoster.Rebirth) then
								if (myRoster.Rebirth - t < 120) then
									tinsert(ret, {["name"] = name, class = fileName, cd = myRoster.Rebirth - t})
								end
							else
								tinsert(ret, {["name"] = name, class = fileName, cd = 0})
							end
						end
					end
				end
			end
		end

		if (#ret > 0) then
			sort(ret, function(a,b) return a.cd < b.cd end)

			local list = ""
			for k,v in pairs(ret) do
				local name = XPerlColourTable[v.class]..v.name.."|r"

				if (v.cd > 0) then
					name = name.." (in "..SecondsToTime(v.cd)..")"
				end

				if (list == "") then
					list = name
				else
					list = list..", "..name
				end
			end

			return list
		else
			return "|c00FF0000"..NONE.."|r"
		end
	end

	if (anyAlive == 0) then
		return "|c00FF0000"..NONE.."|r"
	elseif (anyCombat == 0) then
		return "|c00FFFFFF"..ALL.."|r"
	end
end

-- XPerl_RaidTipExtra
function XPerl_RaidTipExtra(unitid)

	if (UnitInRaid(unitid)) then
		local unitName = UnitName(unitid)
		local zone
		local name, rank, subgroup, level, class, fileName, zone, online, isDead

		for i = 1,GetNumRaidMembers() do
			name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i)
			if (name == unitName) then
				break
			end
			zone = ""
		end

		local stats = XPerl_Roster[unitName]
		if (stats) then
			local t = GetTime()

			if (stats.version) then
				if (stats.oRAversion) then
					GameTooltip:AddLine("CTRA "..stats.version.." (oRA "..stats.oRAversion..")", 1, 1, 1)
				else
					GameTooltip:AddLine("CTRA "..stats.version, 1, 1, 1)
				end
			else
				GameTooltip:AddLine(XPERL_RAID_TOOLTIP_NOCTRA, 0.7, 0.7, 0.7)
			end

			if (stats.offline and UnitIsConnected(unitid)) then
				stats.offline = nil
			end
			if (stats.afk and not UnitIsAFK(unitid)) then
				stats.afk = nil
			end
			if (stats.dnd and not UnitIsDND(unitid)) then
				stats.dnd = nil
			end

			if (stats.offline) then
				GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_OFFLINE, SecondsToTime(t - stats.offline)))

			elseif (stats.afk) then
				GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_AFK, SecondsToTime(t - stats.afk)))

			elseif (stats.dnd) then
				GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_DND, SecondsToTime(t - stats.dnd)))

			elseif (stats.fd) then
				if (not UnitIsDead(unitid)) then
					stats.fd = nil
				else
					local x = stats.fd + 360 - t
					if (x > 0) then
						GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_DYING, SecondsToTime(x)))
					end
				end
			end

			if (stats.Rebirth) then
				if (stats.Rebirth - t > 0) then
					GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_REBIRTH, SecondsToTime(stats.Rebirth - t)))
				else
					stats.Rebirth = nil
				end

			elseif (stats.Reincarnation) then
				if (stats.Reincarnation - t > 0) then
					GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_ANKH, SecondsToTime(stats.Reincarnation - t)))
				else
					stats.Reincarnation = nil
				end

			elseif (stats.Soulstone) then
				if (stats.Soulstone - t > 0) then
					GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_SOULSTONE, SecondsToTime(stats.Soulstone - t)))
				else
					stats.Soulstone = nil
				end
			end

			if (UnitIsDeadOrGhost(unitid) and not XPerl_IsFeignDeath(unitid)) then
				if (stats.resCount) then
					GameTooltip:AddLine(XPERL_LOC_RESURRECTED.." x"..stats.resCount)
				end

				local Rezzers = GetCombatRezzerList()
				if (Rezzers) then
					GameTooltip:AddLine(XPERL_RAID_RESSER_AVAIL..Rezzers, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
				end
			end
		end

		GameTooltip:Show()
	end
end

-- GetNamesWithoutBuff
local matches = {
	{"Holy_WordFortitude", "Holy_PrayerOfFortitude"},
	{"Holy_MagicalSentry", "Holy_ArcaneIntellect"},
	{"Shadow_AntiShadow", "Holy_PrayerofShadowProtection"},
	{"Holy_DivineSpirit", "Holy_PrayerofSpirit"},
	{"Holy_FistOfJustice", "Holy_GreaterBlessingofKings"},
	{"Holy_SealOfWisdom", "Holy_GreaterBlessingofWisdom"},
	{"Magic_MageArmor", "Magic_GreaterBlessingofKings"},
	{"Holy_SealOfSalvation", "Holy_GreaterBlessingofSalvation"},
	{"Holy_PrayerOfHealing02", "Holy_GreaterBlessingofLight"},
	{"Nature_LightningShield", "Holy_GreaterBlessingofSanctuary"},
	{"Nature_Regeneration", "Nature_Regeneration"}
}
local function GetNamesWithoutBuff(spellName, tex, with)
	local count = 0
	local names
	local checkExpiring

	local _, class = UnitClass("player")
	if (IsRaidOfficer()) then
		checkExpiring = {all = 5}

	elseif (class == "PRIEST") then
		checkExpiring = {Spell_Holy_WordFortitude = 5, Spell_Holy_PrayerOfFortitude = 5,
				Spell_Holy_DivineSpirit = 2, Spell_Holy_PrayerofSpirit = 2,
				Spell_Shadow_AntiShadow = 2, Spell_Holy_PrayerofShadowProtection = 2}

	elseif (class == "DRUID") then
		checkExpiring = {Spell_Nature_Regeneration = 5, Spell_Nature_Thorns = 2}

	elseif (class == "MAGE") then
		checkExpiring = {Spell_Holy_MagicalSentry = 5, Spell_Holy_ArcaneIntellect = 5, Spell_Holy_FlashHeal = 2, Spell_Nature_AbolishMagic = 2}

	elseif (class == "PALADIN") then
		checkExpiring = {Spell_Holy_Excorcism = 2,
				Spell_Holy_FistOfJustice = 1, Spell_Holy_GreaterBlessingofKings = 1,
				Spell_Holy_SealOfWisdom = 1, Spell_Holy_GreaterBlessingofWisdom = 1,
				Spell_Magic_MageArmor = 1, Spell_Magic_GreaterBlessingofKings = 1,
				Spell_Holy_SealOfSalvation = 1, Spell_Holy_GreaterBlessingofSalvation = 1,
				Spell_Holy_PrayerOfHealing02 = 1, Spell_Holy_GreaterBlessingofLight = 1,
				Spell_Nature_LightningShield = 1, Spell_Holy_GreaterBlessingofSanctuary = 1}
	end

	for i = 1,GetNumRaidMembers() do
		local name = UnitName("raid"..i)
		local _, unitClass = UnitClass("raid"..i)
		if (name and UnitIsConnected("raid"..i) and not UnitIsDeadOrGhost("raid"..i)) then
			local hasBuff
			for num = 1,25 do
				local name, rank, buffTexture = UnitBuff("raid"..i, num)
				if (not name) then
					break
				end

				if (name == spellName) then
					hasBuff = true
				else
					for dups,pair in pairs(matches) do
						if (tex == "Interface\\Icons\\Spell_"..pair[1] or tex == "Interface\\Icons\\Spell_"..pair[2]) then
							if (buffTexture == "Interface\\Icons\\Spell_"..pair[1] or buffTexture == "Interface\\Icons\\Spell_"..pair[2]) then
								hasBuff = true
								break
							end
						end
					end
				end
				if (hasBuff) then
					if (without and checkExpiring) then
						local found = checkExpiring.all

						if (not found) then
							found = checkExpiring[strsub(buffTexture, 23)]
						end

						if (found) then
							local myRoster = XPerl_Roster[name]
							if (myRoster and myRoster.Buffs) then
								local expire = myRoster.Buffs[spellName]
								if (expire and expire <= GetTime() + (found*60)) then
									GameTooltip:AddLine(format(XPERL_RAID_TOOLTIP_BUFFEXPIRING, XPerlColourTable[unitClass]..name.."|c", buffName, SecondsToTime(buff - GetTime())), 1, 0.2, 0)
								end
							end
						end
					end
					break
				end
			end

			if ((with and hasBuff) or (not with and not hasBuff)) then
				count = count + 1
				local _, class = UnitClass("raid"..i)
				if (class) then
					names = (names or "")..XPerlColourTable[class]..name.." "
				else
					names = (names or "")..name.." "
				end
			end
		end
	end

	return names, count
end

-- XPerl_Raid_AddBuffDuration
function XPerl_Raid_AddBuffDuration(partyid, x)
	local XTooltip
	if (x) then
		XTooltip = x
	else
		XTooltip = GameTooltip
	end

	if (GetNumRaidMembers() > 0 and XTooltip:IsOwned(this)) then
		if (partyid ~= "player") then
			local stats = XPerl_Roster[UnitName(partyid)]

			if (stats and stats.Buffs) then
				local buffName = getglobal("GameTooltipTextLeft1"):GetText()
				if (buffName) then
					local buff = stats.Buffs[buffName]
					if (buff) then
						if (buff > GetTime()) then
							XTooltip:AddLine(SecondsToTime(buff - GetTime())..XPERL_RAID_TOOLTIP_REMAINING, 1, 0.82, 0)
						end
					end
				end
			end
		end

		if (conf.BuffTooltipHelper == 1 and partyid and UnitInRaid(partyid)) then
			local icon = getglobal(this:GetName().."Icon")
			if (not icon) then
				icon = this.icon
			end
			if (icon) then
				local buffName = getglobal("GameTooltipTextLeft1"):GetText()
				local tex = icon:GetTexture()
				if (buffName and tex) then
					local names, count = GetNamesWithoutBuff(buffName, tex, IsAltKeyDown())
					if (names) then
						if (IsAltKeyDown()) then
							XTooltip:AddLine(format(XPERL_RAID_TOOLTIP_WITHBUFF, count), 0.3, 1, 0.2)
						else
							XTooltip:AddLine(format(XPERL_RAID_TOOLTIP_WITHOUTBUFF, count), 1, 0.3, 0.1)
						end
						XTooltip:AddLine(names, 0.8, 0.5, 0.4, 1)
					end
				end
			end
		end

		XTooltip:Show()
	end
end

-- XPerl_Raid_AddDebuffDuration
function XPerl_Raid_AddDebuffDuration(partyid, x, id, cureCast)
	local XTooltip
	if (x) then
		XTooltip = x
	else
		XTooltip = GameTooltip
	end

	if (GetNumRaidMembers() > 0 and XTooltip:IsOwned(this)) then
		if (partyid ~= "player") then
			local stats = XPerl_Roster[UnitName(partyid)]

			if (stats and stats.Debuffs) then
				local buffName = XPerl_UnitDebuff(partyid, id, cureCast)

				if (buffName) then
					buffName = strsub(buffName, 17)
					local buff = stats.Debuffs[buffName]
					if (buff) then
						if (buff > GetTime()) then
							XTooltip:AddLine(SecondsToTime(buff - GetTime())..XPERL_RAID_TOOLTIP_REMAINING, 1, 0.82, 0)
						end
					end
				end
			end
		end

		XTooltip:Show()
	end
end

-- SetMainHeaderAttributes
local function SetMainHeaderAttributes(self)
	if (conf.SortRaidAlpha == 1) then
		self:SetAttribute("sortMethod", "NAME")
	else
		self:SetAttribute("sortMethod", nil)
	end

	self:SetAttribute("point", conf.RaidAnchor)
	self:SetAttribute("minWidth", 80)
	self:SetAttribute("minHeight", 10)
	local titleFrame = self:GetParent()
	self:ClearAllPoints()
	if (conf.RaidAnchor == "TOP") then
		self:SetPoint("TOP", titleFrame, "BOTTOM", 0, 0)
		self:SetAttribute("xOffset", 0)
		self:SetAttribute("yOffset", -conf.RaidVerticalSpacing)
	elseif (conf.RaidAnchor == "LEFT") then
		self:SetPoint("TOPLEFT", titleFrame, "BOTTOMLEFT", 0, 0)
		self:SetAttribute("xOffset", conf.RaidVerticalSpacing)
		self:SetAttribute("yOffset", 0)
	elseif (conf.RaidAnchor == "BOTTOM") then
		self:SetPoint("BOTTOM", titleFrame, "TOP", 0, 0)
		self:SetAttribute("xOffset", 0)
		self:SetAttribute("yOffset", conf.RaidVerticalSpacing)
	elseif (conf.RaidAnchor == "RIGHT") then
		self:SetPoint("TOPRIGHT", titleFrame, "BOTTOMRIGHT", 0, 0)
		self:SetAttribute("xOffset", -conf.RaidVerticalSpacing)
		self:SetAttribute("yOffset", 0)
	end
	self:SetAttribute("template", "XPerl_Raid_FrameTemplate")
	self:SetAttribute("templateType", "Button")

	self.initialConfigFunction = function(self)
		-- This is the only place we're allowed to set attributes whilst in combat
		Setup1RaidFrame(self)

		self:SetAttribute("*type1", "target")
		self:SetAttribute("type2", "menu")
		self.menu = XPerl_Raid_ShowPopup

		-- Does AllowAttributeChange work for children?
		self.nameFrame:SetAttribute("useparent-unit", true)
		self.nameFrame:SetAttribute("*type1", "target")
		self.nameFrame:SetAttribute("type2", "menu")
		self.nameFrame.menu = XPerl_Raid_ShowPopup

		if (conf.RaidMana == 1) then
			self:SetAttribute("initial-height", 44)
		else
			self:SetAttribute("initial-height", 39)
		end
	end
end

-- XPerl_Raid_SetAttributes
function XPerl_Raid_ChangeAttributes()

	if (InCombatLockdown()) then
		tinsert(XPerl_OutOfCombatQueue, XPerl_Raid_ChangeAttributes)
		return
	end

	conf.RaidAnchor = conf.RaidAnchor or "TOP"

	local function GroupFilter(n)
		if (conf.SortRaidByClass == 1) then
			return conf["RaidClass"..n]
		else
			local f
			if (conf["ShowGroup"..n]) then
				f = tostring(n)
			end

			for i = 1,9 do
				if (conf["RaidClass"..i.."Enable"] == 1) then
					if (not f) then
						f = conf["RaidClass"..i]
					else
						f = f..","..conf["RaidClass"..i]
					end
				end
			end
			return f
		end
	end

	for i = 1,8+conf.SortRaidByClass do
		local groupHeader = getglobal(XPERL_RAIDGRP_PREFIX..i)
		--local s = groupHeader:IsShown()

		-- Hide this when we change attributes, so the whole re-calc is only done once, instead of for every attribute change
		groupHeader:Hide()

		groupHeader:SetAttribute("strictFiltering", conf.SortRaidByClass == 0)
		groupHeader:SetAttribute("groupFilter", GroupFilter(i))

		SetMainHeaderAttributes(groupHeader)

		--if (s) then
		--	groupHeader:Show()
		--end
	end

	XPerl_Raid_HideShowRaid()
end

-- XPerl_Raid_Set_Bits
function XPerl_Raid_Set_Bits()
	XPerl_ProtectedCall(XPerl_Raid_HideShowRaid)
	XPerl_ScaleRaid()
	for i,frame in pairs(FrameArray) do
		Setup1RaidFrame(frame)
	end
end
