-- Colour tables now taken from WoW values (Since 1.6.4) - Well, we want to be ready when there's Paladin and Shaman in same raid
-- and we can leave the colour choice up to Blizzard and it'll just all work.

-- We craeted our string based colour table from Blizzard's raid colour table in
XPerlColourTable = {}
for class,c in pairs(RAID_CLASS_COLORS) do
	XPerlColourTable[class] = string.format("|c00%02X%02X%02X", 255 * c.r, 255 * c.g, 255 * c.b)
end

XPerl_AnchorList = {"TOP", "LEFT", "BOTTOM", "RIGHT"}

-- XPerl_GetClassColour
function XPerl_GetClassColour(class)
	if (class) then
		local color = RAID_CLASS_COLORS[class];		-- Now using the WoW class color table
		if (color) then
			return color
		end
	end
	return {r = 0.5, g = 0.5, b = 1}
end

-- XPerl_IsFeignDeath(unit)
function XPerl_IsFeignDeath(unit)
	for i = 1,20 do
		local name, rank, buff = UnitBuff(unit, i)

		if (buff) then
			if (strfind(strlower(buff), "feigndeath")) then
				return true
			end
		else
			break
		end
	end
end

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

-- XPerl_BlizzFramesDisable
function XPerl_BlizzFramesDisable()
	local XPerl_DummyFrame
	local XPerl_DummyFunc

	if (IsAddOnLoaded("XPerl_Player")) then
		-- Blizz Player Frame Events
		PlayerFrame:UnregisterAllEvents()
		PlayerFrameHealthBar:UnregisterAllEvents()
		PlayerFrameManaBar:UnregisterAllEvents()
		PlayerFrame:Hide()
		-- Make it so it won't be visible, even if shown by another mod
		PlayerFrame:ClearAllPoints()
		PlayerFrame:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT", 0, 50)
	end

	if (IsAddOnLoaded("XPerl_PlayerPet")) then
		-- Blizz Pet Frame Events
		PetFrame:UnregisterAllEvents()
		PetFrame:Hide()
		PetFrame:ClearAllPoints()
		PetFrame:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT", 0, 50)
	end

	if (IsAddOnLoaded("XPerl_Target")) then
		-- Blizz Target Frame Events
		TargetFrame:UnregisterAllEvents()
		TargetFrameHealthBar:UnregisterAllEvents()
		TargetFrameManaBar:UnregisterAllEvents()
		TargetFrame:Hide()
		TargetofTargetFrame:UnregisterAllEvents()
		TargetofTargetFrame:Hide()
		TargetFrame:ClearAllPoints()
		TargetFrame:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT", 0, 50)
		TargetofTargetFrame:ClearAllPoints()
		TargetofTargetFrame:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT", 0, 50)
	end

	if (IsAddOnLoaded("XPerl_Party")) then
		-- Blizz Party Events
		HIDE_PARTY_INTERFACE = "1"
		hooksecurefunc("ShowPartyFrame",
			function()
				if (not InCombatLockdown()) then
					for i = 1,4 do
						getglobal("PartyMemberFrame"..i):Hide()
					end
				end
			end
		)

		for num = 1, 4 do
			local f = getglobal("PartyMemberFrame"..num)
			f:Hide()
			f:UnregisterAllEvents()
			getglobal("PartyMemberFrame"..num.."HealthBar"):UnregisterAllEvents()
			getglobal("PartyMemberFrame"..num.."ManaBar"):UnregisterAllEvents()

			f:ClearAllPoints()
			f:SetPoint("BOTTOMLEFT", UIParent, "TOPLEFT", 0, 50)
		end
	end
	XPerl_BlizzFramesDisable = nil
end

---------------------------------
--Smooth Health Bar Color      --
---------------------------------
function XPerl_SetSmoothBarColor (bar, percentage)
	if (bar) then
		local r, g, b
		if (XPerlConfig.ClassicHealthBar == 1) then
			if (percentage < 0.5) then
				r = 1
				g = 2*percentage
				b = 0
			else
				g = 1
				r = 2*(1 - percentage)
				b = 0
			end
		else
			r = XPerlConfig.ColourHealthEmpty.r + ((XPerlConfig.ColourHealthFull.r - XPerlConfig.ColourHealthEmpty.r) * percentage)
			g = XPerlConfig.ColourHealthEmpty.g + ((XPerlConfig.ColourHealthFull.g - XPerlConfig.ColourHealthEmpty.g) * percentage)
			b = XPerlConfig.ColourHealthEmpty.b + ((XPerlConfig.ColourHealthFull.b - XPerlConfig.ColourHealthEmpty.b) * percentage)
		end

		if (r >= 0 and g >= 0 and b >= 0 and r <= 1 and g <= 1 and b <= 1) then
			bar:SetStatusBarColor(r, g, b)

			local backBar = getglobal(bar:GetName().."BG")
			if (backBar) then
				backBar:SetVertexColor(r, g, b, 0.25)
			end
		end
	end
end

-- XPerl_SetHealthBar
function XPerl_SetHealthBar(bar, hp, max, healerMode, healerModeType)
	bar:SetMinMaxValues(0, max)
	if (XPerlConfig.InverseBars == 1) then
		bar:SetValue(max - hp)
	else
		bar:SetValue(hp)
	end

	local percent = hp / max
	local healthPct = string.format("%3.0f", percent * 100)

	XPerl_SetSmoothBarColor(bar, percent)

	local barPct = getglobal(bar:GetName().."Percent")
	if (barPct) then
		barPct:SetText(healthPct.."%")
	end

	local barText = getglobal(bar:GetName().."Text")
	if (barText) then
		if (healerMode == 1) then
			if (healerModeType == 1) then
				barText:SetText(string.format("%d/%d", hp - max, max))
			else
				barText:SetText(hp - max)
			end
		else
			barText:SetText(hp.."/"..max)
		end
	end
end

---------------------------------
--Class Icon Location Functions--
---------------------------------
function XPerl_ClassPos (class)
	if(class=="WARRIOR") then return 0,    0.25,    0,	0.25;	end
	if(class=="MAGE")    then return 0.25, 0.5,     0,	0.25;	end
	if(class=="ROGUE")   then return 0.5,  0.75,    0,	0.25;	end
	if(class=="DRUID")   then return 0.75, 1,       0,	0.25;	end
	if(class=="HUNTER")  then return 0,    0.25,    0.25,	0.5;	end
	if(class=="SHAMAN")  then return 0.25, 0.5,     0.25,	0.5;	end
	if(class=="PRIEST")  then return 0.5,  0.75,    0.25,	0.5;	end
	if(class=="WARLOCK") then return 0.75, 1,       0.25,	0.5;	end
	if(class=="PALADIN") then return 0,    0.25,    0.5,	0.75;	end
	return 0.25, 0.5, 0.5, 0.75	-- Returns empty next one, so blank
end

-- XPerl_Toggle
function XPerl_Toggle()
	if (XPerlLocked == 1) then
		XPerl_UnlockFrames()
	else
		XPerl_LockFrames()
	end
end

-- XPerl_UnlockFrames
function XPerl_UnlockFrames()
	EnableAddOn("XPerl_Options")
	if (not IsAddOnLoaded("XPerl_Options")) then
		UIParentLoadAddOn("XPerl_Options")
	end

	XPerlLocked = 0

	if (XPerl_Options) then
		XPerl_Options:Show()
		XPerl_Options:SetAlpha(0)
		XPerl_Options.Fading = "in"
	end

	if (XPerl_RaidTitles) then
		XPerl_RaidTitles()
	end
end

-- XPerl_LockFrames
function XPerl_LockFrames()
	XPerlLocked = 1
	if (XPerl_Options) then
		XPerl_Options.Fading = "out"
	end

	if (XPerl_RaidTitles) then
		XPerl_RaidTitles()
	end

	XPerl_OptionActions()
end

-- Minimap Icon
function XPerl_MinimapButton_OnClick(self)
	XPerl_Toggle()
end

-- XPerl_MinimapButton_Init
function XPerl_MinimapButton_Init(self)
	if(XPerlConfig.MinimapButtonShown == 1) then
		self:Show()
	else
		self:Hide()
	end
end

-- XPerl_MinimapButton_UpdatePosition
function XPerl_MinimapButton_UpdatePosition(self)
	self:SetPoint(
		"TOPLEFT",
		"Minimap",
		"TOPLEFT",
		54 - (78 * cos(XPerlConfig.MinimapButtonPosition)),
		(78 * sin(XPerlConfig.MinimapButtonPosition)) - 55
	)
	XPerl_MinimapButton_Init(self)
end

-- XPerl_MinimapButton_Dragging
function XPerl_MinimapButton_Dragging(self)
	local xpos,ypos = GetCursorPosition()
	local xmin,ymin = Minimap:GetLeft(), Minimap:GetBottom()

	xpos = xmin-xpos/UIParent:GetScale()+70
	ypos = ypos/UIParent:GetScale()-ymin-70

	XPerl_MinimapButton_SetPosition(self, math.deg(math.atan2(ypos,xpos)))
end

-- XPerl_MinimapButton_SetPosition
function XPerl_MinimapButton_SetPosition(self, v)
	if (v < 0) then
		v = v + 360
	end

	XPerlConfig.MinimapButtonPosition = v
	XPerl_MinimapButton_UpdatePosition(self)
end

-- XPerl_MinimapButton_OnEnter
function XPerl_MinimapButton_OnEnter(self)
	GameTooltip:SetOwner(self, "ANCHOR_LEFT")
	GameTooltip:SetText(XPerl_Version, 1, 1, 1)
	GameTooltip:AddLine(XPERL_MINIMAP_HELP1)
	GameTooltip:AddLine(XPERL_MINIMAP_HELP2)
	GameTooltip:Show()
end

-- XPerl_SetManaBarType
local ManaColours = {"ColourMana", "ColourRage", "ColourFocus", "ColourEnergy"}
function XPerl_SetManaBarType(argUnit, argFrame)
	local power = UnitPowerType(argUnit)
	if (power) then
		local colour = XPerlConfig[ManaColours[power + 1]]

		argFrame:SetStatusBarColor(colour.r, colour.g, colour.b, 1)

		local argFrameBG = getglobal(argFrame:GetName().."BG")
		argFrameBG:SetVertexColor(colour.r, colour.g, colour.b, 0.25)
	end
end

-- XPerl_PlayerTip
function XPerl_PlayerTip(unitid)

	if (not unitid or XPerlLocked == 0 or XPerlConfig.UnitTooltips == 0) then
		return
	end

        if (SpellIsTargeting()) then
                if (SpellCanTargetUnit(unitid)) then
                        SetCursor("CAST_CURSOR")
                else
                        SetCursor("CAST_ERROR_CURSOR")
                end
        end

	GameTooltip_SetDefaultAnchor(GameTooltip, this)
	GameTooltip:SetUnit(unitid)

	if (XPerl_RaidTipExtra) then
		XPerl_RaidTipExtra(unitid)
	end

	if (XPerlConfig.XPerlTooltipInfo == 1 and XPerl_GetUsage) then
		local xpUsage = XPerl_GetUsage(UnitName(unitid))
		if (xpUsage) then
			local xp, any = "|cFFD00000X-Perl|r "
			if (xpUsage.version) then
				xp = xp..xpUsage.version
			else
				xp = xp..XPerl_VersionNumber
			end
			any = true

			if (xpUsage.mods and IsShiftKeyDown()) then
				local modList = XPerl_DecodeModuleList(xpUsage.mods)
				if (modList) then
					xp = xp.." : |c00909090"..modList
				end
			end
			if (any) then
				GameTooltip:AddLine(xp, 1, 1, 1, 1)
				GameTooltip:Show()
			end
		end
	end
end

-- XPerl_PlayerTipHide
function XPerl_PlayerTipHide()
	if (XPerlConfig.FadingTooltip == 1) then
		GameTooltip:FadeOut()
	else
		GameTooltip:Hide()
	end
end

-- XPerl_ToolTip_AddBuffDuration
function XPerl_ToolTip_AddBuffDuration(partyid, x)
	if (XPerl_Raid_AddBuffDuration) then
		XPerl_Raid_AddBuffDuration(partyid, x)
	end
end

-- XPerl_ToolTip_AddBuffDuration
function XPerl_ToolTip_AddDebuffDuration(partyid, x, index, cureCast)
	if (XPerl_Raid_AddDebuffDuration) then
		XPerl_Raid_AddDebuffDuration(partyid, x, index, cureCast)
	end
end

-- XPerl_ColourFriendlyUnit
function XPerl_ColourFriendlyUnit(frame, partyid)
	local color
	if (UnitCanAttack("player", partyid) and UnitIsEnemy("player", partyid)) then	-- For dueling
		color = XPerlConfig.ColourReactionEnemy
	else
		if (XPerlConfig.ClassColouredNames == 1) then
			local _, engClass = UnitClass(partyid)
			color = XPerl_GetClassColour(engClass)
		else
			if (UnitIsPVP(partyid)) then
				color = XPerlConfig.ColourReactionFriend
			else
				color = XPerlConfig.ColourReactionNone
			end
		end
	end

	frame:SetTextColor(color.r, color.g, color.b, XPerlConfig.TextTransparency)
end

-- XPerl_ReactionColour
function XPerl_ReactionColour(argUnit)

        if (UnitPlayerControlled(argUnit) or not UnitIsVisible(argUnit)) then
                if (UnitFactionGroup("player") == UnitFactionGroup(argUnit)) then
                        if (UnitIsEnemy("player", argUnit)) then
				-- Dueling
				return XPerlConfig.ColourReactionEnemy

                        elseif (UnitIsPVP(argUnit)) then
				return XPerlConfig.ColourReactionFriend
			end
		else
                        if (UnitIsPVP(argUnit)) then
				if (UnitIsPVP("player")) then
					return XPerlConfig.ColourReactionEnemy
				else
					return XPerlConfig.ColourReactionNeutral
				end
			end
		end
	else
                if (UnitIsTapped(argUnit) and not UnitIsTappedByPlayer(argUnit)) then
                        return XPerlConfig.ColourTapped
                else
			local reaction = UnitReaction(argUnit, "player")
			if (reaction) then
				if (reaction >= 5) then
					return XPerlConfig.ColourReactionFriend
				elseif (reaction <= 2) then
					return XPerlConfig.ColourReactionEnemy
				elseif (reaction == 3) then
					return XPerlConfig.ColourReactionUnfriendly
				else
					return XPerlConfig.ColourReactionNeutral
				end
			else
				if (UnitFactionGroup("player") == UnitFactionGroup(argUnit)) then
					return XPerlConfig.ColourReactionFriend
				elseif (UnitIsEnemy("player", argUnit)) then
					return XPerlConfig.ColourReactionEnemy
				else
					return XPerlConfig.ColourReactionNeutral
				end
			end
		end
	end

	return XPerlConfig.ColourReactionNone
end

-- XPerl_SetUnitNameColor
function XPerl_SetUnitNameColor(argUnit,argFrame)
	local color
        if (UnitPlayerControlled(argUnit) or not UnitIsVisible(argUnit)) then
		-- 1.8.3 - Changed to override pvp name colours
		if (XPerlConfig.ClassColouredNames == 1) then
			local _, class = UnitClass(argUnit)
			color = XPerl_GetClassColour(class)
		else
			color = XPerl_ReactionColour(argUnit)
		end
	else
                if (UnitIsTapped(argUnit) and not UnitIsTappedByPlayer(argUnit)) then
                        color = XPerlConfig.ColourTapped
                else
			color = XPerl_ReactionColour(argUnit)
		end
	end

	argFrame:SetTextColor(color.r, color.g, color.b, XPerlConfig.TextTransparency)
end

-- XPerl_UnitEvents
unitEvents = {}
function XPerl_UnitEvents(self, eventArray, eventList)

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

--ChatFrame1:AddMessage("XPerl_UnitEvents - self:"..self:GetName()..", unit:"..(unit or "nil"))

	local a = unitEvents[unit]
	if (not a) then
		a = {}
		unitEvents[unit] = a
	end

	a.array = eventArray
	for k,v in pairs(eventList) do
		local selves = a[v]
		if (not selves) then
			selves = {}
			a[v] = selves
		end
		tinsert(selves, self)
		XPerl_Globals:RegisterEvent(v)
	end
end

function XPerl_UnitEvent(unit, event)
	local a = unitEvents[unit]
	if (a) then
		local selves = a[event]
		if (selves) then
			local array = a.array
			if (array) then
				for k,v in pairs(selves) do
--ChatFrame1:AddMessage("Event: |c0080FF80"..event.."|r, unit = |c00FFFF80"..unit.."|r, self = |c0080FFFF"..v:GetName())
					array[event](v)
				end
				return true
			end
		end
	end

--ChatFrame1:AddMessage("Event: |c0080FF80"..event.."|r, unit = |c00FFFF80"..unit.."|r, NOT SENT")
end

-- XPerl_RegisterBasics
function XPerl_RegisterBasics(self, events)
	XPerl_UnitEvents(self, events, {"UNIT_RAGE", "UNIT_MAXRAGE", "UNIT_ENERGY", "UNIT_MAXENERGY",
			"UNIT_MANA", "UNIT_MAXMANA", "UNIT_HEALTH", "UNIT_MAXHEALTH",
			"UNIT_LEVEL", "UNIT_DISPLAYPOWER", "UNIT_NAME_UPDATE"})
end

-- PerlSetPortrait3D
function XPerlSetPortrait3D(self, argUnit)
	self:ClearModel()
	self:SetUnit(argUnit)
	self:SetCamera(0)
end

-- XPerl_CombatFlashSet
function XPerl_CombatFlashSet (elapsed, self, argNew, argGreen)
	if (XPerlConfig.PerlCombatFlash == 0) then
		self.PlayerFlash = nil
		return false
	end

	if (self) then
		if (argNew) then
			self.PlayerFlash = 1
			self.PlayerFlashGreen = argGreen
		else
			if (self.PlayerFlash and elapsed) then
				self.PlayerFlash = self.PlayerFlash - elapsed

				if (self.PlayerFlash <= 0) then
					self.PlayerFlash = 0
					self.PlayerFlashTime = nil
					self.PlayerFlashGreen = nil
				end
			else
				return false
			end
		end

		return true
	end
end

-- XPerl_CombatFlashSetFrames
function XPerl_CombatFlashSetFrames(self)
	if (self.PlayerFlash) then
		local baseColour
		if (self.forcedColour) then
			baseColour = self.forcedColour
		else
			baseColour = XPerlConfig.BorderColour
		end

		local r, g, b, a
		if (self.PlayerFlash > 0) then
			local flashOffsetColour = self.PlayerFlash / 2
			if (self.PlayerFlashGreen) then
				r = baseColour.r - flashOffsetColour
				g = baseColour.g + flashOffsetColour
				b = baseColour.b - flashOffsetColour
				a = baseColour.a + flashOffsetColour
			else
				r = baseColour.r + flashOffsetColour
				g = baseColour.g - flashOffsetColour
				b = baseColour.b - flashOffsetColour
				a = baseColour.a + flashOffsetColour
			end

			if (r < 0) then r = 0; elseif (r > 1) then r = 1; end
			if (g < 0) then g = 0; elseif (g > 1) then g = 1; end
			if (b < 0) then b = 0; elseif (b > 1) then b = 1; end
			if (a < 0) then a = 0; elseif (a > 1) then a = 1; end
		else
			r, g, b = baseColour.r, baseColour.g, baseColour.b, baseColour.a
		end

		if (not a) then
			a = XPerlConfig.BorderColour.a
		end
		for i, frame in pairs(self.FlashFrames) do
			frame:SetBackdropBorderColor(r, g, b, a)
		end

		if (self.PlayerFlash == 0) then
			self.PlayerFlash = nil
		end
	end
end

local bgDef = {bgFile = "Interface\\Addons\\XPerl\\Images\\XPerl_FrameBack",
	       edgeFile = "",
	       tile = true, tileSize = 32, edgeSize = 16,
	       insets = { left = 5, right = 5, top = 5, bottom = 5 }
		}

-- XPerl_CheckDebuffs
function XPerl_CheckDebuffs(self)

	local unit = self:GetAttribute("unit")

	local conf = XPerlConfig
	if (conf.HighlightDebuffs == 0) then
		if (self.FlashFrames) then
			-- Reset the frame edges back to normal in case they changed options while debuffed.
			bgDef.edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border"
			for i, f in pairs(self.FlashFrames) do
				f:SetBackdrop(bgDef)
				f:SetBackdropColor(conf.BackColour.r, conf.BackColour.g, conf.BackColour.b, conf.BackColour.a)
				f:SetBackdropBorderColor(conf.BorderColour.r, conf.BorderColour.g, conf.BorderColour.b, conf.BorderColour.a)
			end
		end
		return
	end

	local show
	local Curses = {}
	local debuffCount = 0

	for i = 1, 40 do
		local _,_, debuff, debuffStack, debuffType = UnitDebuff(unit, i)

		if (not debuff) then
			break
		end

		if (debuffType) then
			Curses[debuffType] = debuffType
			debuffCount = debuffCount + 1
		end
	end

	if (debuffCount > 0) then
		if (XPerlConfig.HighlightDebuffsClass == 0) then
			show = Curses.Magic or Curses.Curse or Curses.Poison or Curses.Disease
		end

		-- We also re-set the colours here so that we highlight best colour per class
		local _, class = UnitClass("player")
		if (class == "MAGE") then
			show = Curses.Curse or show

		elseif (class == "DRUID") then
			show = Curses.Curse or Curses.Poison or show

		elseif (class == "PRIEST") then
			show = Curses.Magic or Curses.Disease or show

		elseif (class == "WARLOCK") then
			show = Curses.Magic or show

		elseif (class == "PALADIN") then
			show = Curses.Magic or Curses.Poison or Curses.Disease or show

		elseif (class == "SHAMAN") then
			show = Curses.Poison or Curses.Disease or show
		end
	end

	local colour, borderColour
	if (show) then
		colour = DebuffTypeColor[show]
		colour.a = 1

		if (conf.HighlightDebuffsBorder > 0) then
			borderColour = colour
		else
			borderColour = conf.BorderColour
		end
	else
		colour = conf.BackColour
		borderColour = conf.BorderColour
	end

	if (self.FlashFrames) then
		for i, f in pairs(self.FlashFrames) do
			if (show and conf.HighlightDebuffsBorder > 0) then
				f:GetParent().forcedColour = borderColour
				bgDef.edgeFile = "Interface\\Addons\\XPerl\\Images\\XPerl_Curse"
				f:SetBackdrop(bgDef)
			else
				f:GetParent().forcedColour = nil
				bgDef.edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border"
				f:SetBackdrop(bgDef)
			end

			if (XPerlConfig.HighlightDebuffsBorder == 2) then
				colour = conf.BackColour
			end
			f:SetBackdropColor(colour.r, colour.g, colour.b, colour.a)
			f:SetBackdropBorderColor(borderColour.r, borderColour.g, borderColour.b, borderColour.a)
		end
	end
end

-- XPerl_SavePosition(frame)
function XPerl_SavePosition(frame)
	if (not XPerlConfig.SavedPositions) then
		XPerlConfig.SavedPositions = {}
	end
	XPerlConfig.SavedPositions[frame:GetName()] = {top = frame:GetTop(), left = frame:GetLeft()}
end

-- XPerl_RestorePosition(frame)
function XPerl_RestorePosition(frame)
	if (XPerlConfig.SavedPositions) then
		local pos = XPerlConfig.SavedPositions[frame:GetName()]
		if (pos) then
			if (pos.left or pos.right) then
				frame:ClearAllPoints()
				frame:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", pos.left, pos.top)
			end
		end
	end
end

-- XPerl_SetBuffSize
function XPerl_SetBuffSize(prefix, sizeBuff, sizeDebuff)
	for i = 1,20 do
		local buff = getglobal(prefix.."BuffFrame_Buff"..i)
		if (buff) then
			buff:SetHeight(sizeBuff)
			buff:SetWidth(sizeBuff)
		end

		local buff = getglobal(prefix.."BuffFrame_DeBuff"..i)
		if (buff) then
			buff:SetHeight(sizeDebuff)
			buff:SetWidth(sizeDebuff)
		end
	end
end

-- true indicates the buff is valid on anyone, otherwise a class string dictates we only count it if the player with the buff matches
local BuffExceptions = {
	--ALL = {Spell_Misc_Food = true},	-- Enable this for food
	PRIEST = {Spell_Nature_ResistNature = true, Spell_Nature_Rejuvenation = true},	-- Druid regens
	DRUID = {Spell_Holy_Renew = true},		-- Priest regens
	WARLOCK = {Spell_Shadow_SoulGem = true},	-- Soulstone Resurrection
	HUNTER = {Spell_Nature_RavenForm = "HUNTER", Ability_Mount_JungleTiger = "HUNTER", Ability_Mount_WhiteTiger = true, Ability_Mount_PinkTiger = "HUNTER", Ability_Hunter_AspectOfTheMonkey = "HUNTER", Spell_Nature_ProtectionformNature = true, Ability_Rogue_FeignDeath = "HUNTER", Ability_Hunter_RunningShot = "HUNTER", Ability_TrueShot = true, Ability_Hunter_BeastTraining = "WARRIOR"},
	ROGUE = {Ability_Stealth = "ROGUE", Spell_Shadow_ShadowWard = "ROGUE", Ability_Vanish = "ROGUE", Ability_Rogue_Sprint = "ROGUE"}
}

local DebuffExceptions = {
	ALL = {INV_Misc_Bandage_08 = true},		-- Recently Bandaged
	PRIEST = {Spell_Holy_AshesToAshes = true}	-- Weakened Soul
}

-- BuffException
--local showInfo
local function BuffException(unit, index, flag, func, exceptions)

	local name, rank, buff, count, debuffType
	if (not flag or flag == 0 or flag == "0") then
		-- Not filtered, just return it
		name, rank, buff, count, debuffType = func(unit, index)
		return name, rank, buff, count, debuffType, index
	end

	name, rank, buff, count, debuffType = func(unit, index, 1)
	if (buff) then
		--if (showInfo) then
		--	ChatFrame7:AddMessage("Found buff with normal filter at index "..index)
		--end
		-- We need the index of the buff unfiltered later for tooltips
		for i = 1,20 do
			local name1, rank1, buff1, count1, debuffType1 = func(unit, i)
			if (name == name1 and rank == rank1 and buff == buff1 and count == count1) then
				index = i
				break
			end
		end

		return name, rank, buff, count, debuffType, index
	end

	-- See how many filtered buffs WoW has returned by default
	local normalBuffFilterCount = 0
	for i = 1,20 do
		name, rank, buff, count, debuffType = func(unit, i, 1)
		if (not buff) then
			normalBuffFilterCount = i - 1
			break
		end
	end

	-- Nothing found by default, so look for exceptions
	local _, class = UnitClass("player")
	local _, unitClass = UnitClass(unit)
	local foundValid = 0
	for i = 1,20 do
		name, rank, buff, count, debuffType = func(unit, i)

		if (not buff) then
			break
		end

		if (strsub(buff, 1, 16) == "Interface\\Icons\\") then
			local test = strsub(buff, 17)
			local good

			if (exceptions[class]) then
				good = exceptions[class][test]
			elseif (exceptions.ALL) then
				good = exceptions.ALL[test]
			end

			-- Enable this for potions
			--if (not good) then
			--	if (exceptions == BuffExceptions and strsub(test, 1, 11) == "INV_Potion_") then
			--		good = true
			--	end
			--end

			if (good and type(good) == "string" and good ~= unitClass) then
				good = nil
			end

			if (good) then
				foundValid = foundValid + 1
				if (foundValid + normalBuffFilterCount == index) then
					--if (showInfo) then
					--	ChatFrame7:AddMessage("Found extra: "..buff..", requested index == "..index..", actual buff index == "..i)
					--end
					return name, rank, buff, count, debuffType, i
				end
			end
		end
	end
end

-- XPerl_UnitBuff
function XPerl_UnitBuff(unit, index, flag)
	return BuffException(unit, index, flag, UnitBuff, BuffExceptions)
end

-- XPerl_UnitBuff
function XPerl_UnitDebuff(unit, index, flag)
	return BuffException(unit, index, flag, UnitDebuff, DebuffExceptions)
end

-- XPerl_TooltipSetUnitBuff
-- Retreives the index of the actual unfiltered buff, and uses this on unfiltered tooltip call
function XPerl_TooltipSetUnitBuff(tooltip, unit, ind, flag)
	--showInfo = true
	local name, rank, buff, count, _, index = BuffException(unit, ind, flag, UnitBuff, BuffExceptions)
	--showInfo = nil
	tooltip:SetUnitBuff(unit, index)
end

-- XPerl_TooltipSetUnitDebuff
-- Retreives the index of the actual unfiltered debuff, and uses this on unfiltered tooltip call
function XPerl_TooltipSetUnitDebuff(tooltip, unit, ind, flag)
	local name, rank, buff, count, debuffType, index = BuffException(unit, ind, flag, UnitDebuff, DebuffExceptions)
	tooltip:SetUnitDebuff(unit, index)
end

local colourList = {
		[XPERL_LOC_CLASS_WARRIOR]	= "WARRIOR",
		[XPERL_LOC_CLASS_MAGE]		= "MAGE",
		[XPERL_LOC_CLASS_ROGUE]		= "ROGUE",
		[XPERL_LOC_CLASS_DRUID]		= "DRUID",
		[XPERL_LOC_CLASS_HUNTER]	= "HUNTER",
		[XPERL_LOC_CLASS_SHAMAN]	= "SHAMAN",
		[XPERL_LOC_CLASS_PRIEST]	= "PRIEST",
		[XPERL_LOC_CLASS_WARLOCK]	= "WARLOCK",
		[XPERL_LOC_CLASS_PALADIN]	= "PALADIN"}

-- XPerl_GuildStatusUpdate()
function XPerl_GuildStatusUpdate()
	if (XPerlConfig.ClassColouredNames == 1 or XPerlConfig.XPerlTooltipInfo == 1) then

		local myZone = GetRealZoneText()

		local guildOffset = FauxScrollFrame_GetOffset(GuildListScrollFrame)
		for i=1, GUILDMEMBERS_TO_DISPLAY, 1 do
			local guildIndex = guildOffset + i

			local button = getglobal("GuildFrameButton"..i);
			button.guildIndex = guildIndex;
			local name, rank, rankIndex, level, class, zone, note, officernote, online = GetGuildRosterInfo(guildIndex)
			if (not name) then
				break
			end

			if (XPerlConfig.XPerlTooltipInfo == 1 and XPerl_GetUsage) then
				local u = XPerl_GetUsage(name)

				if (u) then
					if (online) then
						getglobal("GuildFrameButton"..i.."Name"):SetTextColor(1, 0.4, 0.4)
						getglobal("GuildFrameGuildStatusButton"..i.."Name"):SetTextColor(1, 0.4, 0.4)
					else
						getglobal("GuildFrameButton"..i.."Name"):SetTextColor(0.5, 0.2, 0.2)
						getglobal("GuildFrameGuildStatusButton"..i.."Name"):SetTextColor(0.5, 0.2, 0.2)
					end
				end
			end

			if (XPerlConfig.ClassColouredNames == 1 and XPerlConfig.ApplyToGuildList == 1) then
				class = colourList[class]

				if (class) then
					local color = XPerl_GetClassColour(class)
					if (color) then
						if (online) then
							getglobal("GuildFrameButton"..i.."Class"):SetTextColor(color.r, color.g, color.b)
						else
							getglobal("GuildFrameButton"..i.."Class"):SetTextColor(color.r / 2, color.g / 2, color.b / 2)
						end
					end
				end

				if (zone == myZone) then
					if (online) then
						getglobal("GuildFrameButton"..i.."Zone"):SetTextColor(0, 1, 0)
					else
						getglobal("GuildFrameButton"..i.."Zone"):SetTextColor(0, 0.5, 0)
					end
				end

				local color = GetDifficultyColor(level)
				if (online) then
					getglobal("GuildFrameButton"..i.."Level"):SetTextColor(color.r, color.g, color.b)
				else
					getglobal("GuildFrameButton"..i.."Level"):SetTextColor(color.r / 2, color.g / 2, color.b / 2)
				end
			end
		end
	end
end

hooksecurefunc("GuildStatus_Update", XPerl_GuildStatusUpdate)

-- XPerl_WhoList_Update
function XPerl_WhoList_Update()
	local numWhos, totalCount = GetNumWhoResults()
	local whoOffset = FauxScrollFrame_GetOffset(WhoListScrollFrame)

	local myZone = GetRealZoneText()
	local myRace = UnitRace("player")
	local myGuild = GetGuildInfo("player")

	for i=1, WHOS_TO_DISPLAY, 1 do
		local name, guild, level, race, class, zone = GetWhoInfo(whoOffset + i)
		local color
		if (not name) then
			break
		end

		getglobal("WhoFrameButton"..i.."Name"):SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
		if (XPerlConfig.XPerlTooltipInfo == 1 and XPerl_GetUsage) then
			local u = XPerl_GetUsage(name)
			if (u) then
				getglobal("WhoFrameButton"..i.."Name"):SetTextColor(1, 0.4, 0.4)
			end
		end

		if (XPerlConfig.ClassColouredNames == 1 and XPerlConfig.ApplyToGuildList == 1) then
			if (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 1) then
				-- Zone
				if (zone == myZone) then
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(0, 1, 0)
				else
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(1, 1, 1)
				end
			elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 2) then
				if (guild == myGuild) then
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(0, 1, 0)
				else
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(1, 1, 1)
				end

			elseif (UIDropDownMenu_GetSelectedID(WhoFrameDropDown) == 3) then
				if (race == myRace) then
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(0, 1, 0)
				else
					getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(1, 1, 1)
				end
			end

			getglobal("WhoFrameButton"..i.."Class"):SetTextColor(1, 1, 1)
			class = colourList[class]
			if (class) then
				local color = XPerl_GetClassColour(class)
				if (color) then
					getglobal("WhoFrameButton"..i.."Class"):SetTextColor(color.r, color.g, color.b)
				end
			end

			local color = GetDifficultyColor(level)
			getglobal("WhoFrameButton"..i.."Level"):SetTextColor(color.r, color.g, color.b)
		else
			getglobal("WhoFrameButton"..i.."Variable"):SetTextColor(1, 1, 1)
			getglobal("WhoFrameButton"..i.."Level"):SetTextColor(1, 1, 1)
			getglobal("WhoFrameButton"..i.."Class"):SetTextColor(1, 1, 1)
		end
	end
end

hooksecurefunc("WhoList_Update", XPerl_WhoList_Update)

----------------------
-- Fading Bar Stuff --
----------------------
local fadeBars = {}
local freeFadeBars = {}
local tempDisableFadeBars

function XPerl_NoFadeBars(tempDisable)
	tempDisableFadeBars = tempDisable
end

-- CheckOnUpdate
local function CheckOnUpdate()
	local any
	for k,v in pairs(fadeBars) do
		any = true
		break
	end

	if (any) then
		XPerl_Globals:SetScript("OnUpdate", XPerl_BarUpdate)
	else
		XPerl_Globals:SetScript("OnUpdate", nil)
	end
end

-- XPerl_BarUpdate
--local speakerTimer = 0
--local speakerCycle = 0
function XPerl_BarUpdate()
	local did
	for k,v in pairs(fadeBars) do
		if (k:IsShown()) then
			v:SetAlpha(k.fadeAlpha)
			k.fadeAlpha = k.fadeAlpha - (arg1 / XPerlConfig.FadingBarsTime)

			local r, g, b = v.Tex:GetVertexColor()
			v:SetStatusBarColor(r, g, b)
		else
			-- Not shown, so end it
			k.fadeAlpha = 0
		end

		if (k.fadeAlpha <= 0) then
			tinsert(freeFadeBars, v)
			fadeBars[k] = nil
			k.fadeAlpha = nil
			k.fadeBar = nil
			v:SetValue(0)
			v:Hide()
			v.Tex = nil
			did = true
		end
	end

	if (did) then
		CheckOnUpdate()
	end
end

-- GetFreeFader
local function GetFreeFader(parent)
	local bar = freeFadeBars[1]
	if (bar) then
		tremove(freeFadeBars, 1)
		bar:SetParent(parent)
	else
		bar = CreateFrame("StatusBar", nil, parent)
	end

	if (bar) then
		fadeBars[parent] = bar
		CheckOnUpdate()

		bar.Tex = getglobal(parent:GetName().."Tex")

		local tex = parent:GetStatusBarTexture()
		bar:SetStatusBarTexture(tex:GetTexture())

		local r, g, b = bar.Tex:GetVertexColor()
		bar:SetStatusBarColor(r, g, b)

		bar:SetFrameLevel(parent:GetFrameLevel())

		bar:ClearAllPoints()
		bar:SetPoint("TOPLEFT", 0, 0)
		bar:SetPoint("BOTTOMRIGHT", 0, 0)

		return bar
	end
end

-- XPerl_StatusBarSetValue
function XPerl_StatusBarSetValue(self, val)
	if (not tempDisableFadeBars and XPerlConfig.FadingBars == 1 and self:GetName()) then
		local min, max = self:GetMinMaxValues()
		local current = self:GetValue()

		if (val < current and val <= max and val >= min) then
			local bar = fadeBars[self]

			if (not bar) then
				bar = GetFreeFader(self)
			end

			if (bar) then
				if (not self.fadeAlpha) then
					self.fadeAlpha = 1
					bar:SetValue(current)
				end

				bar:SetMinMaxValues(min, max)
				bar:SetAlpha(self.fadeAlpha)
				bar:Show()
			end
		end
	end

	XPerl_OldStatusBarSetValue(self, val)
end

-- XPerl_RegisterClickCastFrame
function XPerl_RegisterClickCastFrame(self)
	if (not ClickCastFrames) then
		ClickCastFrames = {}
	end
	ClickCastFrames[self] = true
end

function XPerl_UnregisterClickCastFrame(self)
	if (ClickCastFrames) then
		ClickCastFrames[self] = nil
	end
end

function XPerl_SecureUnitButton_OnLoad(self, unit, menufunc)
	self:SetAttribute("*type1", "target")
	self:SetAttribute("type2", "menu")
	self:SetAttribute("unit", unit)
	self.menu = menufunc
end
