Quantcast
--[[
    Copyright (c) 2010-2011 yaroot(@gmail.com)

    You can do whatever you want with this file, and if you find it
    useful, you can buy me a beer if we meet someday.
--]]

--[=[
    .icon                   [texture]
    .count                  [fontstring]
    .cd                     [cooldown]

    .ShowBossDebuff         [boolean]
    .BossDebuffPriority     [number]

    .ShowDispelableDebuff   [boolean]
    .DispelPriority         [table]     { [type] = prio }
    .DispelFilter           [table]     { [type] = true }
    .DebuffTypeColor        [table]     { [type] = { r, g, b } }

    .Debuffs                [table]     { [name(string)|id(number)] = prio(number) }
    .MatchBySpellName       [boolean]

    .SetBackdropColor       [function]  function(r, g, b) end
--]=]


local _, ns = ...
local oUF = ns.oUF or oUF
assert(oUF, 'oUF RaidDebuffs: unable to locate oUF')

local bossDebuffPrio = 9999999
local invalidPrio = -1
local auraFilters = {
    ['HARMFUL'] = true,
}

local debuffTypeColor = {
    ['none'] = {0, 0, 0},
}
for k, v in next, DebuffTypeColor do
    if(k ~= '' and k ~= 'none') then
        debuffTypeColor[k] = { v.r, v.g, v.b }
    end
end

local dispelPrio = {
    ['Magic']   = 4,
    ['Curse']   = 3,
    ['Disease'] = 2,
    ['Poison']  = 1,
}

local dispelFilter = ({
    PIREST = { Magic = true, Disease = true, },
    SHAMAN = { Magic = true, Curse = true, },
    PALADIN = { Magic = false, Poison = true, Disease = true, },
    MAGE = { Curse = true, },
    DRUID = { Magic = true, Curse = true, Poison = true, },
})[select(2, UnitClass'player')]

local UpdateDebuffFrame = function(rd)
    if(rd.PreUpdate) then
        rd:PreUpdate()
    end

    if(rd.index and rd.type and rd.filter) then
        local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura(rd.__owner.unit, rd.index, rd.filter)

        if(rd.icon) then
            rd.icon:SetTexture(icon)
            rd.icon:Show()
        end

        if(rd.count) then
            if count and (count > 0) then
                rd.count:SetText(count)
                rd.count:Show()
            else
                rd.count:Hide()
            end
        end

        if(rd.cd) then
            if(duration and (duration > 0)) then
                rd.cd:SetCooldown(expirationTime - duration, duration)
                rd.cd:Show()
            else
                rd.cd:Hide()
            end
        end

        if(rd.SetDebuffTypeColor) then
            local colors = rd.DebuffTypeColor or debuffTypeColor
            local c = colors[debuffType] or colors.none or debuffTypeColor.none
            rd:SetDebuffTypeColor(unpack(c))
        end

        if(not rd:IsShown()) then
            rd:Show()
        end
    else
        if(rd:IsShown()) then
            rd:Hide()
        end
    end

    if(rd.PostUpdate) then
        rd:PostUpdate()
    end
end

local Update = function(self, event, unit)
    if(unit ~= self.unit) then return end
    local rd = self.RaidDebuffs
    rd.priority = invalidPrio

    for filter in next, (rd.Filters or auraFilters) do
        local i = 0
        while(true) do
            i = i + 1
            local name, rank, icon, count, debuffType, duration, expirationTime, unitCaster, isStealable, shouldConsolidate, spellId, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura(unit, i, filter)
            if (not name) then break end

            if(rd.ShowBossDebuff and isBossDebuff) then
                local prio = rd.BossDebuffPriority or bossDebuffPrio
                if(prio and prio > rd.priority) then
                    rd.priority = prio
                    rd.index = i
                    rd.type = 'Boss'
                    rd.filter = filter
                end
            end

            if(rd.ShowDispelableDebuff and debuffType) then
                local disPrio = rd.DispelPriority or dispelPrio
                local disFilter = rd.DispelFilter or dispelFilter
                local prio

                if(rd.FilterDispelableDebuff and disFilter) then
                    prio = disFilter[debuffType] and disPrio[debuffType]
                else
                    prio = disPrio[debuffType]
                end

                if(prio and (prio > rd.priority)) then
                    rd.priority = prio
                    rd.index = i
                    rd.type = 'Dispel'
                    rd.filter = filter
                end
            end

            local prio = rd.Debuffs and rd.Debuffs[rd.MatchBySpellName and name or spellId]
            if(prio and (prio > rd.priority)) then
                rd.priority = prio
                rd.index = i
                rd.type = 'Custom'
                rd.filter = filter
            end
        end
    end

    if(rd.priority == invalidPrio) then
        rd.index = nil
        rd.filter = nil
        rd.type = nil
    end

    return (rd.OverrideUpdateFrame or UpdateDebuffFrame) ( rd )
end

local f

local searchFor = function(spell, i)
    local spellName = GetSpellInfo(spell)
    local found
    for j = 1, GetNumSpellTabs() do
        for k = 1, GetNumTalents(j) do
            local talentName, _, _, _, rank = GetTalentInfo(j, k)
            if(talentName and talentName == spellName) then
                return rank and rank > 0
            end
        end
    end
end

local talentTbl = ({
    PALADIN = {
        [53551] = 'Magic',
    },
    SHAMAN = {
        [77130] = 'Magic',
    },
    DRUID = {
        [88423] = 'Magic',
    },
})[select(2, UnitClass'player')]

local spellCheck = function()
    local _, class = UnitClass'player'
    if(talentTbl) then
        for k, v in next, talentTbl do
            dispelFilter[v] = searchFor(k)
        end
    end
end

local Path = function(self, ...)
    return (self.RaidDebuffs.Override or Update) (self, ...)
end

local ForceUpdate = function(element)
    return Path(element.__owner, 'ForceUpdate', element.__owner.unit)
end

local Enable = function(self)
    local rd = self.RaidDebuffs
    if(rd) then
        self:RegisterEvent('UNIT_AURA', Path)
        rd.ForceUpdate = ForceUpdate
        rd.__owner = self

        if(talentTbl and (not f) and (not rd.DispelFilter) and (not rd.Override)) then
            f = CreateFrame'Frame'
            f:SetScript('OnEvent', spellCheck)
            f:RegisterEvent('PLAYER_TALENT_UPDATE')
            f:RegisterEvent('CHARACTER_POINTS_CHANGED')
            spellCheck()
        end

        return true
    end
end

local Disable = function(self)
    if(self.RaidDebuffs) then
        self:UnregisterEvent('UNIT_AURA', Path)
        self.RaidDebuffs:Hide()
        self.RaidDebuffs.__owner = nil
    end
end

oUF:AddElement('RaidDebuffs', Update, Enable, Disable)