local XPerl_Raid_Events = {}
local RaidGroupCounts = {0,0,0,0,0,0,0,0,0}
local FrameArray = {}
local perlSetupDone = nil
local ResArray = {}
local XUpdated = nil

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 = {}

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

----------------------
-- 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", "ADDON_LOADED",
			"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"}
	for i,event in pairs(events) do
		self:RegisterEvent(event)
	end

	self.time = 0
	self.Array = {}
end

-- Setup1RaidFrame
local function Setup1RaidFrame(self)
	local frameStats = getglobal(self:GetName().."_StatsFrame")
	local frameMana = getglobal(self:GetName().."_StatsFrame_ManaBar")

	if (XPerlConfig.RaidMana == 1) then
		if (not InCombatLockdown()) then
			self:SetHeight(44)
		end
		frameStats:SetHeight(30)
		frameMana:Show()
	else
		if (not InCombatLockdown()) then
			self:SetHeight(39)
		end
		frameStats:SetHeight(25)
		frameMana:Hide()
	end
end

-- Non local by request
function XPerl_Raid_FindID(self)
	return strmatch(self:GetName(), "^XPerl_Raid_Grp%dUnitButton(%d+)")
end

-- ShowPopup
local function ShowPopup(self)
	HideDropDownMenu(1)
	FriendsDropDown.initialize = XPerl_RaidFrameDropDown_Initialize
	FriendsDropDown.displayMode = "MENU"

	self.unit = self:GetAttribute("unit")
	self.name = UnitName(self.unit)
	self.id = strmatch(self.unit, "(%d+)")

	ToggleDropDownMenu(1, nil, FriendsDropDown, self:GetName(), 0, 0)

	self.unit, self.name, self.id = nil, nil, nil
end

-- onAttrChanged
local function onAttrChanged(self, name, value)
	if (name == "unit") then
		if (value) then
			FrameArray[value] = self

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

-- XPerl_Raid_Single_OnLoad
function XPerl_Raid_Single_OnLoad(self)

	local tex = getglobal(self:GetName().."HighlightTex")
	tex:SetVertexColor(0.86, 0.82, 0.41)

	XPerl_RegisterPerlFrames(self, {"NameFrame", "StatsFrame"})

	self.FlashFrames = {getglobal(self:GetName().."_NameFrame"), getglobal(self:GetName().."_StatsFrame")}

	--Setup1RaidFrame(self)

	self:SetScript("OnAttributeChanged", onAttrChanged)

	XPerl_RegisterClickCastFrame(self)
end

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

	local name = UnitName(partyid)

	if (name or self.pet) then
		local frame = getglobal(self:GetName().."_NameFrame_NameBarText")
		local frameParent = getglobal(self:GetName().."_NameFrame")

		if (self.pet and name) then
			self.petname = name	-- Store it for when pet is absent
		end

		if (not name) then
			if (self.petname) then
				name = self.petname
			else
				name = UnitName(self.petOwner).."'s "..PET
			end
		end

                frame:SetText(name)

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

		if (self.pet) then
			local color = XPerlConfig.ColourReactionNone
			frame:SetTextColor(color.r, color.g, color.b)
		else
			XPerl_ColourFriendlyUnit(frame, 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.afk) then
			return {flag = XPERL_RAID_AFK}

		elseif (unitInfo.dnd) then
			return {flag = XPERL_RAID_DND}

		elseif (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
		end
	end
end

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

	local flags
	if (not skipFlags) then
		flags = XPerl_Raid_CheckFlags(partyid)
	end
	if (not flags) then
		if (XPerlConfig.RaidMana == 1) then
			local frame = getglobal(self:GetName().."_StatsFrame_ManaBar")
			local frameBG = getglobal(self:GetName().."_StatsFrame_ManaBarBG")
			XPerl_SetManaBarType(partyid, frame)
		end
	end
end

-- XPerl_Raid_ShowFlags
local function XPerl_Raid_ShowFlags(self, flags)
	--self.flags = flags
	local f, color, bgcolor

	if (flags.bgcolor) then
		bgcolor = flags.bgcolor
	else
		bgcolor = {r = 0.5, g = 0.5, b = 0.5}
	end

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

	f = getglobal(self:GetName().."_StatsFrame_HealthBar"); if (f) then f:SetStatusBarColor(bgcolor.r, bgcolor.g, bgcolor.b); end
	f = getglobal(self:GetName().."_StatsFrame_HealthBarBG"); if (f) then f:SetVertexColor(bgcolor.r, bgcolor.g, bgcolor.b, 0.5); end
	f = getglobal(self:GetName().."_StatsFrame_ManaBar"); if (f) then f:SetStatusBarColor(bgcolor.r, bgcolor.g, bgcolor.b); end
	f = getglobal(self:GetName().."_StatsFrame_ManaBarBG"); if (f) then f:SetVertexColor(bgcolor.r, bgcolor.g, bgcolor.b, 0.5); end
	f = getglobal(self:GetName().."_StatsFrame_HealthBarText")
	if (f) then
		f:SetText(flags.flag)
		f:SetTextColor(color.r, color.g, color.b)
	end
end

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

	local health = UnitHealth(partyid)
	local healthmax = UnitHealthMax(partyid)
	local healthBar = getglobal(self:GetName().."_StatsFrame_HealthBar")
	local frameText = getglobal(self:GetName().."_StatsFrame_HealthBarText")

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

	healthBar:SetMinMaxValues(0, healthmax)
	if (XPerlConfig.InverseBars == 1) then
		healthBar:SetValue(healthmax - health)
	else
		healthBar:SetValue(health)
	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)
	        		healthBar:SetMinMaxValues(0, 1)
	        		healthBar:SetValue(0)
				frameText:SetText(XPERL_LOC_FEIGNDEATH)
				XPerl_SetSmoothBarColor(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 = string.format("%3.0f", percentHp * 100)

			if (XPerlConfig.HealerModeRaid == 1) then
				frameText:SetText(-(healthmax - health))
			else
				frameText:SetText(phealthPct.."%")
			end

			XPerl_SetSmoothBarColor(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
		return
	end

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

	local manaBar = getglobal(self:GetName().."_StatsFrame_ManaBar")
	local manaBarText = getglobal(self:GetName().."_StatsFrame_ManaBarText")

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

	manaBar:SetMinMaxValues(0, manamax)
	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)
	for i = 1,GetNumRaidMembers() do
		if (UnitName("raid"..i) == findName) then
			return i
		end
	end
end

-- XPerl_Raid_GetUnitFrameByName
function XPerl_Raid_GetUnitFrameByName(findName)
	local id = XPerl_GetRaidPosition(findName)
	if (id) then
		return FrameArray["raid"..id]
	end
end

-- UpdateUnitByName
local function UpdateUnitByName(name,flagsOnly)
	local id = XPerl_GetRaidPosition(name)
	if (id) then
		local frame = FrameArray["raid"..id]
		if (frame) then
			if (flagsOnly) then
				XPerl_Raid_UpdateHealth(frame)
			else
				XPerl_Raid_UpdateDisplay(frame)
			end
		end
	end
end

-- XPerl_Raid_OnUpdate
-- 1.8.3 Converted OnUpdate to 1 single routine for all frames, instead of calling
-- potentially the same function 40 times Globally, this is more efficient on time
-- and memory usage. It does remove from the concept of object oriented updates
-- and such, but X-Perl is a rather intensive mod that is the focus of much of the
-- user interface, so I felt it was important to be over cautious.
function XPerl_Raid_OnUpdate(self, elapsed)

	XUpdated = nil

	local updateTanker, tankerName
	if (XPerlConfig.RaidHighlightTanker == 1) then
		self.time = self.time + elapsed
		if (self.time >= 0.2) then
			self.time = 0
			updateTanker = true

			if (UnitExists("target")) then
				if (UnitCanAttack("player", "target")) then
					if (UnitInRaid("targettarget")) then
						tankerName = UnitName("targettarget")
					end
				elseif (UnitCanAttack("player", "targettarget")) then
					if (UnitInRaid("targettargettarget")) then
						tankerName = UnitName("targettargettarget")
					end
				end
			end
		end
	end

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

			if (updateTanker) then
				local set
				if (tankerName) then
					set = (UnitName(frame:GetAttribute("unit")) == tankerName)
				end

				local highlightFrame = getglobal(frame:GetName().."Highlight")
				if ((XPerlConfig.HighlightSelection == 1 and MouseIsOver(highlightFrame)) or set) then
					highlightFrame:Show()
				else
					highlightFrame:Hide()
				end
			end
		end
	end
end

-- CheckRosterBuffs
-- Check buffs exist in roster that we've received, if not then strip them out
local function CheckRosterBuffs(unitName)
	local id = XPerl_GetRaidPosition(unitName)
	if (id) then
		id = "raid"..id
		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

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

	local name = self:GetName().."_BuffFrame_Buff"..buffnum
	local button = getglobal(name)

	if (not button and createIfAbsent) then
		button = CreateFrame("Button", name, getglobal(self:GetName().."_BuffFrame"), "XPerl_BuffTemplate")
		button:SetID(buffnum)

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

		local icon = getglobal(name.."Icon")
		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)
	local show,cureCast
	if (self:GetID() == 99) then
		-- Pullout frames all have ID 99
		local RaidPullout = self:GetParent()

		if (RaidPullout.showBuffs == 1) then
			show = "b"
		else
			show = "d"
		end
		if (RaidPullout.cureCast) then
			cureCast = 1
		else
			cureCast = 0
		end
	else
		if (XPerlConfig.RaidBuffs == 1) then
			show = "b"
		elseif (XPerlConfig.RaidDebuffs == 1) then
			show = "d"
		end
		cureCast = XPerlConfig.BuffsCastableCurable
	end

	return show,cureCast
end

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

	local cursed, color = XPerl_CheckDebuffs(self)

	local frameText = getglobal(self:GetName().."_NameFrame_NameBarText")
	if (cursed) then
		frameText:SetTextColor(1,0,0)
	else
		XPerl_ColourFriendlyUnit(frameText, partyid)
	end

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

	local show, cureCast = GetShowCast(self)
	if (show) then
		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
				local icon = getglobal(button:GetName().."Icon")

				if (buff) then
					buffCount = buffCount + 1

					icon:SetTexture(buff)
					button:Show()
				else
					button:Hide()
				end
			end
		end
		for buffnum=maxBuff+1,8 do
                        local button = getglobal(self:GetName().."_BuffFrame_Buff"..buffnum)
			if (button) then
				button:Hide()
			end
		end
	end

	CheckRosterBuffs(partyid)

	local buffFrame = getglobal(self:GetName().."_BuffFrame")
	local nameFrame = getglobal(self:GetName().."_NameFrame")
	local statsFrame = getglobal(self:GetName().."_StatsFrame")

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

		if (XPerlConfig.RaidBuffsRight == 1 or id == 99) then
			buffFrame:SetPoint("BOTTOMLEFT", self:GetName().."_StatsFrame", "BOTTOMRIGHT", -2, 2)

			if (XPerlConfig.RaidBuffsInside == 1 and id ~= 99) then
				if (buffCount > 5 + XPerlConfig.RaidMana) then
					nameFrame:SetWidth(60)
				elseif (buffCount > 2) then
					nameFrame:SetWidth(70)
				else
					nameFrame:SetWidth(80)
				end

				if (buffCount > 3 + XPerlConfig.RaidMana) then
					statsFrame:SetWidth(60)
				else
					statsFrame:SetWidth(70)
				end
			else
				nameFrame:SetWidth(80)
				statsFrame:SetWidth(80)
			end

			getglobal(self:GetName().."_BuffFrame_Buff1"):ClearAllPoints()
			getglobal(self:GetName().."_BuffFrame_Buff1"):SetPoint("BOTTOMLEFT", 0, 0)
			for i = 2,buffCount do
				if (i > buffCount) then break end

				local buffI = getglobal(self:GetName().."_BuffFrame_Buff"..i)
				buffI:ClearAllPoints()

				if (i == 4 + XPerlConfig.RaidMana) then
					if (XPerlConfig.RaidBuffsInside == 1 and id ~= 99) then
						buffI:SetPoint("BOTTOMLEFT", 0, 0)
						getglobal(self:GetName().."_BuffFrame_Buff1"):SetPoint("BOTTOMLEFT", buffI, "BOTTOMRIGHT", 0, 0)
					else
						buffI:SetPoint("BOTTOMLEFT", self:GetName().."_BuffFrame_Buff"..(i-(4 - abs(1 - XPerlConfig.RaidMana))), "BOTTOMRIGHT", 0, 0)
					end
				else
					buffI:SetPoint("BOTTOMLEFT", self:GetName().."_BuffFrame_Buff"..(i-1), "TOPLEFT", 0, 0)
				end
			end
		else
			nameFrame:SetWidth(80)
			statsFrame:SetWidth(80)

			buffFrame:SetPoint("TOPLEFT", self:GetName().."_StatsFrame", "BOTTOMLEFT", 0, 2)

			local prevBuff
			for i = 1,buffCount do
				local buff = getglobal(self:GetName().."_BuffFrame_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
		nameFrame:SetWidth(80)
		statsFrame:SetWidth(80)
		buffFrame: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

-- UpdateRosterBuffs
-- Scans a unit's buffs and sets up initial buff time remaining entries
local duplicateTextures = {
	["Interface\\Icons\\Spell_Nature_Regeneration"] = true,
	["Interface\\Icons\\Spell_Nature_LightningShield"] = true,
	["Interface\\Icons\\Spell_Shadow_AntiShadow"] = true,
	["Interface\\Icons\\Spell_Holy_PrayerOfHealing02"] = true,
};

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
}

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 name, rank, buff = UnitBuff(unit, 1);
		local num = 1
		while (buff) do
			if (not duplicateTextures[buff]) then
				for k, v in pairs(XPerl_CTBuffTextures) do
					if ("Interface\\Icons\\"..v[1] == buff) then
						buffName = k
						break
					end
				end
			else
				XPerlBuffStatusTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
				XPerlBuffStatusTooltipTextLeft1:SetText("")
				XPerlBuffStatusTooltip:SetUnitBuff(unit, num)
				local tooltipName = XPerlBuffStatusTooltipTextLeft1:GetText()
				if (strlen(tooltipName or "") > 0 and XPerl_CTBuffTextures[tooltipName]) then
					buffName = tooltipName
				end
			end

			if (buffName) then
				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
			end

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

		num, buff = 1, UnitDebuff(unit, 1);
		while (buff) 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
			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
end

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

	if (UnitIsVisible(partyid) and UnitIsCharmed(partyid)) then
		getglobal(self:GetName().."_NameFrame_Warning"):Show()
	else
		getglobal(self:GetName().."_NameFrame_Warning"):Hide()
	end
end

-- XPerl_Raid_UpdateCombat
local function XPerl_Raid_UpdateCombat(self)
	local frame = getglobal(self:GetName().."_NameFrame_ActivityStatus")
	if (not (frame == nil)) then
		local partyid = self:GetAttribute("unit")
	        if (UnitExists(partyid) and UnitAffectingCombat(partyid)) then
	                frame:Show()
		else
	                frame:Hide()
		end
	end
end

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

	if (UnitIsConnected(partyid)) then
		self:SetAlpha(XPerlConfig.Transparency)
	else
		self:SetAlpha(XPerlConfig.Transparency/2)
		if (healthBarText) then
			healthBarText:SetText("Offine")
		end
	end
end

-------------------------
-- The Update Function --
-------------------------
function XPerl_Raid_UpdateDisplay(self)
	local partyid = self:GetAttribute("unit")
	if (partyid and UnitExists(partyid) or self.pet) then
		local healthBarText = getglobal(self:GetName().."_StatsFrame_HealthBarText")
		local manaBarText = getglobal(self:GetName().."_StatsFrame_ManaBarText")

		SetFrameAlpha(self)

		if (healthBarText and manaBarText) then
			if (XPerlConfig.ShowRaidPercents == 0) then
				manaBarText:Hide()
					healthBarText:Hide()
			else
				manaBarText:Show()
				healthBarText:Show()
			end
		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)
	end
end

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

		if (XPerlConfig["ShowGroup"..i] == 1 and XPerlConfig.ShowRaid == 1) then
			frame:Show()
			SecureRaidGroupHeader_Update(frame)
		else
			frame:Hide()
		end
	end
end

-------------------
-- Event Handler --
-------------------
function XPerl_Raid_OnEvent(self, event)
	local func = XPerl_Raid_Events[event]
	if (func) then
		if (strmatch(event, "^UNIT_")) then
			local f = FrameArray[arg1]
			if (f) then
				func(f)
			end
		else
			func(self)
		end
	else
XPerl_ShowMessage("EXTRA EVENT")
	end
end

-- ADDON_LOADED
function XPerl_Raid_Events:ADDON_LOADED()
	if (arg1 == "Blizzard_RaidUI") then
		if (CT_RATab_newRaidFrameDropDown_Initialize) then
			if (not oldCT_RATab_newRaidFrameDropDown_Initialize) then
				oldCT_RATab_newRaidFrameDropDown_Initialize = CT_RATab_newRaidFrameDropDown_Initialize
				CT_RATab_newRaidFrameDropDown_Initialize = XPerl_CT_RATab_newRaidFrameDropDown_Initialize
				--RaidFrameDropDown_Initialize = XPerl_CTRaidFrameDropDown_Initialize
			end
		end

		--XPerl_ReplaceBlizzardPullouts()

		self:UnregisterEvent("ADDON_LOADED")
	end
end

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_ScaleRaid(XPerlConfig.Scale_Raid)

	XPerl_ProtectedCall(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

-- 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 (XPerlConfig.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)

	--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
		if (XPerl_Roster[resTargetName]) then
			if (resEnd) then
				if (not XPerl_Roster[resTargetName].resCount) then
					XPerl_Roster[resTargetName].resCount = 1
				else
					XPerl_Roster[resTargetName].resCount = XPerl_Roster[resTargetName].resCount + 1
				end
			end
			UpdateUnitByName(resTargetName)
		end
	end
end

-- 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
		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
					local 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
		UpdateUnitByName(unitName)
	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

	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 (XPerlConfig.SortRaidByClass == 1) then
			RaidGroupCounts[groupIndex[fileName]] = RaidGroupCounts[groupIndex[fileName]] + 1
		else
			RaidGroupCounts[group] = RaidGroupCounts[group] + 1
		end

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

		if (XPerl_Roster[name]) then
			NewRoster[name] = XPerl_Roster[name]
		else
			NewRoster[name] = {}
		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_CT_RATab_newRaidFrameDropDown_Initialize()
	--XPerl_RaidFrameDropDown_Initialize(true)
end

function XPerl_RaidFrameDropDown_Initialize(ct)
	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 = string.format("|c00FFFF80"..XPERL_RAID_DROPDOWN_REMOVEMT.."|r", i)
				info.value = {UIDROPDOWNMENU_MENU_VALUE[1], UIDROPDOWNMENU_MENU_VALUE[2], i, 1}
			else
				info.text = string.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 (IsRaidOfficer()) then
		if (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}
		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]
		info.value = this.id
		info.func = CT_RATab_AutoPromote_OnClick
		UIDropDownMenu_AddButton(info)
	end
end

-- XPerl_MainTankSet_OnClick
function XPerl_MainTankSet_OnClick()
	if (this.value[1] == "Main Tanks") then
		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(num)
	for frame = 1,9 do
		local f = getglobal("XPerl_Raid_Title"..frame)
		if (f) then
			f:SetScale(num)
		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().."_NameFrame_NameBarText")

		if (XPerlConfig.SortRaidByClass == 1) then
			if (ClassNames[groups[i]]) then
				titleFrame:SetText(ClassNames[groups[i]])
			else
				titleFrame:SetText(localGroups[i])
			end
		else
			titleFrame:SetText(string.format(XPERL_RAID_GROUP, i))
		end

		if (XPerlLocked == 0 or (XPerlConfig.ShowRaidTitles == 1 and RaidGroupCounts[i] > 0 and XPerlConfig.ShowRaid == 1 and XPerlConfig["ShowGroup"..i] == 1)) then
			titleFrame:Show()
		else
			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 (XPerl_Raid_Grp1 and XPerl_Raid_Grp2) then
		return math.floor((math.floor((XPerl_Raid_Grp2:GetLeft() - XPerl_Raid_Grp1:GetRight() + 0.01) * 100) / 100) + 4)
	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 do
			tinsert(ret, getglobal("XPerl_Raid_Title"..i))
		end
	elseif (interest == "even") then
		for i = 2,9 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()

		local left = frames[1]:GetLeft()
		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
	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 partyid = self:GetParent():GetParent():GetAttribute("unit")
	GameTooltip:SetOwner(self,"ANCHOR_BOTTOMRIGHT",30,0)

	local show, cureCast = GetShowCast(self:GetParent():GetParent())
	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 (XPerlConfig.BuffsCastableCurable == 1) then
			XPerlConfig.BuffsCastableCurable = 0
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_BUFFANY)
		else
			XPerlConfig.BuffsCastableCurable = 1
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_BUFFCURECAST)
		end
	else
		if (XPerlConfig.RaidBuffs == 1) then
			XPerlConfig.RaidBuffs = 0
			XPerlConfig.RaidDebuffs = 1
			XPerl_Notice(XPERL_KEY_NOTICE_RAID_DEBUFFS)

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

		else
			XPerlConfig.RaidBuffs = 1
			XPerlConfig.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.offline) then
				GameTooltip:AddLine(string.format(XPERL_RAID_TOOLTIP_OFFLINE, SecondsToTime(t - stats.offline)))

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

			elseif (stats.dnd) then
				GameTooltip:AddLine(string.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(string.format(XPERL_RAID_TOOLTIP_DYING, SecondsToTime(x)))
					end
				end
			end

			if (stats.Rebirth) then
				if (stats.Rebirth - t > 0) then
					GameTooltip:AddLine(string.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(string.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(string.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 function GetNamesWithoutBuff(tex, with)
	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"}
	}

	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 buffTexture) then
					break
				end

				if (buffTexture == tex) then
					hasBuff = true
					break
				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
							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 buffName
									for k,v in pairs(XPerl_CTBuffTextures) do
										if (v[1] == "Interface\\Icons\\"..buffTexture) then
											buffName = k
											break
										end
									end

									if (buffName) then
										if (myRoster.Buffs[buffName]) then
											local expire = myRoster.Buffs[buffName]
											if (expire > GetTime() + (found*60)) then
												GameTooltip:AddLine(string.format(XPERL_RAID_TOOLTIP_BUFFEXPIRING, XPerlColourTable[unitClass]..name.."|c", buffName, SecondsToTime(buff - GetTime())), 1, 0.2, 0)
											end
										end
									end
								end
							end
						end
						break
					end
				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 (UnitInRaid("player") 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 (XPerlConfig.BuffTooltipHelper == 1 and partyid and UnitInRaid(partyid)) then
			local icon = getglobal(this:GetName().."Icon")
			if (icon) then
				local tex = icon:GetTexture()
				if (tex) then
					local names, count = GetNamesWithoutBuff(tex, IsAltKeyDown())
					if (names) then
						if (IsAltKeyDown()) then
							XTooltip:AddLine(string.format(XPERL_RAID_TOOLTIP_WITHBUFF, count), 0.3, 1, 0.2)
						else
							XTooltip:AddLine(string.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 (UnitInRaid("player") 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

-- XPerl_Raid_SetAttributes
function XPerl_Raid_ChangeAttributes()

	XPerlConfig.RaidAnchor = XPerlConfig.RaidAnchor or "TOP"

	for i = 1,9 do
		local frame = getglobal(XPERL_RAIDGRP_PREFIX..i)

		if (XPerlConfig["ShowGroup"..i]) then
			if (XPerlConfig.SortRaidByClass == 1) then
				frame:SetAttribute("groupFilter", groups[i])
			else
				frame:SetAttribute("groupFilter", i)
			end
		else
			frame:SetAttribute("groupFilter", "none")
		end

		if (XPerlConfig.SortRaidAlpha == 1) then
			frame:SetAttribute("sortMethod", "NAME")
		else
			frame:SetAttribute("sortMethod", nil)
		end

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

		frame.initialConfigFunction = function(self)
			local id = XPerl_Raid_FindID(self)
			Setup1RaidFrame(self)

			self:SetAttribute("*type1", "target")
			self:SetAttribute("type*", "menu")
			self.menu = ShowPopup

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

	XPerl_ProtectedCall(XPerl_Raid_HideShowRaid)
end

-- XPerl_Raid_Set_Bits
function XPerl_Raid_Set_Bits()
	XPerl_ProtectedCall(XPerl_Raid_HideShowRaid)

	for i,frame in pairs(FrameArray) do
		Setup1RaidFrame(frame)
		if (frame:IsShown()) then
			XPerl_Raid_UpdateName(frame)
			SetFrameAlpha(frame)
			XPerl_CheckDebuffs(frame)
		end
	end
end
