--[[---------------------------------------------------------------------------------
 Detox
 written by Maia/Proudmoore, 
 code based on the Decursive 1.9.7 core code by Archarodim and Quu.
 The name 'Detox' was provided by Tem.
------------------------------------------------------------------------------------]]

local vmajor,vminor = "0.3", tonumber(string.sub("$Revision: 10659 $", 12, -3))

--[[---------------------------------------------------------------------------------
 locals
------------------------------------------------------------------------------------]]

local compost           = AceLibrary("Compost-2.0")
local aura              = AceLibrary("SpecialEvents-Aura-2.0")
local L                 = AceLibrary("AceLocale-2.0"):new("Detox")
local BS                = AceLibrary("Babble-Spell-2.0")
local BC                = AceLibrary("Babble-Class-2.0")
local RL                = AceLibrary("RosterLib-2.0")
local tablet            = AceLibrary("Tablet-2.0")

local last_DemonType    = nil
local curr_DemonType    = nil
local castTarget        = nil
local soundPlayed       = false
local hasSpells         = false
local blacklist         = {}
local outofrange        = {}
local spells            = {} -- will be filled with available spells
local priorityIndex     = {} -- this table references name to index of the priority table
local priority          = {} -- this integer keyed table will hold subtables containing name, unitid and priority for each unit in our roster

local DebuffTypeColor      = {}
DebuffTypeColor["none"]    = { r = 0.80, g = 0, b = 0 }
DebuffTypeColor["Magic"]   = { r = 0.20, g = 0.60, b = 1.00 }
DebuffTypeColor["Curse"]   = { r = 0.60, g = 0.00, b = 1.00 }
DebuffTypeColor["Disease"] = { r = 0.60, g = 0.40, b = 0 }
DebuffTypeColor["Poison"]  = { r = 0.00, g = 0.60, b = 0 }

local invisible = {
    [BS["Prowl"]]                 = true,
    [BS["Stealth"]]               = true,
    [BS["Shadowmeld"]]            = true,
}

local ignore = {
    [BS["Banish"]]                = true,
    [BS["Phase Shift"]]           = true,
}

local skip = {
    [L["Dreamless Sleep"]]        = true,
    [L["Greater Dreamless Sleep"]] = true,
--    [BS["Mind Vision"]] = true,
}

local skipByClass = {
    ["WARRIOR"] = {
		[L["Ancient Hysteria"]]   = true,
		[L["Ignite Mana"]]        = true,
		[L["Tainted Mind"]]       = true,
    };
    ["ROGUE"] = {
		[BS["Silence"]]           = true,
		[L["Ancient Hysteria"]]   = true,
		[L["Ignite Mana"]]        = true,
		[L["Tainted Mind"]]       = true,
    };
   	["HUNTER"] = {
		[L["Magma Shackles"]]     = true,
    };
   	["MAGE"] = {
		[L["Magma Shackles"]]     = true,
    }
}

local cleaningSpells = {
	[BS["Cure Disease"]]          = true,
	[BS["Abolish Disease"]]       = true,
	[BS["Purify"]]                = true,
	[BS["Cleanse"]]               = true,
	[BS["Dispel Magic"]]          = true,
	[BS["Cure Poison"]]           = true,
	[BS["Abolish Poison"]]        = true,
	[BS["Remove Lesser Curse"]]   = true,
	[BS["Remove Curse"]]          = true,
	[BS["Purge"]]                 = true,
	[BS["Devour Magic"]]          = true,
}

BINDING_HEADER_DETOX              = "Detox"
BINDING_NAME_CLEAN                = L["Clean group"]
BINDING_NAME_PRIORITYADD          = L["Add current target to priority list"]
BINDING_NAME_PRIORITYDEL          = L["Remove current target from priority list"]

--[[---------------------------------------------------------------------------------
 defaults and AceOptions table
------------------------------------------------------------------------------------]]

local defaults = {
	ignoreStealthed = true,
	blacklistTime = 5,
	checkAbolish = true,
	scanPets = true,
	liveDisplay = 5,
	filter = false,
	range = true,
	sound = true,
	feedback = true,
	updateSpeed = 0.5,
	priorityNames = {},
}

local options = {
	type = 'group',
	args = {
		clean = {
			type = 'execute',
			name = "Clean group", 
		    desc = "Will attempt to clean a player in your raid/party",
		    func = function()
		        Detox:Clean()
		    end,
			order = 101
		},
		stealth = {
			type = 'toggle',
			name = "Ignore stealthed units",
			desc = "It is recommended not to cure stealthed units.",
			get = function() return Detox.db.profile.ignoreStealthed end,
			set = function()
				Detox.db.profile.ignoreStealthed = not Detox.db.profile.ignoreStealthed
			end,
			order = 110
		},
		abolish = {
			type = 'toggle',
			name = "Skip units that have 'Abolish' buff",
			desc = "Skip units that have an active abolish buff.",
			get = function() return Detox.db.profile.checkAbolish end,
			set = function()
				Detox.db.profile.checkAbolish = not Detox.db.profile.checkAbolish
			end,
			order = 130
		},
		pets = {
			type = 'toggle',
			name = "Scan pets",
			desc = "Pets are your friends.",
			get = function() return Detox.db.profile.scanPets end,
			set = function()
				Detox.db.profile.scanPets = not Detox.db.profile.scanPets
				Detox:UpdatePriority()
			end,
			order = 140
		},
		filter = {
			type = 'toggle',
			name = "Filter display",
			desc = "Only show debuffs you can cure.",
			get = function() return Detox.db.profile.filter end,
			set = function()
				Detox.db.profile.filter = not Detox.db.profile.filter
			end,
			order = 150
		},
		range = {
			type = 'toggle',
			name = "Only show units in range",
			desc = "Only show units in range.",
			get = function() return Detox.db.profile.range end,
			set = function()
				Detox.db.profile.range = not Detox.db.profile.range
			end,
			order = 160
		},
		sound = {
			type = 'toggle',
			name = "Play sound if unit needs decursing",
			desc = "Play sound if unit needs decursing.",
			get = function() return Detox.db.profile.sound end,
			set = function()
				Detox.db.profile.sound = not Detox.db.profile.sound
			end,
			order = 170
		},
		feedback = {
			type = 'toggle',
			name = "Show detoxing in scrolling combat frame",
			desc = "This will use SCT5 when enabled, otherwise Blizzards Floating Combat Text",
			get = function() return Detox.db.profile.feedback end,
			set = function()
				Detox.db.profile.feedback = not Detox.db.profile.feedback
			end,
			order = 180
		},
		blacklist = {
			type = 'range',
			name = "Seconds to blacklist",
			desc = "Units that are out of LoS will be blacklisted.",
			get = function() return Detox.db.profile.blacklistTime end,
			set = function(v) 
				Detox.db.profile.blacklistTime = v
			end,
			min = 1,
			max = 20,
			step = 1,
			isPercent = false,
			order = 200
		},
		live = {
			type = 'range',
			name = "Max live display unit debuffs",
			desc = "Defines the max number of unit debuffs to display in the tooltip.",
			get = function() return Detox.db.profile.liveDisplay end,
			set = function(v) 
				Detox.db.profile.liveDisplay = v
			end,
			min = 0,
			max = 20,
			step = 1,
			isPercent = false,
			order = 300
		},
		speed = {
			type = 'range',
			name = "Live display update speed",
			desc = "Defines the speed the live list is updated.",
			get = function() return Detox.db.profile.updateSpeed end,
			set = function(v) 
				Detox.db.profile.updateSpeed = v
				Detox:CancelScheduledEvent("liveList")
				Detox:ScheduleRepeatingEvent("liveList", Detox.Update, v, Detox)
			end,
			min = 0.2,
			max = 2,
			step = 0.1,
			isPercent = false,
			order = 350
		},
		priority = {
			type = 'group',
			name = "units to priorize",
			desc = "These units will be priorized.",
			order = 400,
			args = {}
		}
	}
}



--[[---------------------------------------------------------------------------------
 Initialization
------------------------------------------------------------------------------------]]

Detox = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceDB-2.0", "AceConsole-2.0", "AceDebug-2.0", "FuBarPlugin-2.0")

-- stuff for FuBar:
Detox.OnMenuRequest = options
--Detox.name = "Detox"  -- should be auto provided if using AceAddon-2.0
Detox.hasIcon = true
Detox.defaultPosition = "LEFT"
Detox.defaultMinimapPosition = 250
Detox.cannotDetachTooltip = false
Detox.tooltipHiddenWhenEmpty = false
Detox.hideWithoutStandby = true
Detox.clickableTooltip = true
Detox.independentProfile = true

function Detox:OnInitialize()
	self:RegisterDB("DetoxDB")
	self:RegisterDefaults('profile', defaults )
	self:RegisterChatCommand({'/detox'}, options )
end

function Detox:OnEnable()
	self:RegisterEvent("SPELLCAST_STOP")
	self:RegisterEvent("UNIT_PET",1)
	self:RegisterEvent("SPELLS_CHANGED","RescanSpellbook",1)
	self:RegisterEvent("LEARNED_SPELL_IN_TAB","RescanSpellbook",1)
	self:RegisterEvent("UI_ERROR_MESSAGE")
	self:RegisterEvent("RosterLib_RosterChanged")
	self:ParseSpellbook()
	self:ScheduleRepeatingEvent(self.OnUpdate, 1, self)
	self:ScheduleRepeatingEvent("liveList", self.Update, Detox.db.profile.updateSpeed or 0.5, self)	-- this is the FuBar live list update!
	self.debugging = self:IsDebugging()
	Detox.OnMenuRequest.args.detachTooltip.name = "Show live list"
end

function Detox:OnDisable()
end

function Detox:OnClick()  -- FuBarPlugin
	self:Clean()
end

function Detox:IsDebugging() return self.db.profile.debugging end
function Detox:SetDebugging(debugging) self.db.profile.debugging = debugging; self.debugging = debugging; end

--[[---------------------------------------------------------------------------------
 Events
------------------------------------------------------------------------------------]]

function Detox:OnUpdate()
	self:UpdateBlacklist()
end


function Detox:UpdateBlacklist()
	for unit in blacklist do
		blacklist[unit] = blacklist[unit] - 1
		if blacklist[unit] < 0 then
			blacklist[unit] = nil
		end
	end
end


function Detox:RosterLib_RosterChanged(tbl)
	for name in tbl do
		local u = tbl[name]
		if not u.name then -- someone left the raid
			for i = table.getn(priority), 1, -1 do
				if not RL.roster[priority[i].name] then
					priorityIndex[priority[i].name] = nil
					compost:Reclaim(priority[i])
					priority[i] = nil
					table.remove(priority, i)
				end
			end
		end
	end
	self:UpdatePriority()
end


function Detox:SPELLCAST_STOP()
	castTarget = nil
end


function Detox:UNIT_PET(unit)
	if unit == "player" then
		self:ScheduleEvent(self.CheckPet, 2, self)
	end
end


function Detox:UI_ERROR_MESSAGE(arg1)
	if arg1 == SPELL_FAILED_LINE_OF_SIGHT then
		if castTarget and not UnitIsUnit(castTarget, "player") then
			blacklist[castTarget] = Detox.db.profile.blacklistTime
	    end
	end
end


--[[---------------------------------------------------------------------------------
 Spellbook
------------------------------------------------------------------------------------]]


function Detox:ParseSpellbook()
	compost:Reclaim(spells,1)
	spells = compost:Acquire()
	hasSpells = false
	local BookType    = BOOKTYPE_SPELL
	local break_flag  = false
	local i = 1
	local maxRankMagic      = 0
	local maxRankEnemyMagic = 0
    while not break_flag do
		while (true) do --  HUH?
		    local name, rank = GetSpellName(i, BookType)
		    if not name then
				if BookType == BOOKTYPE_PET then
			    	break_flag = true
			    	break
				end
				BookType = BOOKTYPE_PET
				i = 1
				break
		    end
		    if cleaningSpells[name] then
				hasSpells = true
				spells["cooldown"] = compost:Acquire(i, BookType)
				if name == BS["Cure Disease"] or name == BS["Purify"] then
					spells["disease1"] = compost:Acquire(i, BookType, name)
				end
				if name == BS["Abolish Disease"] or name == BS["Cleanse"] then
					spells["disease2"] = compost:Acquire(i, BookType, name)
				end
				if name == BS["Cure Poison"] or name == BS["Purify"] then
					spells["poison1"] = compost:Acquire(i, BookType, name)
				end			
				if name == BS["Abolish Poison"] or name == BS["Cleanse"] then
					spells["poison2"] = compost:Acquire(i, BookType, name)
				end
				if name == BS["Remove Curse"] or name == BS["Remove Lesser Curse"] then
					spells["curse"] = compost:Acquire(i, BookType, name)
				end
				if name == BS["Cleanse"] or name == BS["Dispel Magic"] or name == BS["Devour Magic"] then
					if rank and string.find(rank, "Rank (%d+)") then
						local ranknum = string.gsub(rank, "Rank (%d+)", "%1")
						if tonumber(ranknum) > maxRankMagic then
							spells["magic"] = compost:Acquire(i, BookType, name)
							maxRankMagic = tonumber(ranknum)
						end
					end
				end
				if name == BS["Dispel Magic"] or name == BS["Purge"] or name == BS["Devour Magic"] then
					if rank and string.find(rank, "Rank (%d+)") then
						local ranknum = string.gsub(rank, "Rank (%d+)", "%1")
						if tonumber(ranknum) > maxRankEnemyMagic then
							spells["enemymagic"] = compost:Acquire(i, BookType, name)
							maxRankEnemyMagic = tonumber(ranknum)
						end
					end
				end
	    	end
	    	i = i + 1
		end
    end
end


function Detox:RescanSpellbook()
	if not hasSpells then return end
	local valid = true
	for spell in pairs(spells) do
		if GetSpellName(spells[spell][1], spells[spell][2]) ~= spells[spell][3] then 
			valid = false
		end
	end
	if not valid then
		self:ParseSpellbook()
    end
end


function Detox:CheckPet()
	curr_DemonType = UnitCreatureFamily("pet")
	if last_DemonType ~= curr_DemonType then
		last_DemonType = curr_DemonType
		self:ParseSpellbook()
	end
end


--[[---------------------------------------------------------------------------------
 Cleaning
------------------------------------------------------------------------------------]]


function Detox:Clean()
    if not hasSpells then
		self:ParseSpellbook()
		if not hasSpells then
			return false
		end
    end
	-- check for cooldown
	local _, cooldown = GetSpellCooldown(spells["cooldown"][1], spells["cooldown"][2])
	if cooldown ~= 0 then
		return false
	end
	-- temporarily disable selfcasting
	local selfCast = GetCVar("autoSelfCast")
	SetCVar("autoSelfCast", "0")
	-- save target
	local targetEnemy = false
	local targetName = nil
	local cleanedType = nil
	local cleanedName = nil
	castTarget = nil
	-- try to clean target first
	if UnitExists("target") then
		if UnitIsFriend("target", "player") and not UnitIsCharmed("target") then
			cleanedType = self:CureUnit("target")
			if cleanedType then
				cleanedName = UnitName("target")
			end
			targetName = UnitName("target")
		else
	 		targetEnemy = true
	 		if UnitIsCharmed("target") then
  				cleanedType = self:CureUnit("target")
				if cleanedType then
					cleanedName = UnitName("target")
				end
			end
	 	end
	end
	-- couldn't clean target, check for MCd units
	if not cleanedType and spells["enemymagic"] then
		for k,v in ipairs(priority) do
			if v.pri == 0 then break end
			local unit = v.unitid
			if      UnitIsCharmed(unit) 
			and not blacklist[unit] then
				cleanedType = self:CureUnit(unit)
				if cleanedType then
					cleanedName = UnitName(unit)
					break
				end
			end
		end
	end
	-- now do the rest (this is duplicated code, gah!)
	if not cleanedType then
		for k,v in ipairs(priority) do
			if v.pri == 0 then break end
			local unit = v.unitid
			if  not blacklist[unit] 
			and not UnitIsCharmed(unit) then
				cleanedType = self:CureUnit(unit)
				if cleanedType then
					cleanedName = UnitName(unit)
					break
				end
			end
		end	
	end	
	-- now try blacklist
	if not cleanedType then
		for unit in blacklist do
			cleanedType = self:CureUnit(unit)
			if cleanedType then
				cleanedName = UnitName(unit)
				blacklist[unit] = nil
				break
			end
		end
	end
	-- restore target
	if targetEnemy then
		if not UnitIsEnemy("target", "player") then
			TargetUnit("playertarget")
		end
	elseif targetName then
		if targetName ~= UnitName("target") then
			TargetByName(targetName)
		end
	elseif UnitExists("target") then
		ClearTarget()
	end
	-- restore self-cast
	SetCVar("autoSelfCast", selfCast)  -- maybe not do this here but on a delayed timer?
	-- scrolling combat text notification
	if self.db.profile.feedback then
		local text
		local color = {r = 1, g = 1, b = 1}
		if cleanedType and cleanedName then 
			text = "cleaned "..cleanedName
			color = DebuffTypeColor[cleanedType]
		else
			text = "couldn't clean anything"
		end
		if SCT and SCT_MSG_FRAME then -- SCT 5.x
			SCT_MSG_FRAME:AddMessage( text, color.r, color.g, color.b, 1 )
		elseif CombatText_AddMessage then
			-- CHECK FOR COMBAT TEXT
			CombatText_AddMessage(text, COMBAT_TEXT_SCROLL_FUNCTION, color.r, color.g, color.b, nil, nil)
		end
	end
	return cleanedType and true or false
end


function Detox:UnitCurable(unit)
	if not UnitExists(unit) then return false end
	if not UnitIsVisible(unit) then return false end
	if Detox.db.profile.range and not CheckInteractDistance(unit, 4) and not curr_DemonType then return false end
	-- check if unit is stealthed
	if Detox.db.profile.ignoreStealthed then
		for buff in invisible do
			if aura:UnitHasBuff(unit, buff) then return false end
		end
	end
	-- check if we need to ignore unit
	for debuff in ignore do
		if aura:UnitHasDebuff(unit, debuff) then return false end
	end
	-- check for debuffs that are buffs
	for debuff in skip do
		if aura:UnitHasDebuff(unit, debuff) then return false end
	end
	-- check for debuffs we want to skip for specific classes when in combat
	if UnitAffectingCombat("player") then
		local _, class = UnitClass(unit)
		if skipByClass[class] then
			for debuff in skipByClass[class] do
				if aura:UnitHasDebuff(unit, debuff) then return false end
			end
		end
	end
	if Detox.db.profile.checkAbolish and aura:UnitHasBuff(unit, BS["Abolish Poison"]) then return false end
	if Detox.db.profile.checkAbolish and aura:UnitHasBuff(unit, BS["Abolish Disease"]) then return false end
	return true
end


function Detox:CureUnit(unit)
	if not self:UnitCurable(unit) then return false end
	local cleaned = false
	local skip
	local type
	-- check if we can clean a magic debuff
	if (spells["magic"] or spells["enemymagic"]) and aura:UnitHasDebuffType(unit, "Magic") then
		type = "Magic"
		-- check for MC
		if spells["enemymagic"] and UnitIsCharmed(unit) and UnitCanAttack("player", unit) then
				cleaned = self:CastCuringSpell(spells["magic"], unit, "Charm", true)
		elseif spells["magic"] and not UnitCanAttack("player", unit) then
				cleaned = self:CastCuringSpell(spells["magic"], unit, "Magic", spells["enemymagic"])
		end	
	end
	-- check if we can clean a curse
	if not cleaned and spells["curse"] and not UnitIsCharmed(unit) and aura:UnitHasDebuffType(unit, "Curse") then
		type = "Curse"
		cleaned = self:CastCuringSpell(spells["curse"], unit, "Curse", false)
	end
	-- check if we can clean poison
	skip = ( Detox.db.profile.checkAbolish and aura:UnitHasBuff(unit, BS["Abolish Poison"]) ) or false
	if not cleaned and ( spells["poison1"] or spells["poison2"] ) and not UnitIsCharmed(unit) and not skip and aura:UnitHasDebuffType(unit, "Poison") then
		type = "Poison"
		if spells["poison2"] and not aura:UnitHasBuff(unit, BS["Abolish Poison"]) then
			cleaned = self:CastCuringSpell(spells["poison2"], unit, "Poison", false)
		else
			cleaned = self:CastCuringSpell(spells["poison1"], unit, "Poison", false)
		end
	end
	-- check if we can clean disease
	skip = ( Detox.db.profile.checkAbolish and aura:UnitHasBuff(unit, BS["Abolish Disease"]) ) or false
	if not cleaned and ( spells["disease1"] or spells["disease2"] ) and not UnitIsCharmed(unit) and not skip and aura:UnitHasDebuffType(unit, "Disease") then
		type = "Disease"
		if spells["disease2"] and not aura:UnitHasBuff(unit, BS["Abolish Disease"]) then
			cleaned = self:CastCuringSpell(spells["disease2"], unit, "Disease", false)
	    else
			cleaned = self:CastCuringSpell(spells["disease1"], unit, "Disease", false)
	    end
	end
	if cleaned then return type else return nil end
end


function Detox:CastCuringSpell(spellID, unit, AfflictionType, ClearCurrentTarget)
	if UnitIsDeadOrGhost("player") then return false end
	if spellID[1] == 0 then return false end
	if spellID[2] ~= BOOKTYPE_PET and not CheckInteractDistance(unit, 4) then return false end
	if ClearCurrentTarget then
		if not UnitIsUnit("target", unit) then ClearTarget() end
	elseif UnitIsFriend("player", "target") and not UnitIsUnit("target", unit) then
		ClearTarget()
	end
	if spellID[2] == BOOKTYPE_PET then TargetUnit(unit) end
	if SpellIsTargeting() then SpellStopTargeting() end
	castTarget = unit
	CastSpell(spellID[1], spellID[2])
	if spellID[2] == BOOKTYPE_PET then
		TargetUnit("playertarget")
	elseif SpellIsTargeting() then
		SpellTargetUnit(unit)
	end
	if SpellIsTargeting() then 
		SpellStopTargeting()
		return false
	else
		return true
	end
end


--[[---------------------------------------------------------------------------------
 Priority
------------------------------------------------------------------------------------]]

function Detox:PriorityAdd()
	if not UnitExists("target") then
		self:Print("Can't add current target to priority list, it doesnt exist.")
		return
	end
	local name = UnitName("target")
	if not RL.roster[name] then
		self:Print("Can't add current target to priority list, it's not in your raid.")
		return
	end
	Detox.db.profile.priorityNames[name] = true
	self:Print(name.." was added to the priority list")
	self:UpdatePriority()
end


function Detox:PriorityDel()
	if not UnitExists("target") then
		self:Print("Can't delete current target from priority list, it doesnt exist.")
		return
	end
	local name = UnitName("target")
	if not RL.roster[name] then
		self:Print("Can't delete current target from priority list, it's not in your raid.")
		return
	end
	Detox.db.profile.priorityNames[name] = false
	self:Print(name.." has been removed from the priority list.")
	self:UpdatePriority()
end


local function sortPri(a,b) 
	return a.pri > b.pri 
end


function Detox:UpdatePriority()
	self:CreateOptionsTable()
	-- update and sort all units. this task has a few steps:
	local datasubtable, index
	for name, u in pairs(RL.roster) do
		-- see if that unit is in our priorityIndex table
		index = priorityIndex[name]
		-- either use the existing subtable or create a new one
		datasubtable = index and priority[index] or compost:Acquire()
		datasubtable.pri = self:GetPriority(name)
		datasubtable.unitid = u.unitid
		if not index then
			-- our subtable still needs a name
			datasubtable.name = name
			-- add that subtable to the priority table
			table.insert(priority, datasubtable)
			priorityIndex[name] = table.getn(priority)  -- maybe not needed, but for safety reasons it can't hurt.
		end
	end
	-- now re-sort the priority table
	table.sort(priority, sortPri)
	-- save the current index for all names
	for k,v in ipairs(priority) do
		priorityIndex[v.name] = k
	end
end


function Detox:CreateOptionsTable()
	-- we need to update the dewdrop menu, so lets clear the old one and inject new stuff:
	for class in options.args.priority.args do
		for unit in options.args.priority.args[class].args do
			compost:Reclaim(options.args.priority.args[class].args[unit])
			options.args.priority.args[class].args[unit] = nil
		end
		compost:Reclaim(options.args.priority.args[class])
		options.args.priority.args[class] = nil
	end
	-- create subgroups
	for name, unit in pairs(RL.roster) do
		local n = name
		local u = unit
		if not options.args.priority.args[u.class] then
			options.args.priority.args[u.class] = compost:Acquire()
			options.args.priority.args[u.class].type = 'group'
			-- convert class name. gah.
			-- we need this as Babble-Class doesnt know capitalized class names, nor does it know "PET"
			local c = strupper(strsub(u.class, 1, 1)) .. strlower(strsub(u.class, 2))
			if c ~= "Pet" then c = BC[c] end
			options.args.priority.args[u.class].name = c
			options.args.priority.args[u.class].desc = c
			options.args.priority.args[u.class].order = 101
			options.args.priority.args[u.class].args = compost:Acquire()
		end
		options.args.priority.args[u.class].args[n] = compost:Acquire()
		options.args.priority.args[u.class].args[n].type = 'toggle'
		options.args.priority.args[u.class].args[n].name = n
		options.args.priority.args[u.class].args[n].desc = 'priorize '..n
		options.args.priority.args[u.class].args[n].order = 101
		options.args.priority.args[u.class].args[n].get = 
			function() return Detox.db.profile.priorityNames[n] end
		options.args.priority.args[u.class].args[n].set = 
			function() 
				Detox.db.profile.priorityNames[n] = not Detox.db.profile.priorityNames[n]
				Detox:UpdatePriority()
			end
	end
end


function Detox:GetPriority(name)
	local playergroup = (RL.roster[UnitName("player")] and RL.roster[UnitName("player")].subgroup) or 1
	local pri
	local grp = RL.roster[name].subgroup
	-- priority range 1000 - 0.
	if     name == UnitName("player") then  pri = 800
	elseif playergroup == grp         then  pri = 700
	elseif playergroup < grp          then  pri = 700 - (grp - playergroup)*10
	else                                    pri = 700 - (grp + 8 - playergroup)*10
	end
	if RL.roster[name].class == "PET" then 
		if self.db.profile.scanPets then pri = pri - 200 else pri = 0 end
	end
	if Detox.db.profile.priorityNames[name] then pri = pri + 200 end  -- unit in priority list
	return pri
end


--[[---------------------------------------------------------------------------------
 Live display and other FuBar stuff
------------------------------------------------------------------------------------]]


function Detox:OnTooltipUpdate()
	local cat = tablet:AddCategory("columns", 2, "text", "", "text2", "", "showWithoutChildren", true, "hideBlankLine", false)
	local lines = 0
	for k,v in ipairs(priority) do
		if v.pri == 0 then break end
		if lines > Detox.db.profile.liveDisplay then break end
		local unit = v.unitid
		if not blacklist[unit] and self:UnitCurable(unit) then
			for debuffname, count, type, texture in aura:DebuffIter(unit) do
				if ( not Detox.db.profile.filter and ( type == "Magic" or type == "Poison" or type == "Disease" or type == "Curse" ) )
				or ( type == "Magic" and spells["magic"] )
				or ( type == "Poison" and ( spells["poison1"] or spells["poison2"] ) )
				or ( type == "Disease" and ( spells["disease1"] or spells["disease2"] ) )
				or ( type == "Curse" and spells["curse"] )
				then
					local r,g,b = self:GetRaidColors(unit)
					if count == 0 then count = 1 end
					lines = lines + 1
					cat:AddLine(
						"text", string.format("%s (%dx)", debuffname, count), 
						"textR", DebuffTypeColor[type].r,
						"textG", DebuffTypeColor[type].g,
						"textB", DebuffTypeColor[type].b,
						"text2", UnitName(unit),
						"text2R", r,
						"text2G", g,
						"text2B", b,
						"hasCheck", true, 
						"checked", true, 
						"checkIcon", texture,
						"func", function(unit) Detox:CureUnit(unit) end,
						"arg1", unit,
						"justify", "LEFT",
						"justify2", "LEFT"
					)
					-- update text
					if lines == 1 then
						self:SetIcon(texture)
						self:SetText(string.format("|cff%02x%02x%02x%s|r", DebuffTypeColor[type].r*255, DebuffTypeColor[type].g*255, DebuffTypeColor[type].b*255, UnitName(unit)))
					end
				end
			end
		end
	end
	if lines == 0 then
		self:SetText("Detox")
		self:SetIcon("Interface\\AddOns\\Detox\\icon.tga")
	end
	-- play sound
	if Detox.db.profile.sound then
		if lines == 0 then
			soundPlayed = false
		elseif not soundPlayed then
			PlaySoundFile("Sound\\interface\\AuctionWindowOpen.wav")
			soundPlayed = true
		end
	end
end


function Detox:OnClick()
	self:Clean()
end


function Detox:GetRaidColors(unit)
	local _,class = UnitClass(unit)
	if RAID_CLASS_COLORS[class] then
		return RAID_CLASS_COLORS[class].r, RAID_CLASS_COLORS[class].g, RAID_CLASS_COLORS[class].b
	else
		return 0.5, 0.5, 0.5
	end
end


--[[

TODO:

- hide tooltip when empty

--]]
