vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
local XPerl_Raid_Events = {}
local RaidGroupCounts = {0,0,0,0,0,0,0,0,0}
local FrameArray = {}
local PulloutFrameArray = {}
local perlSetupDone = nil
local ResArray = {}
local MovingMember = nil
local RosterUpdate = nil
local RaidPositions = {}
local oldRaidFrameDropDown_Initialize

XPERL_RAIDGRP_PREFIX    = "XPerl_Raid_Grp"
XPERL_RAIDMEMBER_PREFIX = "XPerl_raid"

local CELL_MOTION_SPEED         = 0.8           -- Motion offset mulitplied by this per frame (TODO, make this time based)

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

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

-- XPerl_Raid_OnLoad
function XPerl_Raid_OnLoad()
        this:RegisterEvent("PLAYER_ENTERING_WORLD")
        this:RegisterEvent("PLAYER_LEAVING_WORLD")
        this:RegisterEvent("VARIABLES_LOADED")
        this:RegisterEvent("RAID_ROSTER_UPDATE")
        this:RegisterEvent("ADDON_LOADED")
        this.time = 0
        this.Array = {}
end

-- GetRaidGroup
local function GetRaidGroup(num, createIfAbsent)
        local frame = getglobal(XPERL_RAIDGRP_PREFIX..num)

        if (not frame and createIfAbsent) then
                frame = CreateFrame("Frame", XPERL_RAIDGRP_PREFIX..num, XPerl_Raid_Frame, "XPerl_Raid_TitleFrameTemplate")
                frame:SetScale(XPerlConfig.Scale_Raid)

                frame = getglobal(XPERL_RAIDGRP_PREFIX..num.."_NameFrame_NameBarText")
                frame:SetText("Group "..num)
        end

        return frame
end

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

        if (XPerlConfig.RaidMana == 1) then
                frameStats:SetHeight(30)
                frameMana:Show()
        else
                frameStats:SetHeight(25)
                frameMana:Hide()
        end
end

-- GetRaidUnit
local function GetRaidUnit(num, createIfAbsent)
        local frame = getglobal(XPERL_RAIDMEMBER_PREFIX..num)

        if (not frame and createIfAbsent) then
                frame = CreateFrame("Frame", XPERL_RAIDMEMBER_PREFIX..num, XPerl_Raid_Frame, "XPerl_Raid_FrameTemplate")

                frame:SetScale(XPerlConfig.Scale_Raid)
                Setup1RaidFrame(frame)
        end

        return frame
end

-- Non local by request
Perl_Raid_FindID = XPerl_Frame_FindID

local function Perl_Raid_FindNum(partyid)
        local _, _, num = strfind(partyid, "(%d+)")
        return tonumber(num)
end

local function Spacing()
        return XPerlConfig.RaidVerticalSpacing + ((-1 + XPerlConfig.RaidMana) * 5)
end

-- XPerl_Raid_Group_OnLoad
function XPerl_Raid_Group_OnLoad()
        local id = XPerl_Frame_FindID(this)
        this:SetID(id)

        local x, y
        if (XPerlConfig.RaidPositions) then
                local pos = XPerlConfig.RaidPositions[this:GetName()]

                if (pos and pos.left and pos.top) then
                        x = pos.left
                        y = pos.top
                end
        end

        if (not (x and y)) then
                y = 565
                x = (id - 1) * 77
        end

        this:ClearAllPoints()
        this:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)

        function this:SavePosition()
                XPerlConfig.RaidPositions[this:GetName()] = {top = this:GetTop(), left = this:GetLeft()}
        end
end

-- SaveAllPositions
local function SaveAllPositions()
        for i = 1,NUM_RAID_GROUPS do
                local frame = GetRaidGroup(i)
                if (frame) then
                        XPerlConfig.RaidPositions[frame:GetName()] = {top = frame:GetTop(), left = frame:GetLeft()}
                end
        end
end

-- XPerl_Raid_Single_OnLoad
function XPerl_Raid_Single_OnLoad()
        if (strsub(this:GetName(), 1, 5) == "XPerl") then
                local id = XPerl_Frame_FindID(this)
                this:SetID(id)

                this.partyid = "raid"..this:GetID()

                FrameArray[this.partyid] = this
        else
                this:SetID(99)
        end

        this.showing = 0

        XPerl_RegisterHighlight(getglobal(this:GetName().."_CastClickOverlay"), 2)
        XPerl_RegisterPerlFrames(this, {"NameFrame", "StatsFrame"})

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

local events = {"UNIT_FACTION", "UNIT_FLAGS", "UNIT_DYNAMIC_FLAGS", "UNIT_AURA",
                "UNIT_SPELLMISS", "CHAT_MSG_ADDON", "CHAT_MSG_RAID", "CHAT_MSG_RAID_LEADER",
                "CHAT_MSG_PARTY"}

-- XPerl_Raid_RegisterSome
local function XPerl_Raid_RegisterSome()
        for i,event in pairs(events) do
                this:RegisterEvent(event)
        end
        XPerl_RegisterBasics()
        this:UnregisterEvent("UNIT_LEVEL")      -- Don't care about player level
end

-- XPerl_Raid_UnregisterSome
local function XPerl_Raid_UnregisterSome()
        for i,event in pairs(events) do
                this:UnregisterEvent(event)
        end
        XPerl_UnregisterBasics()
end

-- XPerl_Raid_UpdateName
local function XPerl_Raid_UpdateName(thisFrame)
        local name = UnitName(thisFrame.partyid)

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

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

                if (not name) then
                        if (thisFrame.petname) then
                                name = thisFrame.petname
                        else
                                name = UnitName(thisFrame.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 (thisFrame.pet) then
                        color = XPerlConfig.ColourReactionNone
                        frame:SetTextColor(color.r, color.g, color.b)
                else
                        XPerl_ColourFriendlyUnit(frame, thisFrame.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.." 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 = "AFK"}

                elseif (unitInfo.dnd) then
                        return {flag = "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}}
                                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(thisFrame, skipFlags)
        local flags
        if (not skipFlags) then
                flags = XPerl_Raid_CheckFlags(thisFrame.partyid)
        end
        if (not flags) then
                if (XPerlConfig.RaidMana == 1) then
                        local frame = getglobal(thisFrame:GetName().."_StatsFrame_ManaBar")
                        local frameBG = getglobal(thisFrame:GetName().."_StatsFrame_ManaBarBG")
                        XPerl_SetManaBarType(thisFrame.partyid, frame, frameBG)
                end
        end
end

-- XPerl_Raid_ShowFlags
local function XPerl_Raid_ShowFlags(thisFrame, flags)
        --thisFrame.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(thisFrame:GetName().."_StatsFrame_HealthBar"); if (f) then f:SetStatusBarColor(bgcolor.r, bgcolor.g, bgcolor.b); end
        f = getglobal(thisFrame:GetName().."_StatsFrame_HealthBarBG"); if (f) then f:SetVertexColor(bgcolor.r, bgcolor.g, bgcolor.b, 0.5); end
        f = getglobal(thisFrame:GetName().."_StatsFrame_ManaBar"); if (f) then f:SetStatusBarColor(bgcolor.r, bgcolor.g, bgcolor.b); end
        f = getglobal(thisFrame:GetName().."_StatsFrame_ManaBarBG"); if (f) then f:SetVertexColor(bgcolor.r, bgcolor.g, bgcolor.b, 0.5); end
        f = getglobal(thisFrame:GetName().."_StatsFrame_HealthBarText")
        if (f) then
                f:SetText(flags.flag)
                f:SetTextColor(color.r, color.g, color.b)
        end
end

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

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

-- XPerl_Raid_UpdateHealth
local function XPerl_Raid_UpdateHealth(thisFrame)
        local partyid = thisFrame.partyid
        local health = UnitHealth(partyid)
        local healthmax = UnitHealthMax(partyid)
        local healthBar = getglobal(thisFrame:GetName().."_StatsFrame_HealthBar")
        local frameText = getglobal(thisFrame:GetName().."_StatsFrame_HealthBarText")

        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 and health > 0) then
                        -- Seems we don't always get the UNIT_AURA messages, so we'll check their health on updates.
                        -- health > 0 means they can't be FD
                        myRoster.fd = nil
                end

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

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

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

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

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

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

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

                        if (XPerlConfig.HealerMode == 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(thisFrame, true)
                                end
                        end
                end
        else
                thisFrame.dead = nil
                XPerl_Raid_ShowFlags(thisFrame, {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(thisFrame)
        local mana = UnitMana(thisFrame.partyid)
        local manamax = UnitManaMax(thisFrame.partyid)

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

        if (UnitPowerType(thisFrame.partyid) == 0 and not thisFrame.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(thisFrame, elapsed, argNew, argGreen)
        if (XPerl_CombatFlashSet (elapsed, thisFrame, argNew, argGreen)) then
                XPerl_CombatFlashSetFrames(thisFrame)
        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

                for k,v in pairs(PulloutFrameArray) do
                        if (v.partyid == "raid"..id) then
                                if (flagsOnly) then
                                        XPerl_Raid_UpdateHealth(frame)
                                else
                                        XPerl_Raid_UpdateDisplay(frame)
                                end
                        end
                end
        end
end

-- Raid_Motion
local function Raid_Motion(cell)

        local motion = cell.motion

        motion.currentLeft = motion.currentLeft * CELL_MOTION_SPEED
        if (motion.currentLeft > -1 and motion.currentLeft < 1) then
                motion.currentLeft = 0
        end

        motion.currentTop = motion.currentTop * CELL_MOTION_SPEED
        if (motion.currentTop > -1 and motion.currentTop < 1) then
                motion.currentTop = 0
        end

        cell:ClearAllPoints()
        if (XPerlConfig.RaidUpward == 1) then
                cell:SetPoint("BOTTOMLEFT", motion.parent, "TOPLEFT", motion.currentLeft, motion.targetTop - motion.currentTop)
        else
                cell:SetPoint("TOPLEFT", motion.parent, "BOTTOMLEFT", motion.currentLeft, motion.targetTop + motion.currentTop)
        end

        if (motion.currentLeft == 0 and motion.currentTop == 0) then
                cell.motion = nil
        end
end

-------------------
-- Raid movement --
-------------------

-- XPerl_Raid_StartMovingMember
function XPerl_Raid_StartMovingMember()

        if (not MovingMember) then
                local frame = this:GetParent()

                if (frame:GetID() ~= 99) then
                        HideDropDownMenu(1)
                        MovingMember = frame
                        frame:StartMoving()
                        frame:SetAlpha(XPerlConfig.Transparency / 2)
                        XPerl_RaidTitles()
                end
        end
end

-- XPerl_Raid_CheckMovingMember
local function XPerl_Raid_CheckMovingMember()

        local x = MovingMember:GetLeft() + ((MovingMember:GetRight() - MovingMember:GetLeft()) / 2)
        local y = MovingMember:GetBottom() + ((MovingMember:GetTop() - MovingMember:GetBottom()) / 2)

        XPerl_Raid_Mover:ClearAllPoints()
        XPerl_Raid_Mover:Hide()
        XPerl_Raid_Mover:SetScale(XPerlConfig.Scale_Raid)

        for i,cell in pairs(FrameArray) do
                getglobal(cell:GetName().."_CastClickOverlay"):UnlockHighlight()
        end

        local name = UnitName(MovingMember.partyid)
        local spacing = Spacing()

        for i = 1,NUM_RAID_GROUPS do
                if (i ~= RaidPositions[name].group) then
                        local frame = GetRaidGroup(i, true)

                        if (frame) then
                                local left = (frame:GetLeft() or 0)
                                local right = (frame:GetRight() or 0)
                                local top
                                if (XPerlConfig.RaidUpward == 1) then
                                        top = frame:GetTop() or 0
                                else
                                        top = frame:GetBottom() or 0
                                end
                                if (left) then
                                        local bottom
                                        if (XPerlConfig.RaidUpward == 1) then
                                                bottom = top + (MEMBERS_PER_RAID_GROUP * spacing)
                                                local t = top
                                                top = bottom
                                                bottom = t
                                        else
                                                bottom = top - (MEMBERS_PER_RAID_GROUP * spacing)
                                        end

                                        if (x >= left and x <= right and y >= bottom and y <= top) then
                                                if (XPerlConfig.RaidUpward == 1) then
                                                        XPerl_Raid_Mover:SetPoint("BOTTOMLEFT", frame, "TOPLEFT", 0, 0)
                                                        XPerl_Raid_Mover:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -(bottom - top))
                                                else
                                                        XPerl_Raid_Mover:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, 0)
                                                        XPerl_Raid_Mover:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, (bottom - top))
                                                end
                                                XPerl_Raid_Mover:Show()

                                                local size = (bottom - top) / 5
                                                local sub = math.floor((y - top - size) / size) + 2
                                                if (sub < 1) then
                                                        sub = 1
                                                elseif (sub > 5) then
                                                        sub = 5
                                                end

                                                if (XPerlConfig.RaidUpward == 1) then
                                                        sub = 6 - sub
                                                end

                                                for j,cell in pairs(FrameArray) do
                                                        local pos = RaidPositions[UnitName(cell.partyid)]

                                                        if (pos and cell ~= MovingMember and pos.group == i and pos.row == sub) then
                                                                getglobal(cell:GetName().."_CastClickOverlay"):LockHighlight()
                                                                break
                                                        end
                                                end
                                                return i, sub
                                        end
                                end
                        end
                end
        end
end

-- XPerl_Raid_StopMovingMember
function XPerl_Raid_StopMovingMember(commit)
        if (MovingMember) then
                local good
                MovingMember:StopMovingOrSizing()

                if (commit) then
                        local group,subpos = XPerl_Raid_CheckMovingMember()

--ChatFrame7:AddMessage("group "..(group or "nil").."   row "..(subpos or "nil"))

                        if (group) then
                                local pos = RaidPositions[UnitName(MovingMember.partyid)]
                                if (pos and group ~= pos.group) then
                                        local unit
                                        for i,cell in pairs(FrameArray) do
                                                local cellPos = RaidPositions[UnitName(cell.partyid)]
                                                if (cellPos and cellPos.group == group and cellPos.row == subpos) then
                                                        unit = cell
                                                        break
                                                end
                                        end

                                        local index = Perl_Raid_FindNum(MovingMember.partyid)

--ChatFrame7:AddMessage("  index "..(index or "nil"))

                                        if (unit) then
                                                local targetIndex = Perl_Raid_FindNum(unit.partyid)
--ChatFrame7:AddMessage("  targetIndex "..(targetIndex or "nil"))
                                                SwapRaidSubgroup(index, targetIndex)
                                        else
                                                SetRaidSubgroup(index, group)
                                        end
                                        good = true
                                else
--ChatFrame7:AddMessage("Cancelled, same as my current group")
                                end
                        else
--ChatFrame7:AddMessage("Cancelled, no group")
                        end
                end

                for i,cell in pairs(FrameArray) do
                        getglobal(cell:GetName().."_CastClickOverlay"):UnlockHighlight()
                end

                if (not good) then
                        local pos = RaidPositions[UnitName(MovingMember.partyid)]
                        if (pos) then
                                local parentGroup = GetRaidGroup(pos.group)
                                MovingMember:ClearAllPoints()
                                if (XPerlConfig.RaidUpward == 1) then
                                        MovingMember:SetPoint("BOTTOMLEFT", parentGroup, "TOPLEFT", 0, -2 + ((pos.row - 1) * Spacing()))
                                else
                                        MovingMember:SetPoint("TOPLEFT", parentGroup, "BOTTOMLEFT", 0, 2 - ((pos.row - 1) * Spacing()))
                                end
                        end
                end

                if (UnitIsConnected(MovingMember.partyid)) then
                        MovingMember:SetAlpha(XPerlConfig.Transparency)
                else
                        MovingMember:SetAlpha(XPerlConfig.Transparency / 2)
                end

                MovingMember = nil
                XPerl_RaidTitles()

                XPerl_Raid_Mover:Hide()
        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()
        if (RosterUpdate) then
                RosterUpdate = nil

                XPerl_Raid_Position()
                if (GetNumRaidMembers() == 0) then
                        ResArray = {}
                        XPerl_Roster = {}
                        RaidPositions = {}
                end
        end

        local updateTanker
        if (XPerlConfig.RaidHighlightTanker == 1) then
                if arg1 then this.time = this.time + arg1; end
                if this.time >= 0.2 then
                        this.time = 0
                        updateTanker = true
                end
        end

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

                        if (frame.motion) then
                                Raid_Motion(frame)
                        end

                        if (updateTanker) then
                                local set
                                if (UnitExists("target")) then
                                        if (UnitCanAttack("player", "target")) then
                                                if (UnitInRaid("targettarget")) then
                                                        set = UnitIsUnit("targettarget", frame.partyid)
                                                end
                                        elseif (UnitCanAttack("player", "targettarget")) then
                                                if (UnitInRaid("targettargettarget")) then
                                                        set = UnitIsUnit("targettargettarget", frame.partyid)
                                                end
                                        end
                                end

                                local highlightFrame = getglobal(frame:GetName().."_CastClickOverlay")
                                if (XPerlConfig.HighlightSelection == 0) then
                                        if (set) then
                                                highlightFrame:SetHighlightTexture("Interface\\Addons\\XPerl\\Images\\XPerl_Highlight", "ADD")
                                                local tex = highlightFrame:GetHighlightTexture(); tex:SetTexCoord(0.25, 0.75, 0, 0.5)
                                                tex:SetVertexColor(0.86, 0.82, 0.41)
                                        else
                                                highlightFrame:SetHighlightTexture("")
                                        end
                                else
                                        highlightFrame:SetHighlightTexture("Interface\\Addons\\XPerl\\Images\\XPerl_Highlight", "ADD")
                                        local tex = highlightFrame:GetHighlightTexture(); tex:SetTexCoord(0.25, 0.75, 0, 0.5)
                                        tex:SetVertexColor(0.86, 0.82, 0.41)
                                end

                                if (set) then
                                        highlightFrame:LockHighlight()
                                else
                                        highlightFrame:UnlockHighlight()
                                end
                        end
                end
        end

        if (MovingMember) then
                XPerl_Raid_CheckMovingMember()
        end
end

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

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

        if (not button and createIfAbsent) then
                button = CreateFrame("Button", name, getglobal(thisFrame: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(thisFrame)
        local show,cureCast
        if (thisFrame:GetID() == 99) then
                -- Pullout frames all have ID 99
                local RaidPullout = thisFrame: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(thisFrame)
        local partyid = thisFrame.partyid
        local cursed, color = XPerl_CheckDebuffs(partyid, {getglobal(thisFrame:GetName().."_NameFrame"), getglobal(thisFrame:GetName().."_StatsFrame")})

        local frameText = getglobal(thisFrame: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(thisFrame)
        if (show) then
                for buffnum=1,maxBuff do
                        local buff, count
                        if (show == "b") then
                                buff = XPerl_UnitBuff(partyid, buffnum, cureCast)
                        else
                                buff = XPerl_UnitDebuff(partyid, buffnum, cureCast)
                        end
                        local button = GetBuffButton(thisFrame, 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(thisFrame:GetName().."_BuffFrame_Buff"..buffnum)
                        if (button) then
                                button:Hide()
                        end
                end
        end

        --local myRoster = XPerl_Roster[UnitName(partyid)]
        --if (myRoster and XPerlConfig.RaidBuffs == 1 and buffCount == 0 and cureCast == 0) then
        --      if (UnitIsConnected(partyid)) then
        --              -- Already scanned raid buffs and there are none
        --              myRoster.Buffs = nil
        --      end
        --else
        --      CheckRosterBuffs(partyid)
        --end

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

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

                if (XPerlConfig.RaidBuffsRight == 1 or id == 99) then
                        buffFrame:SetPoint("BOTTOMLEFT", thisFrame: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(thisFrame:GetName().."_BuffFrame_Buff1"):ClearAllPoints()
                        getglobal(thisFrame:GetName().."_BuffFrame_Buff1"):SetPoint("BOTTOMLEFT", 0, 0)
                        for i = 2,buffCount do
                                if (i > buffCount) then break end

                                local buffI = getglobal(thisFrame: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(thisFrame:GetName().."_BuffFrame_Buff1"):SetPoint("BOTTOMLEFT", buffI, "BOTTOMRIGHT", 0, 0)
                                        else
                                                buffI:SetPoint("BOTTOMLEFT", thisFrame:GetName().."_BuffFrame_Buff"..(i-(4 - abs(1 - XPerlConfig.RaidMana))), "BOTTOMRIGHT", 0, 0)
                                        end
                                else
                                        buffI:SetPoint("BOTTOMLEFT", thisFrame:GetName().."_BuffFrame_Buff"..(i-1), "TOPLEFT", 0, 0)
                                end
                        end
                else
                        nameFrame:SetWidth(80)
                        statsFrame:SetWidth(80)

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

                        local prevBuff
                        for i = 1,buffCount do
                                local buff = getglobal(thisFrame: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(thisFrame)
                                end
                        elseif (myRoster.fd) then
                                myRoster.fd = nil
                                XPerl_Raid_UpdateManaType(thisFrame, true)
                        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
};
local function UpdateRosterBuffs(thisFrame)

        local unit = thisFrame.partyid
        local myRoster = XPerl_Roster[unit]

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

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

                local num, buff = 0, UnitBuff(unit, 1);
                while (buff) do
                        num = num + 1;
                        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
                                PerlBuffStatusTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
                                PerlBuffStatusTooltipTextLeft1:SetText("")
                                PerlBuffStatusTooltip:SetUnitBuff(unit, num)
                                local tooltipName = PerlBuffStatusTooltipTextLeft1: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 duration = XPerl_CTBuffTextures[buffName][2]

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

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

                        buff = UnitBuff(unit, num + 1)
                end

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

-- XPerl_Raid_Update_Control
local function XPerl_Raid_Update_Control(thisFrame)
        if (UnitIsVisible(thisFrame.partyid) and UnitIsCharmed(thisFrame.partyid)) then
                getglobal(thisFrame:GetName().."_NameFrame_Warning"):Show()
        else
                getglobal(thisFrame:GetName().."_NameFrame_Warning"):Hide()
        end
end

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

-------------------------
-- The Update Function --
-------------------------
function XPerl_Raid_UpdateDisplay(thisFrame)
        if (thisFrame.showing == 1) then
                if (UnitExists(thisFrame.partyid) or thisFrame.pet) then
                        if (UnitIsConnected(thisFrame.partyid) and MovingMember ~= thisFrame) then
                                thisFrame:SetAlpha(XPerlConfig.Transparency)
                        else
                                thisFrame:SetAlpha(XPerlConfig.Transparency/2)
                                getglobal(thisFrame:GetName().."_StatsFrame_HealthBarText"):SetText("Offine")
                        end

                        local manaBarText = getglobal(thisFrame:GetName().."_StatsFrame_ManaBarText")
                        local healthBarText = getglobal(thisFrame:GetName().."_StatsFrame_HealthBarText")
                        if (XPerlConfig.ShowRaidPercents==0) then
                                manaBarText:Hide()
                                healthBarText:Hide()
                        else
                                manaBarText:Show()
                                healthBarText:Show()
                        end

                        -- Health must be updated after mana, since ctra flag checks are done here.
                        XPerl_Raid_UpdateManaType(thisFrame)
                        XPerl_Raid_UpdateMana(thisFrame)
                        XPerl_Raid_UpdateHealth(thisFrame)              -- <<< -- AFTER MANA -- <<< --
                        XPerl_Raid_UpdateName(thisFrame)
                        XPerl_Raid_UpdateCombat(thisFrame)
                        XPerl_Raid_Update_Control(thisFrame)
                        UpdateBuffs(thisFrame)
                else
                        thisFrame:StopMovingOrSizing()
                        thisFrame:Hide()
                end
        else
                thisFrame:StopMovingOrSizing()
                thisFrame:Hide()
        end
end

-------------------
-- Event Handler --
-------------------
function XPerl_Raid_OnEvent()
        local func = XPerl_Raid_Events[event]
        if (func) then
                func()
        else
XPerl_ShowMessage("EXTRA EVENT")
        end
end

function XPerl_Raid_Events:ADDON_LOADED()
        if (arg1 == "Blizzard_RaidUI") then
                if (not oldRaidFrameDropDown_Initialize) then
                        oldRaidFrameDropDown_Initialize = RaidFrameDropDown_Initialize
                        RaidFrameDropDown_Initialize = XPerl_RaidFrameDropDown_Initialize
                end

                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()
                this:UnregisterEvent("ADDON_LOADED")
        end
end

function XPerl_Raid_Events:VARIABLES_LOADED()
        this:UnregisterEvent("VARIABLES_LOADED")

        XPerl_Raid_RegisterSome()

        if (GetNumRaidMembers() == 0) then
                ResArray = {}
                XPerl_Roster = {}
                RaidPositions = {}
        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_RaidTitles()
end

-- PLAYER_ENTERING_WORLD
function XPerl_Raid_Events:PLAYER_ENTERING_WORLD()
        XPerl_Raid_RegisterSome()

        if (not XPerl_raid1 and GetNumRaidMembers() > 0) then
                XPerl_Raid_Position()
        end
end

-- PLAYER_LEAVING_WORLD
function XPerl_Raid_Events:PLAYER_LEAVING_WORLD()
        XPerl_Raid_UnregisterSome()
end

-- RAID_ROSTER_UPDATE
function XPerl_Raid_Events:RAID_ROSTER_UPDATE()
        if (MovingMember) then
                XPerl_Raid_StopMovingMember()
        end

        if (not XPerl_raid1 or XPerl_raid1.showing == 0) then
                -- If no raid yet, just setup anyway
                XPerl_Raid_Position()
                if (GetNumRaidMembers() == 0) then
                        ResArray = {}
                        XPerl_Roster = {}
                        RaidPositions = {}
                end
                if (XPerl_SendModules) then
                        XPerl_SendModules()                     -- Let other X-Perl users know which version we're running
                end
        else
                -- Otherwise, we defer the roster update until the next frame for
                -- cases where we get more than 1 RAID_ROSTER_UPDATE per data packet
                RosterUpdate = true
        end
end

-- UNIT_FLAGS
function XPerl_Raid_Events:UNIT_FLAGS()
        local f = FrameArray[arg1]
        if (f) then
                XPerl_Raid_UpdateCombat(f)

                for name,f in pairs(PulloutFrameArray) do
                        if (f.partyid == arg1 and f:IsShown()) then
                                XPerl_Raid_UpdateCombat(f)
                        end
                end
        end
end

XPerl_Raid_Events.UNIT_DYNAMIC_FLAGS = XPerl_Raid_Events.UNIT_FLAGS

-- UNIT_FACTION
function XPerl_Raid_Events:UNIT_FACTION()
        local f = FrameArray[arg1]
        if (f) then
                XPerl_Raid_Update_Control(f)
                XPerl_Raid_UpdateName(f)
        end

        for name,f in pairs(PulloutFrameArray) do
                if (f.partyid == arg1 and f:IsShown()) then
                        XPerl_Raid_Update_Control(f)
                        XPerl_Raid_UpdateName(f)
                end
        end
end

-- UNIT_COMBAT
function XPerl_Raid_Events:UNIT_COMBAT()
        local f = FrameArray[arg1]
        if (f) then
                if (arg2 == "HEAL") then
                        XPerl_Raid_CombatFlash(f, 0, true, true)
                elseif (arg4 and arg4 > 0) then
                        XPerl_Raid_CombatFlash(f, 0, true)
                end
        end
end

-- UNIT_HEALTH
function XPerl_Raid_Events:UNIT_HEALTH()
        local f = FrameArray[arg1]
        if (f) then
                XPerl_Raid_UpdateHealth(f)
                XPerl_Raid_UpdateCombat(f)
        end

        for name,f in pairs(PulloutFrameArray) do
                if (f.partyid == arg1 and f:IsShown()) then
                        XPerl_Raid_UpdateHealth(f)
                end
        end
end
XPerl_Raid_Events.UNIT_MAXHEALTH = XPerl_Raid_Events.UNIT_HEALTH

function XPerl_Raid_Events:UNIT_DISPLAYPOWER()
        local f = FrameArray[arg1]
        if (f) then
                XPerl_Raid_UpdateManaType(f)
                XPerl_Raid_UpdateMana(f)
        end

        for name,f in pairs(PulloutFrameArray) do
                if (f.partyid == arg1 and f:IsShown()) then
                        XPerl_Raid_UpdateManaType(f)
                        XPerl_Raid_UpdateMana(f)
                end
        end
end

-- UNIT_MANA
function XPerl_Raid_Events:UNIT_MANA()
        if (XPerlConfig.RaidMana == 1) then
                local f = FrameArray[arg1]
                if (f) then
                        XPerl_Raid_UpdateMana(f)
                end

                for name,f in pairs(PulloutFrameArray) do
                        if (f.partyid == arg1 and f:IsShown()) then
                                XPerl_Raid_UpdateMana(f)
                        end
                end
        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()
        local f = FrameArray[arg1]
        if (f) then
                XPerl_Raid_UpdateName(f)

                for name,f in pairs(PulloutFrameArray) do
                        if (f.partyid == arg1 and f:IsShown()) then
                                XPerl_Raid_UpdateName(f)
                        end
                end
        end
end

-- UNIT_AURA
function XPerl_Raid_Events:UNIT_AURA()
        local f = FrameArray[arg1]
        if (f) then
                UpdateRosterBuffs(f)
                UpdateBuffs(f)

                for name,f in pairs(PulloutFrameArray) do
                        if (f.partyid == arg1 and f:IsShown()) then
                                UpdateBuffs(f)
                        end
                end
        end
end

-- UNIT_PET
function XPerl_Raid_Events:UNIT_PET()
        if (strsub(arg1, 1, 4) == "raid") then
                for k,v in pairs(PulloutFrameArray) do
                        if (v.petOwner == arg1) then
                                XPerl_Raid_UpdateDisplay(v)
                        end
                end
        end
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)   m.ressed = nil; 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 = strfind(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
                                        -- 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]
                                        end

                                        if (buffName) then
                                                if (not myRoster.Buffs) then
                                                        myRoster.Buffs = {}
                                                end
                                                myRoster.Buffs[buffName] = GetTime() + secsLeft
                                                --CheckRosterBuffs(unitName)
                                        end
                                end
                        end
                        update = nil

                elseif (strsub(msg, 1, 3) == "CD ") then
                        local _, _, num, cooldown = strfind(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

        for i = 1,GetNumRaidMembers() do
                local name = UnitName("raid"..i)

                if (XPerl_Roster[name]) then
                        NewRoster[name] = XPerl_Roster[name]
                else
                        NewRoster[name] = {}
                end

                if (Last_Roster[name]) then
                        if (RaidPositions[name]) then
                                Last_Roster[name].lastPos = RaidPositions[name]
                        else
                                Last_Roster[name].lastPos = nil
                        end
                end
        end

        RaidPositions = {}
        XPerl_Roster = NewRoster
end

--------------------
-- Raid Functions --
--------------------

-- MoveCell
local function MoveCell(cell, grp, wasShowing)

        local parentGroup = getglobal(XPERL_RAIDGRP_PREFIX..grp)
        local name = UnitName(cell.partyid)
        local noMotion = true

        if (wasShowing == 1) then
                if (cell.lastPos) then
                        if (cell.lastPos.group ~= grp or cell.lastPos.row ~= RaidGroupCounts[grp] + 1) then
                                noMotion = false
                        end
                end

                local oldCell = Last_Roster[name]
                if (oldCell) then
                        if (oldCell.lastPos) then
                                if (oldCell.lastPos.group ~= grp or oldCell.lastPos.row ~= RaidGroupCounts[grp] + 1) then
                                        noMotion = false
                                end
                        end
                end
        end

        RaidPositions[name] = {group = grp, row = RaidGroupCounts[grp] + 1}

        local t
        if (XPerlConfig.RaidUpward == 1) then
                t = -2 + (RaidGroupCounts[grp] * Spacing())
        else
                t = 2 - (RaidGroupCounts[grp] * Spacing())
        end

        if (cell.motion or (not noMotion and cell.showing == 1 and cell:IsShown() and XPerlConfig.RaidMotion == 1)) then
                if (XPerlConfig.RaidUpward == 1) then
                        cell.motion = {
                                targetTop = t,
                                parent = parentGroup,
                                currentLeft = cell:GetLeft() - parentGroup:GetLeft(),
                                currentTop = parentGroup:GetTop() - cell:GetBottom() + t
                        }
                else
                        cell.motion = {
                                targetTop = t,
                                parent = parentGroup,
                                currentLeft = cell:GetLeft() - parentGroup:GetLeft(),
                                currentTop = cell:GetTop() - parentGroup:GetBottom() - t
                        }
                end
        else
                cell:ClearAllPoints()
                if (XPerlConfig.RaidUpward == 1) then
                        cell:SetPoint("BOTTOMLEFT", parentGroup, "TOPLEFT", 0, t)
                else
                        cell:SetPoint("TOPLEFT", parentGroup, "BOTTOMLEFT", 0, t)
                end
        end
end

-- XPerl_Raid_Classes
local function XPerl_Raid_Classes()
        local groups = {WARRIOR = 1, MAGE = 2, PRIEST = 3, WARLOCK = 4, DRUID = 5, ROGUE = 6, HUNTER = 7, PALADIN = 8, SHAMAN = 9}

        for num=1,MAX_RAID_MEMBERS do
                local localClass, class = UnitClass("raid"..num)
                if (class) then
                        if (not ClassNames[class]) then
                                ClassNames[class] = localClass
                        end

                        local frame = GetRaidUnit(num, true)

                        local subgroup = groups[class]
                        if (subgroup) then
                                if (subgroup == 9) then         -- Burning Crusade - REMOVE THIS
                                        subgroup = 8
                                end

                                GetRaidGroup(subgroup, true)
                                local wasShowing = frame.showing
                                frame.showing = XPerlConfig["ShowGroup"..subgroup]
                                MoveCell(frame, subgroup, wasShowing)
                                RaidGroupCounts[subgroup] = RaidGroupCounts[subgroup] + 1
                        end

                else
                        local frame = GetRaidUnit(num)
                        if (frame) then
                                frame.showing = 0
                        end
                end
        end

        for name,index in pairs(groups) do
                local frame = getglobal(XPERL_RAIDGRP_PREFIX..index.."_NameFrame_NameBarText")
                if (frame) then
                        if (index == 8) then    -- Burning Crusade - REMOVE THIS
                                if (UnitFactionGroup("player") == "Horde") then
                                        frame:SetText(ClassNames["SHAMAN"])
                                else
                                        frame:SetText(ClassNames["PALADIN"])
                                end
                                break
                        end

                        frame:SetText(ClassNames[name])
                end
        end
end

-- XPerl_Raid_Groups
local function XPerl_Raid_Groups()
        for num=1,MAX_RAID_MEMBERS do
                local name, rank, subgroup= GetRaidRosterInfo(num)
                if (name and subgroup) then
                        local frame = GetRaidUnit(num, true)

                        if (frame) then
                                GetRaidGroup(subgroup, true)
                                local localClass, class = UnitClass("raid"..num)

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

                                if (name and subgroup and subgroup >= 1 and subgroup <= NUM_RAID_GROUPS) then
                                        local wasShowing = frame.showing
                                        frame.showing = XPerlConfig["ShowGroup"..subgroup]
                                        MoveCell(frame, subgroup, wasShowing)
                                        RaidGroupCounts[subgroup] = RaidGroupCounts[subgroup] + 1
                                else
                                        frame.showing = 0
                                end
                        end
                else
                        local frame = GetRaidUnit(num)
                        if (frame) then
                                frame.showing = 0
                        end
                end
        end

        for index = 1,NUM_RAID_GROUPS do
                local frame = getglobal(XPERL_RAIDGRP_PREFIX..index.."_NameFrame_NameBarText")
                if (frame) then
                        frame:SetText(string.format(XPERL_RAID_GROUP, index))
                end
        end
end

-- XPerl_Raid_Position()
function XPerl_Raid_Position()
        SetRaidRoster()
        RaidGroupCounts = {0,0,0,0,0,0,0,0,0}

        if (XPerlConfig.SortRaidByClass==1) then
                XPerl_Raid_Classes()
        else
                XPerl_Raid_Groups()
        end

        if (GetNumRaidMembers() == 0 or XPerlConfig.ShowRaid == 0) then
                XPerl_Raid_Frame:Hide()
        else
                XPerl_Raid_Frame:Show()

                for num=1,MAX_RAID_MEMBERS do
                        local frame = getglobal(XPERL_RAIDMEMBER_PREFIX..num)

                        if (frame) then
                                if (frame.showing == 1) then
                                        frame:Show()
                                        XPerl_Raid_UpdateDisplay(frame)
                                else
                                        frame:Hide()
                                end
                        end
                end
        end

        XPerl_RaidTitles()

        Last_Roster = nil
        for i,unit in XPerl_Roster do
                unit.lastPos = nil
        end
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

        oldRaidFrameDropDown_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 = "Auto-Promote"
                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

-- ShowPopup
local function ShowPopup(thisFrame)
        HideDropDownMenu(1)
        FriendsDropDown.initialize = RaidFrameDropDown_Initialize
        FriendsDropDown.displayMode = "MENU"

        this.unit = thisFrame.partyid
        this.name = UnitName(thisFrame.partyid)

        local _, _, id = strfind(thisFrame.partyid, "(%d+)")
        this.id = id

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

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

-- XPerl_Raid_OnClick
function XPerl_Raid_OnClick()
        local thisFrame = this:GetParent()
        local unitid = thisFrame.partyid

        if (not XPerl_OnClick_Handler(arg1, unitid)) then
                if (arg1 == "RightButton" and not thisFrame.pet) then
                        ShowPopup(thisFrame)
                end
        end
end

-- XPerl_ScaleRaid
function XPerl_ScaleRaid(num)
        for frame=1,NUM_RAID_GROUPS do
                local f = GetRaidGroup(frame)
                if (f) then
                        f:SetScale(num)
                end
        end
        for id,f in pairs(FrameArray) do
                f:SetScale(num)
        end
        for name,f in pairs(PulloutFrameArray) do
                f:SetScale(num)
        end
end

-- XPerl_RaidTitles
function XPerl_RaidTitles()
        for i = 1,NUM_RAID_GROUPS do
                local frame = GetRaidGroup(i)
                if (frame) then
                        frame:SetAlpha(XPerlConfig.Transparency)
                        if (MovingMember or XPerlLocked == 0 or (XPerlConfig.ShowRaidTitles == 1 and RaidGroupCounts[i] > 0 and XPerlConfig["ShowGroup"..i] == 1)) then
                                frame:Show()
                        else
                                frame:Hide()
                        end
                end
        end
end

-- XPerl_RaidShowAllTitles
function XPerl_RaidShowAllTitles()
        for i = 1,NUM_RAID_GROUPS do
                GetRaidGroup(i, true)
        end
        XPerl_RaidTitles()
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,NUM_RAID_GROUPS do
                        tinsert(ret, GetRaidGroup(i, true))
                end
        elseif (interest == "odd") then
                for i = 1,NUM_RAID_GROUPS,2 do
                        tinsert(ret, GetRaidGroup(i, true))
                end
        elseif (interest == "even") then
                for i = 2,NUM_RAID_GROUPS,2 do
                        tinsert(ret, GetRaidGroup(i, true))
                end
        elseif (interest == "first4") then
                for i = 1,4 do
                        tinsert(ret, GetRaidGroup(i, true))
                end
        elseif (interest == "last4") then
                for i = 5,NUM_RAID_GROUPS do
                        tinsert(ret, GetRaidGroup(i, true))
                end
        end
        return ret
end

-- XPerl_Raid_SetGap
function XPerl_Raid_SetGap(newGap)
        if (not XPerl_Raid_Grp1) then
                return
        end

        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)
                        end

                        framePrev = frame
                end

                SaveAllPositions()
        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)
        end

        SaveAllPositions()
end

-- XPerl_Raid_SetBuffTooltip
function XPerl_Raid_SetBuffTooltip ()
        local partyid = this:GetParent():GetParent().partyid
        GameTooltip:SetOwner(this,"ANCHOR_BOTTOMRIGHT",30,0)

        local show, cureCast = GetShowCast(this:GetParent():GetParent())
        if (show == "b") then
                XPerl_TooltipSetUnitBuff(GameTooltip, partyid, this:GetID(), cureCast)
                XPerl_Raid_AddBuffDuration(partyid)
        elseif (show == "d") then
                XPerl_TooltipSetUnitDebuff(GameTooltip, partyid, this: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 i = 1,GetNumRaidMembers() do
                local frame = GetRaidUnit(i)
                if (frame) then
                        XPerl_Raid_UpdateDisplay(frame)
                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) 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, cd = myRoster.Rebirth - t})
                                                                end
                                                        else
                                                                tinsert(ret, {["name"] = name, class = fileName, cd = 0})
                                                        end
                                                end
                                        end
                                end
                        end
                end

                if (getn(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)) then
                                if (stats.resCount) then
                                        GameTooltip:AddLine(XPERL_LOC_RESURRECTED.." x"..stats.resCount)
                                end

                                local Rezzers = GetCombatRezzerList()
                                if (Rezzers) then
                                        GameTooltip:AddLine("Rezzers Available: "..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 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_Set_Bits
function XPerl_Raid_Set_Bits()

        local msgs = {  "UNIT_DISPLAYPOWER",
                        "UNIT_MANA", "UNIT_MAXMANA",
                        "UNIT_ENERGY", "UNIT_MAXENERGY",
                        "UNIT_RAGE", "UNIT_MAXRAGE"}

        for i,msg in pairs(msgs) do
                if (XPerlConfig.RaidMana == 1) then
                        XPerl_Raid_Frame:RegisterEvent(msg)
                else
                        XPerl_Raid_Frame:UnregisterEvent(msg)
                end
        end

        for i,frame in pairs(FrameArray) do
                Setup1RaidFrame(frame)
                if (frame.showing == 1) then
                        XPerl_Raid_UpdateName(frame)
                end
        end

        for i,frame in pairs(PulloutFrameArray) do
                Setup1RaidFrame(frame)
                if (frame.showing == 1) then
                        XPerl_Raid_UpdateName(frame)
                end
        end
end

-- Blizzard raid pullout replacements

local PetClasses = {HUNTER = true, WARLOCK = true}

function XPerl_ReplaceBlizzardPullouts()

-- RaidPullout_GenerateGroupFrame(groupID)
RaidPullout_GenerateGroupFrame = function(groupID)
        local pets = (IsControlKeyDown() == 1)

        -- construct the button listing
        if ( not groupID ) then
                groupID = this:GetParent():GetID()
        end

        -- Get a handle on a pullout frame
        local pullOutFrame = RaidPullout_GetFrame(groupID)
        if ( pullOutFrame ) then
                pullOutFrame.filterID = groupID
                pullOutFrame.showBuffs = nil

                -- Set pullout name
                local title = GROUP.." "..groupID
                if (pets) then
                        title = title.." "..PET
                end
                getglobal(pullOutFrame:GetName().."Name"):SetText(title)

                -- TODO Title scale to 0.75, but we can't set scale of a fontstring
                if (RaidPullout_Update(pullOutFrame, pets)) then
                        return pullOutFrame
                end
        end
end

RaidPullout_GenerateClassFrame = function(class, fileName)
        local pets = (IsControlKeyDown() == 1)

        -- construct the button listing
        if ( not class ) then
                class = this:GetParent().class;
        end

        if (pets and not PetClasses[fileName]) then  -- not AnyPets(fileName)) then
                return
        end

        -- Get a handle on a pullout frame
        local pullOutFrame = RaidPullout_GetFrame(fileName);
        if ( pullOutFrame ) then
                pullOutFrame.filterID = fileName;
                pullOutFrame.showBuffs = nil;

                -- Set pullout name
                if (pets) then
                        class = class.." "..PET
                end
                getglobal(pullOutFrame:GetName().."Name"):SetText(class);

                -- TODO Title scale to 0.75, but we can't set scale of a fontstring
                if ( RaidPullout_Update(pullOutFrame, pets) ) then
                        return pullOutFrame;
                end
        end
end

RaidPullout_GetFrame = function(groupID)
        -- Grab an available pullout frame
        local frame;
        for i=1, NUM_RAID_PULLOUT_FRAMES do
                frame = getglobal("RaidPullout"..i);
                -- if frame is visible see if its group id is already taken
                if ( frame:IsVisible() and filterID == frame.filterID ) then
                        return nil;
                end
        end
        for i=1, NUM_RAID_PULLOUT_FRAMES do
                frame = getglobal("RaidPullout"..i);
                if ( not frame:IsVisible() ) then
                        return frame;
                end
        end
        NUM_RAID_PULLOUT_FRAMES = NUM_RAID_PULLOUT_FRAMES + 1;
        frame = CreateFrame("Button", "RaidPullout"..NUM_RAID_PULLOUT_FRAMES, UIParent, "RaidPulloutFrameTemplate");
        frame.numPulloutButtons = 0;

        local f = getglobal("RaidPullout"..NUM_RAID_PULLOUT_FRAMES.."MenuBackdrop")
        f:Hide()

        return frame;
end

RaidPullout_Update = function(pullOutFrame, pets)
        if ( not pullOutFrame ) then
                pullOutFrame = this
        end

        if (pets ~= nil) then
                pullOutFrame.pets = pets
        end
        pets = pullOutFrame.pets

        local filterID = pullOutFrame.filterID
        local numPulloutEntries = 0
        if ( RAID_SUBGROUP_LISTS[filterID] ) then
                numPulloutEntries = getn(RAID_SUBGROUP_LISTS[filterID])
        end
        local pulloutList = RAID_SUBGROUP_LISTS[filterID]

        -- Hide the pullout if no entries
        if ( numPulloutEntries == 0 ) then
                pullOutFrame:Hide()
                return nil
        end

        -- Fill out the buttons
        local pulloutButton, pulloutButtonName, color, unit, pulloutHealthBar, pulloutManaBar, unitHPMin, unitHPMax
        local name, rank, subgroup, level, class, fileName, zone, online, isDead
        local debuff

        if (numPulloutEntries > pullOutFrame.numPulloutButtons) then
                local index = pullOutFrame.numPulloutButtons + 1
                local relative
                for i = index, numPulloutEntries do
                        pulloutButton = CreateFrame("Frame", pullOutFrame:GetName().."Button"..i, pullOutFrame, "XPerl_Raid_FrameTemplate")
                        PulloutFrameArray[pulloutButton:GetName()] = pulloutButton

                        if (i == 1) then
                                pulloutButton:SetPoint("TOP", pullOutFrame, "TOP", 0, 0)
                        else
                                relative = getglobal(pullOutFrame:GetName().."Button"..(i-1).."_StatsFrame")
                                pulloutButton:SetPoint("TOP", relative, "BOTTOM", 0, 4)
                        end
                end
                pullOutFrame.numPulloutButtons = numPulloutEntries;
        end

        local j = 1
        for i = 1,numPulloutEntries do
                pulloutButton = getglobal(pullOutFrame:GetName().."Button"..j)
                if (pulloutButton) then
                        local _, ownerClass = UnitClass("raid"..pulloutList[i])
                        if (not pets or PetClasses[ownerClass]) then
                                pulloutButton.showing = 1
                                pulloutButton:SetScale(XPerlConfig.Scale_Raid)
                                Setup1RaidFrame(pulloutButton)

                                pulloutButton.petname = nil
                                if (pets) then
                                        XPerl_Raid_Frame:RegisterEvent("UNIT_PET")
                                        pulloutButton.partyid = "raidpet"..pulloutList[i]
                                        pulloutButton.petOwner = "raid"..pulloutList[i]
                                        pulloutButton.pet = true
                                else
                                        pulloutButton.partyid = "raid"..pulloutList[i]
                                        pulloutButton.pet = nil
                                        pulloutButton.petOwner = nil
                                end

                                XPerl_Raid_UpdateDisplay(pulloutButton)
                                pulloutButton:Show()
                                j = j + 1
                        end
                end
        end
        for i = j,pullOutFrame.numPulloutButtons do
                pulloutButton = getglobal(pullOutFrame:GetName().."Button"..i)
                if (pulloutButton) then
                        pulloutButton.showing = 0
                        pulloutButton:Hide()
                end
        end

        pullOutFrame:SetHeight(1)
        pullOutFrame:Show()
        return 1
end

RaidPulloutDropDown_Initialize = function()
        if ( not UIDROPDOWNMENU_OPEN_MENU or not getglobal(UIDROPDOWNMENU_OPEN_MENU).raidPulloutDropDown ) then
                return;
        end
        local currentPullout = getglobal(UIDROPDOWNMENU_OPEN_MENU):GetParent();
        local info;

        -- Show buffs or debuffs they are exclusive for now
        info = {};
        info.text = SHOW_BUFFS;
        info.func = function()
                currentPullout.showBuffs = 1;
                RaidPullout_Update(currentPullout);
        end;
        if ( currentPullout.showBuffs ) then
                info.checked = 1;
        end
        UIDropDownMenu_AddButton(info);

        info = {};
        info.text = SHOW_DEBUFFS;
        info.func = function()
                currentPullout.showBuffs = nil;
                RaidPullout_Update(currentPullout);
        end;
        if ( not currentPullout.showBuffs ) then
                info.checked = 1;
        end
        UIDropDownMenu_AddButton(info);

        -- Castable/Curable
        info = {};
        if (currentPullout.showBuffs) then
                info.text = SHOW_CASTABLE_BUFFS_TEXT
        else
                info.text = SHOW_DISPELLABLE_DEBUFFS_TEXT
        end
        info.func = function()
                currentPullout.cureCast = not currentPullout.cureCast
                RaidPullout_Update(currentPullout);
        end;
        if (currentPullout.cureCast) then
                info.checked = 1;
        end
        UIDropDownMenu_AddButton(info);

        -- Pets
        info = {}
        if (currentPullout.pets) then
                info.text = "Show Owners"
        else
                info.text = "Show Pets"
        end
        info.func = function()
                currentPullout.pets = not currentPullout.pets
                RaidPullout_Update(currentPullout)

                local titleFrame = getglobal(currentPullout:GetName().."Name")
                local title = titleFrame:GetText()
                if (currentPullout.pets) then
                        title = title.." "..PET
                else
                        title = string.gsub(title, " "..PET, "")
                end
                titleFrame:SetText(title)
        end;
        if (currentPullout.pets) then
                info.checked = 1
        end
        UIDropDownMenu_AddButton(info)

        -- Close option
        info = {};
        info.text = CLOSE;
        info.func = function()
                currentPullout:Hide();
        end;
        UIDropDownMenu_AddButton(info);
end

end