Quantcast
--[[-------------------------------------------------------------------------
  Copyright (c) 2006, Jim Whitehead II <cladhaire@gmail.com>
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.
      * Neither the name of PerfectRaid nor the names of its contributors
        may be used to endorse or promote products derived from this software
        without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------]]

local Buffs = PerfectRaid:NewModule("PerfectRaid-Buffs")
local L = PerfectRaidLocals
local utils, frames

function Buffs:Initialize()
	PerfectRaid.defaults.profile.buffs = {}

	frames = PerfectRaid.frames
	utils = PerfectRaid.utils
	self:RegisterMessage("DONGLE_PROFILE_CREATED")
	self:RegisterMessage("PERFECTRAID_TAB_CHANGED")
end

function Buffs:Enable()
	self:RegisterEvent("UNIT_AURA")
	self:RegisterEvent("RAID_ROSTER_UPDATE")
	if GetNumRaidMembers() > 0 then
		self:RAID_ROSTER_UPDATE()
	end

	self:UpdateBuffTable()

	for unit in pairs(frames) do
		self:UNIT_AURA(nil, unit)
	end
end

function Buffs:DONGLE_PROFILE_CREATED(event, db, addon, svname, profileKey)
	if svname ~= "PerfectRaidDB" then return end
	self:PrintF(L["Adding default buffs to new profile \"%s\""], profileKey)
	local buffs = db.profile.buffs
	for k,v in ipairs(self.defaults) do
		if not v.disabled then
			self:AddDefaultBuff(buffs, v)
		end
	end
end

function Buffs:PERFECTRAID_TAB_CHANGED(old,new)
	if PROptions_Buffs_Edit and PROptions_Buffs_Edit:IsVisible() then
		self.options:FadeOut(PROptions_Buffs_Edit)
	end
end

local raidLookup = {}
for i=1,40 do
	raidLookup[i] = "raid"..i
end
function Buffs:RAID_ROSTER_UPDATE()
	if GetNumRaidMembers() > 0 then
		for i=1,GetNumRaidMembers() do
			local group = select(3, GetRaidRosterInfo(i))
			local unit = raidLookup[i]
			if UnitIsUnit(unit, "player") then raidLookup.player = group end
			raidLookup[unit] = group
		end
	end
end

function Buffs:ConfigureButton(button)
	local font = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
	button.aura = font
end

function Buffs:UpdateButtonLayout(button, options)
	button.aura:ClearAllPoints()

	if options.alignright then
		button.aura:SetPoint("RIGHT", button.leftbox, "RIGHT", -2, 0)
	else
		button.aura:SetPoint("LEFT", button.rightbox, "LEFT", 2, 0)
	end
end

function Buffs:ShowButton(button)
	local unit = button:GetAttribute("unit")
	self:UNIT_AURA(nil, unit)
end

local band = bit.band
local buffs = {}
local buffcache = {}
local mybuffs = {}

local BIT_CURSE = 1
local BIT_MAGIC = 2
local BIT_POISON = 4
local BIT_DISEASE = 8

local debuffstatus = setmetatable({}, {__index=function(t,k) rawset(t,k,0); return 0 end})

local work = {}
local patterns = setmetatable({}, {__index=function(t,k)
											   local str = string.rep("%s ", k):sub(1,-2)
											   rawset(t,k,str)
											   return str
										   end})

function Buffs:UNIT_AURA(event, unit)
	if not frames[unit] then return end

	for k,v in pairs(buffcache) do buffcache[k] = nil end
	for k,v in pairs(mybuffs) do mybuffs[k] = nil end

	for i=1,40 do
		local name,rank,texture,count,duration,timeleft = UnitBuff(unit, i)
		if not name then break end
		buffcache[name] = (buffcache[name] or 0) + 1
		-- Flag this, if this is our buff
		if timeleft then
			mybuffs[name] = true
		end
	end

	local debuffType
	for i=1,40 do
		local name,rank,texture,count,type,durating,timeleft = UnitDebuff(unit, i)
		if not name and not type then break end
		buffcache[name] = (buffcache[name] or 0) + 1
		-- Flag this, if this is our debuff
		if timeleft then
			mybuffs[name] = true
		end

		if type and name ~= type then
			buffcache[type] = (buffcache[type] or 0) + 1
			debuffType = type
		end
	end

	-- Raise any relevant debuff gained/lost messages
	local status = debuffstatus[unit]

	-- Disease
	if buffcache.Disease and band(status, BIT_DISEASE) == 0 then
		status = status + BIT_DISEASE
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_GAINED", unit, "Disease")
	elseif not buffcache.Disease and band(status, BIT_DISEASE) > 0 then
		status = status - BIT_DISEASE
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_LOST", unit, "Disease")
	end

	if buffcache.Poison and band(status, BIT_POISON) == 0 then
		status = status + BIT_POISON
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_GAINED", unit, "Poison")
	elseif not buffcache.Poison and band(status, BIT_POISON) > 0 then
		status = status - BIT_POISON
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_LOST", unit, "Poison")
	end

	if buffcache.Magic and band(status, BIT_MAGIC) == 0 then
		status = status + BIT_MAGIC
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_GAINED", unit, "Magic")
	elseif not buffcache.Magic and band(status, BIT_MAGIC) > 0 then
		status = status - BIT_MAGIC
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_LOST", unit, "Magic")
	end

	if buffcache.Curse and band(status, BIT_CURSE) == 0 then
		status = status + BIT_CURSE
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_GAINED", unit, "Curse")
	elseif not buffcache.Curse and band(status, BIT_CURSE) > 0 then
		status = status - BIT_CURSE
		self:TriggerMessage("PERFECTRAID_DEBUFFTYPE_LOST", unit, "Curse")
	end

	-- Update the status
	debuffstatus[unit] = status

	for k,v in pairs(work) do work[k] = nil end

	for i,entry in ipairs(buffs) do
		local checkcond = false
		local num = buffcache[entry.buffname]
		if entry.missing then
			if not buffcache[entry.buffname] and not buffcache[entry.groupname or "nil"] then
				checkcond = true
			end
		else
			if buffcache[entry.buffname] or buffcache[entry.groupname or "nil"] then
				checkcond = true
			end
		end

		local class = select(2, UnitClass(unit))
		local conds = self.conditions
		local group = raidLookup[unit]

		-- Handle the condition checking for the buff, taking strictness into account
		if checkcond then
			if entry.strict then
				-- Set up a boolean value so we can trip it if a filter fails
				local pass = true

				for i,name in pairs(entry.cond) do
					if conds[name] and not conds[name](unit, class, group) then
						pass = false
					end
				end

				if pass then
					if num and num > 1 then
						table.insert(work, entry.colortext .. "(" .. num .. ")")
					else
						table.insert(work, entry.colortext)
					end
				end
			else
				-- Simply iterate each of the conditions, and break when we match
				for i,name in pairs(entry.cond) do
					if conds[name] and conds[name](unit, class, group) then
						if num and num > 1 then
							table.insert(work, entry.colortext .. "(" .. num .. ")")
						else
							table.insert(work, entry.colortext)
						end

						break
					end
				end
			end
		end
	end

	for frame in pairs(frames[unit]) do
		frame.aura:SetFormattedText(patterns[#work], unpack(work))
	end
end

function Buffs:CreateOptions(opt)
	self.options = opt
	local options = CreateFrame("Frame", "PROptions_Buffs", PROptions)
	opt:AddOptionsTab(L["Buffs/Debuffs"], options)

	local num_entries = 15
	local scrollframe = opt:CreateListFrame(options, num_entries)

	-- Set up double-click handlers
	local function OnDoubleClick(button)
		button:Click()
		self:EditEntry()
	end

	for i=1,num_entries do
		button = scrollframe.entries[i]
		button:SetScript("OnDoubleClick", OnDoubleClick)
	end

	local update = function()
		local list = PerfectRaid.db.profile.buffs

		local idx,button
		local offset = FauxScrollFrame_GetOffset(scrollframe)
		FauxScrollFrame_Update(scrollframe, #list, 15, 20)
		for i=1,num_entries do
			idx = offset + i
			button = scrollframe.entries[i]
			if idx <= #list then
				local entry = list[idx]
				local display = entry.buffname

				if entry.groupname then
					display = display .. "/"..entry.groupname
				end
				display = display .. " (|cFF"..entry.color..entry.disptext.."|r)"

				if entry.disabled then
					display = display .. L[" *** DISABLED ***"]
				end

				button.line1:SetText(display)
				button:Show()
				if options.selected == idx then
					button:SetChecked(true)
				else
					button:SetChecked(false)
				end
			else
				button:Hide()
			end
		end
	end

	scrollframe.update = update

	scrollframe:SetScript("OnVerticalScroll", function()
		FauxScrollFrame_OnVerticalScroll(20, update)
		self:EnableButtons()
	end)
	scrollframe:SetScript("OnShow", function()
		scrollframe.update()
		self:EnableButtons()
	end)

	local postClick = function()
		self:EnableButtons()
	end

	for i,frame in ipairs(scrollframe.entries) do
		frame:SetScript("PostClick", postClick)
	end

	local delete = CreateFrame("Button", "PRBuffs_Delete", options, "PRButtonTemplate")
	delete:SetText(L["Delete"])
	delete:SetPoint("BOTTOMRIGHT", 0, 5)
	delete:SetScript("OnClick", function() self:DeleteEntry() end)
	delete:Show()

	local edit = CreateFrame("Button", "PRBuffs_Edit", options, "PRButtonTemplate")
	edit:SetText(L["Edit"])
	edit:SetPoint("BOTTOMRIGHT", delete, "BOTTOMLEFT", -10, 0)
	edit:SetScript("OnClick", function() self:EditEntry() end)
	edit:Show()

	local add = CreateFrame("Button", "PRBuffs_Add", options, "PRButtonTemplate")
	add:SetText(L["Add"])
	add:SetPoint("BOTTOMRIGHT", edit, "BOTTOMLEFT", -10, 0)
	add:SetScript("OnClick", function() self:AddEntry() end)
	add:Show()

	local disable = CreateFrame("Button", "PRBuffs_Disable", options, "PRButtonTemplate")
	disable:SetText(L["Disable"])
	disable:SetPoint("BOTTOMRIGHT", add, "BOTTOMLEFT", -10, 0)
	disable:SetScript("OnClick", function() self:DisableEntry() end)
	disable:Show()

	self:CreateEditFrame(options)
end

function Buffs:CreateEditFrame(parent)
	local frame = CreateFrame("Frame", "PROptions_Buffs_Edit", PROptions)
	local name = "PROptions_Buffs_Edit"
--	frame:SetPoint("TOPLEFT", 15, -25)
--	frame:SetPoint("BOTTOMRIGHT", -15, 15)
	frame:SetAllPoints(PROptions_Buffs)
	frame:SetFrameLevel(frame:GetFrameLevel() + 1)
	frame:Hide()

	local buffname = CreateFrame("EditBox", name.."BuffName", frame, "InputBoxTemplate")
	local font = buffname:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
	font:SetText(L["Buff Name:"])
	font:SetPoint("BOTTOMLEFT", buffname, "TOPLEFT", 0, 3)
	buffname:SetPoint("TOPLEFT", 0, -10)
	buffname:SetAutoFocus(nil)
	buffname:SetWidth(180)
	buffname:SetHeight(20)
	buffname:Show()
	frame.buffname = buffname

	local groupname = CreateFrame("EditBox", name.."GroupName", frame, "InputBoxTemplate")
	local font = groupname:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
	font:SetText(L["Group Buff Name:"])
	font:SetPoint("BOTTOMLEFT", groupname, "TOPLEFT", 0, 3)
	groupname:SetPoint("TOPLEFT", 220, -10)
	groupname:SetAutoFocus(nil)
	groupname:SetWidth(180)
	groupname:SetHeight(20)
	groupname:Show()
	frame.groupname = groupname

	local disptext = CreateFrame("EditBox", name.."DispText", frame, "InputBoxTemplate")
	local font = disptext:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
	font:SetText(L["Display Text:"])
	font:SetPoint("BOTTOMLEFT", disptext, "TOPLEFT", 0, 3)
	disptext:SetPoint("TOPLEFT", 0, -50)
	disptext:SetAutoFocus(nil)
	disptext:SetWidth(180)
	disptext:SetHeight(20)
	disptext:Show()
	frame.disptext = disptext

	-- Color Swatch
	local function colorSwatchCancel()
		local self = ColorPickerFrame.object;
		local r, g, b = self.r, self.g, self.b;
		self.normalTexture:SetVertexColor(r, g, b);
	end

	local function colorSwatchColor()
		local self = ColorPickerFrame.object;
		local r, g, b = ColorPickerFrame:GetColorRGB();
		self.normalTexture:SetVertexColor(r, g, b);
		self.color[1] = r
		self.color[2] = g
		self.color[3] = b
		disptext:SetTextColor(r,g,b)
	end

	local function colorSwatchOpacity()
		local self = ColorPickerFrame.object;
		local a = OpacitySliderFrame:GetValue();
	end

	local function colorSwatchShow(self)
		local r, g, b, a;
		self.color = self.color or {1,1,1}

		local color = self.color;
		if ( color ) then
			r, g, b, a = unpack(color);
		end

		self.r, self.g, self.b = r, g, b
		self.opacityFunc = colorSwatchOpacity;
		self.swatchFunc = colorSwatchColor;
		self.cancelFunc = colorSwatchCancel;

		ColorPickerFrame.object = self;
		UIDropDownMenuButton_OpenColorPicker(self);
		ColorPickerFrame:SetFrameStrata("TOOLTIP");
		ColorPickerFrame:Raise();
	end

	local function colorSwatchOnClick(self)
		CloseMenus();
		colorSwatchShow(self);
	end

	local function colorSwatchOnEnter(self)
		self.bg:SetVertexColor(1, 0.82, 0);
	end

	local function colorSwatchOnLeave(self)
		self.bg:SetVertexColor(1, 1, 1);
	end

	local swatch = CreateFrame("Button", "PRColorSelect", frame);
	swatch:SetHeight(20) swatch:SetWidth(20)
	swatch:SetPoint("LEFT", disptext, "RIGHT", 5, 0)
	local bg = swatch:CreateTexture(nil, "BACKGROUND");
	local normalTexture = swatch:CreateTexture(nil, "ARTWORK");
	frame.color = swatch

	normalTexture:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch");
	normalTexture:SetAllPoints(swatch);
	swatch:SetNormalTexture(normalTexture);
	bg:SetTexture(.2,.2,.2);
	bg:SetPoint("TOPLEFT", swatch, 1, -1);
	bg:SetPoint("BOTTOMRIGHT", swatch, 0, 1);

	normalTexture:SetVertexColor(1,1,1)

	swatch.bg, swatch.normalTexture = bg, normalTexture;
	swatch.object, swatch.hasAlpha = self, 1

	swatch:SetScript("OnLeave", colorSwatchOnLeave);
	swatch:SetScript("OnEnter", colorSwatchOnEnter);
	swatch:SetScript("OnClick", colorSwatchOnClick);

	buffname:SetScript("OnTabPressed", function() if IsShiftKeyDown() then disptext:SetFocus() else groupname:SetFocus() end end)
	groupname:SetScript("OnTabPressed", function() if IsShiftKeyDown() then buffname:SetFocus() else disptext:SetFocus() end end)
	disptext:SetScript("OnTabPressed", function() if IsShiftKeyDown() then groupname:SetFocus() else buffname:SetFocus() end end)

	local function makeCheck(value)
		local button = CreateFrame("CheckButton", "PRBuffCond_"..value, PROptions_Buffs_Edit, "PRCheckTemplate")
		button.Label:SetText(value)
		button.value = value
		return button
	end

	PROptions_Buffs_Edit.checks = {}
	local checks = PROptions_Buffs_Edit.checks

	-- Build condition checkboxes

	checks[1] = makeCheck(self.conditions[1])
	checks[1]:SetPoint("TOPLEFT", disptext, "BOTTOMLEFT", 0, -20)
	checks[1]:Show()

	for i=2,6 do
		checks[i] = makeCheck(self.conditions[i])
		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
		checks[i]:Show()
	end

	checks[7] = makeCheck(self.conditions[7])
	checks[7]:SetPoint("TOPLEFT", checks[1], "BOTTOMLEFT", 0, -10)
	checks[7]:Show()

	for i=8,12 do
		checks[i] = makeCheck(self.conditions[i])
		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
		checks[i]:Show()
	end

	checks[13] = makeCheck(self.conditions[13])
	checks[13]:SetPoint("TOPLEFT", checks[7], "BOTTOMLEFT", 0, -10)
	checks[13]:Show()

	for i=14,18 do
		checks[i] = makeCheck(self.conditions[i])
		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
		checks[i]:Show()
	end

	checks[19] = makeCheck(self.conditions[19])
	checks[19]:SetPoint("TOPLEFT", checks[13], "BOTTOMLEFT", 0, -10)
	checks[19]:Show()

	for i=20,21 do
		checks[i] = makeCheck(self.conditions[i])
		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
		checks[i]:Show()
	end

	local cancel = CreateFrame("Button", "PRBuffs_EditCancel", PROptions_Buffs_Edit, "PRButtonTemplate")
	cancel:SetText(L["Cancel"])
	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
	cancel:SetScript("OnClick", function() self:CancelEntry() end)
	cancel:Show()

	local save = CreateFrame("Button", "PRBuffs_EditSave", PROptions_Buffs_Edit, "PRButtonTemplate")
	save:SetText(L["Save"])
	save:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -10, 0)
	save:SetScript("OnClick", function() self:SaveEntry() end)
	save:Show()

	local missing = CreateFrame("CheckButton", "PRBuffs_Missing", PROptions_Buffs_Edit, "PRCheckTemplate")
	missing.Label:SetText(L["Only show if this buff is missing"])
	missing:SetPoint("BOTTOMLEFT", 0, 10)
	missing:Show()
	frame.missing = missing

	local disabled = CreateFrame("CheckButton", "PRBuffs_Disabled", PROptions_Buffs_Edit, "PRCheckTemplate")
	disabled.Label:SetText(L["Do not check this buff (Disable)"])
	disabled:SetPoint("BOTTOMLEFT", missing, "TOPLEFT", 0, 10)
	disabled:Show()
	frame.disabled = disabled

	local strict = CreateFrame("CheckButton", "PRBuffs_Strict", PROptions_Buffs_Edit, "PRCheckTemplate")
	strict.Label:SetText(L["Make filters strict"])
	strict:SetPoint("TOPLEFT", disabled, "TOPRIGHT", 200, 0)
	strict:Show()
	frame.strict = strict

	local dropdown = CreateFrame("Frame", "PRBuffs_Dropdown", PROptions_Buffs_Edit, "UIDropDownMenuTemplate")
	dropdown:SetID(1)
	dropdown:SetPoint("BOTTOMRIGHT", -115, 30)
	dropdown:SetScript("OnShow", function() self:DropDown_OnShow() end)

	PRBuffs_DropdownButton:SetScript("OnClick", function() ToggleDropDownMenu(nil, nil, PRBuffs_Dropdown, "cursor") end)
	UIDropDownMenu_SetText(L["Auto-fill Default"], dropdown)
end

function Buffs:FillEntry(entry)
	local options = PROptions_Buffs_Edit
	options.buffname:SetText(entry.buffname)
	options.groupname:SetText(entry.groupname or "")
	options.disptext:SetText(entry.disptext)
	options.disptext:SetTextColor(utils.HexToRGBPerc(entry.color))
	PRColorSelect.normalTexture:SetVertexColor(utils.HexToRGBPerc(entry.color))

	local cond = {string.split(",", entry.conds)}
	for k,v in ipairs(cond) do
		cond[v] = true
	end
	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
		if cond[button.value] then
			button:SetChecked(true)
		else
			button:SetChecked(false)
		end
	end

	options.missing:SetChecked(entry.missing)
	options.disabled:SetChecked(entry.disabled)
	options.strict:SetChecked(entry.strict)
end

function Buffs:EditEntry()
	local scrollframe = PROptions_BuffsScrollFrame
	local offset = FauxScrollFrame_GetOffset(scrollframe)
	local selected = PROptions_Buffs.selected
	local entry = PerfectRaid.db.profile.buffs[selected]

	local options = PROptions_Buffs_Edit
	options.editEntry = entry
	self.options:FadeOut(PROptions_Buffs)
	self.options:FadeIn(options)
	self:FillEntry(entry)
end

function Buffs:DisableEntry()
	local scrollframe = PROptions_BuffsScrollFrame
	local offset = FauxScrollFrame_GetOffset(scrollframe)
	local selected = PROptions_Buffs.selected
	local idx = offset + selected
	local entry = PerfectRaid.db.profile.buffs[idx]
	entry.disabled = not entry.disabled
	scrollframe.update()
	self:EnableButtons()
	self:UpdateBuffTable()
end

function Buffs:AddEntry()
	local options = PROptions_Buffs_Edit
	self.options:FadeOut(PROptions_Buffs)
	self.options:FadeIn(options)
	options.buffname:SetText("")
	options.groupname:SetText("")
	options.disptext:SetText("")
	options.disptext:SetTextColor(1,1,1)
	PRColorSelect.normalTexture:SetVertexColor(1,1,1)

	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
		button:SetChecked(false)
	end
	options.missing:SetChecked(false)
	options.disabled:SetChecked(false)
	options.strict:SetChecked(false)
	options.buffname:SetFocus()
end

function Buffs:DeleteEntry()
	local scrollframe = PROptions_BuffsScrollFrame
	local offset = FauxScrollFrame_GetOffset(scrollframe)
	local selected = PROptions_Buffs.selected
	local idx = offset + selected
	table.remove(PerfectRaid.db.profile.buffs, idx)
	PROptions_Buffs.selected = nil
	scrollframe.update()
	self:EnableButtons()
	self:UpdateBuffTable()
end

function Buffs:UpdateBuffTable()
	for k,v in pairs(buffs) do buffs[k] = nil end

	for idx,entry in ipairs(PerfectRaid.db.profile.buffs) do
		if not entry.disabled then
			local tbl = {}
			tbl.buffname = entry.buffname
			tbl.groupname = entry.groupname
			tbl.colortext = "|cFF"..entry.color..entry.disptext.."|r"
			tbl.missing = entry.missing
			tbl.strict = entry.strict
			tbl.cond = {string.split(",", entry.conds)}
			table.insert(buffs, tbl)
		end
	end

	for unit in pairs(frames) do
		self:UNIT_AURA(nil, unit)
	end
end

function Buffs:SaveEntry()
	local frame = PROptions_Buffs_Edit

	local buffname = frame.buffname:GetText()
	local groupname = frame.groupname:GetText()
	local disptext = frame.disptext:GetText()

	local err
	if buffname == "" then
		err = "You must supply a buff name"
	elseif disptext == "" then
		err = "You must supply a display text"
	end

	if err then
		StaticPopupDialogs["PR_BUFF_SAVE_ERROR"].text = err
		StaticPopup_Show("PR_BUFF_SAVE_ERROR")
		return
	end

	if groupname == "" then
		groupname = nil
	end

	local options = PROptions_Buffs_Edit
	local entry = options.editEntry or {}

	entry.buffname = buffname
	entry.groupname = groupname
	entry.disptext = disptext

	local work = {}
	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
		if button:GetChecked() then
			table.insert(work, button.value)
		end
	end

	local conds = strjoin(",", unpack(work))
	entry.conds = conds
	entry.missing = frame.missing:GetChecked()
	entry.disabled = frame.disabled:GetChecked()
	entry.strict = frame.strict:GetChecked()

	local color = utils.RGBPercToHex(frame.disptext:GetTextColor())
	entry.color = color
	if not options.editEntry then
		table.insert(PerfectRaid.db.profile.buffs, entry)
	end

	options.editEntry = nil

	self:CancelEntry()
	PROptions_BuffsScrollFrame.update()
	self:UpdateBuffTable()
end

function Buffs:CancelEntry()
	local options = PROptions_Buffs_Edit
	self.options:FadeOut(options)
	options.editEntry = nil
	self.options:FadeIn(PROptions_Buffs)
end

function Buffs:EnableButtons()
	local selected = PROptions_Buffs.selected
	if selected then
		PRBuffs_Edit:Enable()
		PRBuffs_Delete:Enable()
		PRBuffs_Disable:Enable()

		-- Change enable/disable
		local scrollframe = PROptions_BuffsScrollFrame
		local offset = FauxScrollFrame_GetOffset(scrollframe)
		local selected = PROptions_Buffs.selected
		local idx = offset + selected
		local entry = PerfectRaid.db.profile.buffs[idx]
		if entry.disabled then
			PRBuffs_Disable:SetText(L["Enable"])
		else
			PRBuffs_Disable:SetText(L["Disable"])
		end
	else
		PRBuffs_Edit:Disable()
		PRBuffs_Delete:Disable()
		PRBuffs_Disable:Disable()
	end
end

Buffs.conditions = {
	["All"] = function(u,c,g) return true end,
	["Warrior"] = function(u,c,g) return c == "WARRIOR" end,
	["Priest"] = function(u,c,g) return c == "PRIEST" end,
	["Druid"] = function(u,c,g) return c == "DRUID" end,
	["Paladin"] = function(u,c,g) return c == "PALADIN" end,
	["Shaman"] = function(u,c,g) return c == "SHAMAN" end,
	["Hunter"] = function(u,c,g) return c == "HUNTER" end,
	["Rogue"] = function(u,c,g) return c == "ROGUE" end,
	["Warlock"] = function(u,c,g) return c == "WARLOCK" end,
	["Mage"] = function(u,c,g) return c == "MAGE" end,
	["Mana"] = function(u,c,g) return c == "DRUID" or c == "PRIEST" or c == "PALADIN" or c == "SHAMAN" or c == "MAGE" or c == "WARLOCK" or c == "HUNTER" end,
	["Healer"] = function(u,c,g) return c == "PRIEST" or c == "SHAMAN" or c == "DRUID" or c == "PALADIN" end,
	["Group 1"] = function(u,c,g) return g == 1 end,
	["Group 2"] = function(u,c,g) return g == 2 end,
	["Group 3"] = function(u,c,g) return g == 3 end,
	["Group 4"] = function(u,c,g) return g == 4 end,
	["Group 5"] = function(u,c,g) return g == 5 end,
	["Group 6"] = function(u,c,g) return g == 6 end,
	["Group 7"] = function(u,c,g) return g == 7 end,
	["Group 8"] = function(u,c,g) return g == 8 end,
	["MyGroup"] = function(u,c,g) return g == raidLookup.player end,
}

local work = {}
for k,v in pairs(Buffs.conditions) do
	table.insert(work, k)
end
table.sort(work)

for k,v in ipairs(work) do
	Buffs.conditions[k] = v
end

StaticPopupDialogs["PR_BUFF_SAVE_ERROR"] = {
	text = "",
	button1 = TEXT(OKAY),
	OnAccept = function()
	end,
	timeout = 0,
	hideOnEscape = 1
}

local click_func = function() Buffs:DropDown_OnClick() end
local init_func = function() Buffs:DropDown_Initialize() end

function Buffs:DropDown_Initialize()
    local info = {}

    for k,v in pairs(self.defaults) do
        info = {}
        info.text = string.format("%s (|cFF%s%s|r)", v.buffname, v.color, v.disptext)
        info.value = v
        info.func = click_func
        UIDropDownMenu_AddButton(info)
	end
end

function Buffs:DropDown_OnClick()
	local entry = this.value
	self:FillEntry(entry)
end

function Buffs:DropDown_OnShow(this)
	table.sort(self.defaults, function(a,b) return a.buffname < b.buffname end)
	UIDropDownMenu_Initialize(PRBuffs_Dropdown, init_func);
end

function Buffs:AddDefaultBuff(profile, entry)
	local tbl = {}
	for k,v in pairs(entry) do tbl[k] = v end
	tbl.disabled = nil
	table.insert(profile, tbl)
end

local class = select(2, UnitClass("player"))
local race = select(2, UnitRace("player"))

Buffs.defaults = {
	{
		conds = "All",
		buffname = L["Power Word: Fortitude"],
		groupname = L["Prayer of Fortitude"],
		disptext = L["STATUS_FORT"],
		color = "FFFFFF",
		missing = true,
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Power Word: Shield"],
		disptext = L["STATUS_PWS"],
		color = "FFD800",
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Weakened Soul"],
		disptext = L["STATUS_WEAKENEDSOUL"],
		color = "FF5500",
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Renew"],
		disptext = L["STATUS_RENEW"],
		color = "00FF10",
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Shadow Protection"],
		groupname = L["Prayer of Shadow Protection"],
		disptext = L["STATUS_SHADOWPROT"],
		missing = true,
		color = "9900FF",
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Divine Spirit"],
		groupname = L["Prayer of Spirit"],
		disptext = L["STATUS_DIVINESPIRIT"],
		missing = true,
		color = "9900FF",
		disabled = (class ~= "PRIEST"),
	},
	{
		conds = "All",
		buffname = L["Power Infusion"],
		disptext = L["STATUS_POWERINFUSION"],
		color = "00FF33",
	},
	{
		conds = "All",
		buffname = L["Fear Ward"],
		disptext = L["STATUS_FEARWARD"],
		color = "9900FF",
		disabled = (class ~= "PRIEST" and race == "Dwarf"),
	},
	{
		conds = "All",
		buffname = L["Prayer of Mending"],
		disptext = L["STATUS_PRAYERMEND"],
		color = "FFCF7F",
		disabled = (class ~= "PRIEST"),
	},
	-- Druid specific spells
	{
		conds = "All",
		buffname = L["Mark of the Wild"],
		groupname = L["Gift of the Wild"],
		disptext = L["STATUS_MOTW"],
		color = "BC64AA",
		missing = true,
		disabled = (class ~= "DRUID"),
	},
	{
		conds = "All",
		buffname = L["Rejuvenation"],
		disptext = L["STATUS_REJUV"],
		color = "bc64aa",
		disabled = (class ~= "DRUID"),
	},
	{
		conds = "All",
		buffname = L["Lifebloom"],
		disptext = L["STATUS_LIFEBLOOM"],
		color = "50fe37",
		disabled = (class ~= "DRUID"),
	},
	{
		conds = "All",
		buffname = L["Regrowth"],
		disptext = L["STATUS_REGROWTH"],
		color = "00FF10",
		disabled = (class ~= "DRUID"),
	},
	{
		conds = "All",
		buffname = L["Thorns"],
		disptext = L["STATUS_THORNS"],
		color = "aa6644",
		disabled = (class ~= "DRUID"),
	},
	{
		conds = "Mana",
		buffname = L["Innervate"],
		disptext = L["STATUS_INNERVATE"],
		color = "00FF33",
	},
	-- Paladin Buffs
	{
		conds = "Melee",
		buffname = L["Blessing of Might"],
		groupname = L["Greater Blessing of Might"],
		disptext = L["STATUS_BLESSINGMIGHT"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	{
		conds = "Mana",
		buffname = L["Blessing of Wisdom"],
		groupname = L["Greater Blessing of Wisdom"],
		disptext = L["STATUS_BLESSINGWISDOM"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	{
		conds = "NonTank",
		buffname = L["Blessing of Salvation"],
		groupname = L["Greater Blessing of Salvation"],
		disptext = L["STATUS_BLESSINGSALVATION"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	{
		conds = "All",
		buffname = L["Blessing of Light"],
		groupname = L["Greater Blessing of Light"],
		disptext = L["STATUS_BLESSINGLIGHT"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	{
		conds = "All",
		buffname = L["Blessing of Sanctuary"],
		groupname = L["Greater Blessing of Sanctuary"],
		disptext = L["STATUS_BLESSINGSANCTUARY"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	{
		conds = "All",
		buffname = L["Blessing of Kings"],
		groupname = L["Greater Blessing of Kings"],
		disptext = L["STATUS_BLESSINGKINGS"],
		color = "F48CBA",
		missing = true,
		disabled = (class ~= "PALADIN"),
	},
	-- Mage Buffs
	{
		conds = "Mana",
		buffname = L["Arcane Intellect"],
		groupname = L["Arcane Brilliance"],
		disptext = L["STATUS_ARCANEINT"],
		color = "3399FF",
		missing = true,
		disabled = (class ~= "MAGE"),
	},
	-- Warlock Buffs
	{
		conds = "All",
		buffname = L["Soulstone Resurrection"],
		disptext = L["STATUS_SOULSTONE"],
		color = "CA21FF",
	},
	-- Misc Debuffs
	{
		conds = "All",
		buffname = L["Mortal Strike"],
		disptext = L["STATUS_MORTALSTRIKE"],
		color = "FF1111",
	},
	-- Debuffs.  These should not be localized.
	{
		conds = "All",
		buffname = "Curse",
		disptext = L["STATUS_CURSE"],
		color = "9900FF",
		disabled = (class ~= "MAGE" and class ~= "DRUID")
	},
	{
		conds = "All",
		buffname = "Poison",
		disptext = L["STATUS_POISON"],
		color = "009900",
		disabled = (class ~= "PALADIN" and class ~= "DRUID" and class ~= "SHAMAN")
	},
	{
		conds = "All",
		buffname = "Disease",
		disptext = L["STATUS_DISEASE"],
		color = "996600",
		disabled = (class ~= "PRIEST" and class ~= "PALADIN" and class ~= "SHAMAN")
	},
	{
		conds = "All",
		buffname = "Magic",
		disptext = L["STATUS_MAGIC"],
		color = "3399FF",
		disabled = (class ~= "PRIEST" and class ~= "PALADIN")
	},
	{
		conds = "All",
		buffname = "Food",
		disptext = "Foo",
		color = "d79a6d",
	},
	{
		conds = "All",
		buffname = "Drink",
		disptext = "Dr",
		color = "00a1de",
	},
}