local L = AceLibrary("AceLocale-2.2"):new("OptiTaunt")
OptiTaunt = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceEvent-2.0", "AceDB-2.0")
-- 59515 + 62466
OptiTaunt.TOCversion = GetAddOnMetadata("OptiTaunt", "Version")
OptiTaunt.REVversion = GetAddOnMetadata("OptiTaunt", "X-Build")
OptiTaunt.version = OptiTaunt.TOCversion
OptiTaunt.revision = OptiTaunt.REVversion

CreateFrame("Frame", "OptiTaunt_EventFrame", UIParent)

local function table_contains(table, element)
	for _, value in pairs(table) do
		if value == element then
			return true
		end
	end
	return false
end

function OptiTaunt:round(num, idp)
  local mult = 10^(idp or 0)
  return math.floor(num * mult + 0.5) / mult
end

function OptiTaunt:OnInitialize()
	
	self.playerclass = select(2, UnitClass("player"))
--	_, self.playerclass	= UnitClass("player")
	self.playername = UnitName("Player")
	--[[
	local default_abilitiy_output = {}
	for _,v in pairs(OptiTaunt_PlayerSpellDB[self.playerclass]) do
		for _,w in pairs(v) do
			local name
			if type(w.SpellID) == "number" then
				name = GetSpellInfo(w.SpellID)
			elseif type(w.SpellID) == "table" then
				name = GetSpellInfo(w.SpellID[1])
			else
				name = ""
			end
			if not table_contains(default_abilitiy_output, name) then
				default_abilitiy_output[name] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				}
			end
		end
	end
	OptiTaunt_default_abilitiy_output = default_abilitiy_output
	local default_debuff_output = {}
	for _,v in pairs(OptiTaunt_SpellDB) do
		local w = v
		for _,w in pairs(v) do
			if not table_contains(default_debuff_output, w.SpellName) then
				default_debuff_output[w.SpellName] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				}
			end
		end
	end
	OptiTaunt_default_debuff_output = default_debuff_output
	]]
	local default_spelldb = {}
	for _,w in pairs(OptiTaunt_PlayerSpellDB[self.playerclass]) do
		if not w.SpellID[1] or w.SpellID[1] == nil then print("!!! There is an error in OptiTaunt's SpellDB !!!") return end
		w.SpellName = GetSpellInfo(w.SpellID[1])
		if not w.output or w.output == {} or type(w.ouput) ~= "table" then
			w.output = {
				raid = true,
				say = false,
				whisper = false,
				raidwarning = false,
				channel = {},
			}
		end
		if not w.announce or w.announce == {} or type(w.announce) ~= "table" then
			w.announce = {
				start = true,
				fading = false,
				ending = true,
			}
		end
		if not w.event or w.event == {} or type(w.event) ~= "table" then
			w.event = {
				SPELL_AURA_APPLIED = false,
				SPELL_AURA_REMOVED = false,
				SPELL_CAST_SUCCESS = false,
				SPELL_INTERRUPT = false,
				SPELL_MISSED = false,
				SPELL_SUMMON = false,
				SPELL_UPDATE_COOLDOWN = false,
			}
		end
--		if OptiTaunt.playerclass == "SHAMAN" then
--			table.insert(w.event, PLAYER_TOTEM_UPDATE)
--			w.event.PLAYER_TOTEM_UPDATE = false
--		end
		if w.Category == "Ability" then
			w.event.SPELL_CAST_SUCCESS = true
		elseif w.Category == "Aura" then
			w.event.SPELL_AURA_APPLIED = true
			w.event.SPELL_AURA_REMOVED = true
		elseif w.Category == "CD" then
			w.event.SPELL_UPDATE_COOLDOWN = true
		elseif w.Category == "Trinket" then
			w.event.SPELL_AURA_APPLIED = true
			w.event.SPELL_AURA_REMOVED = true
		elseif w.Category == "Interrupt" then
			w.event.SPELL_INTERRUPT = true
			w.event.SPELL_MISSED = true
		elseif w.Category == "Taunt" then
			w.event.SPELL_MISSED = true
		elseif w.Category == "Timer" then
			w.event.SPELL_CAST_SUCCESS = true
			w.event["  TIMER via duration"] = true
		elseif w.Category == "Totem" then
			w.event.PLAYER_TOTEM_UPDATE = true
		elseif w.Category == "Summon" then
			w.event.SPELL_SUMMON = true
		else
			w.event.SPELL_CAST_SUCCESS = true
		end
		default_spelldb[w.SpellName] = w
		--table.insert(default_spelldb, w)
	end
	OptiTaunt_default_DB = default_spelldb
	
	local default_debuffdb = {}
	for _,w in pairs(OptiTaunt_SpellDB["DEBUFF"]) do
		w.SpellName = GetSpellInfo(w.SpellID[1])
		if not w.output or w.output == {} or type(w.ouput) ~= "table" then
			w.output = {
				raid = true,
				say = false,
				whisper = false,
				raidwarning = false,
				channel = {},
			}
		end
		if not w.announce or w.announce == {} or type(w.announce) ~= "table" then
			w.announce = {
				start = true,
				fading = false,
				ending = true,
			}
		end
		if not w.event or w.event == {} or type(w.event) ~= "table" then
			w.event = {
				SPELL_AURA_APPLIED = false,
				SPELL_AURA_REMOVED = false,
				SPELL_CAST_SUCCESS = false,
				SPELL_INTERRUPT = false,
				SPELL_UPDATE_COOLDOWN = false,
			}
		end
		if w.Category == "Ability" then
			w.event.SPELL_CAST_SUCCESS = true
		elseif w.Category == "Aura" then
			w.event.SPELL_AURA_APPLIED = true
			w.event.SPELL_AURA_REMOVED = true
		elseif w.Category == "Interrupt" then
			w.event.SPELL_INTERRUPT = true
			w.event.SPELL_MISSED = true
		elseif w.Category == "Timer" then
			w.event.SPELL_CAST_SUCCESS = true
			w.event["  TIMER via duration"] = true
		else
			w.event.SPELL_CAST_SUCCESS = true
		end
		default_debuffdb[w.SpellName] = w
		--table.insert(default_debuffdb, w)
	end
	OptiTaunt_default_debuffDB = default_debuffdb
	
	self:RegisterDB("OptiTauntDB", "OptiTauntDBPC")
	self:RegisterDefaults("profile", {
				
	--messages
		message = {
			resist = L["resistmessage"],
			immune = L["immunemessage"],
			ability = L["abilitymessage"],
			abilityend = L["abilityendmessage"],
			abilityontarget = L["abilityontargetmessage"],
			abilityontargetend = L["abilityontargetendmessage"],
			instantcast = L["instantcastmessage"],
			counterspell = L["counterspellmessage"],
			proc = L["procmessage"],
			disarm = L["disarmmessage"],
			disarmend = L["disarmendmessage"],
			debuff = L["debuffmessage"],
			debuffend = L["debuffendmessage"],
			debuffstun = L["debuffstunmessage"],
			debuffmagic = L["debuffmagicmessage"],
			debuffcurse = L["debuffcursemessage"],
			debuffpoison = L["debuffpoisonmessage"],
			debuffdisease = L["debuffdiseasemessage"],
			healreq = L["healreqmessage"],
			pullcount = L["pullcountmessage"],
			pull = L["pullmessage"],
		},
		
	--Abilities
		abilities = default_spelldb,
		
	--Debuffs
		debuffs = default_debuffdb,
		
	--Pull
		other = {
			["PullTimer"] = {
				pulltimerrange = L["pulltimerrange"],
				["output"] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				},
			},
		},
			
	--options
		announce = {
			resist = true,
			immune = true,
			ability = true,
			abilityend = true,
			abilityontarget = true,
			abilityontargetend = true,
			disarm = true,
			disarmend = true,
			debuff = true,
			debuffend = false,
			debuffstun = true,
			debuffmagic = true,
			debuffcurse = true,
			debuffpoison = true,
			debuffdisease = true,
			healreq = true,			
			
		},
		postSpellLink = false,
			
		giveAudioWarning = true,
		aggroCheck = true,
		whisperWarriors = false,
		pvpCheck = true,
		
		minimapIcon = {},
		
	} )
	
	self:RegisterChatCommand(L["Slash-Commands"], self.options)
	self.opt = self.db.profile
	self.OnMenuRequest = self.options
	
	self.tauntsoundfile = "Interface\\AddOns\\OptiTaunt\\Sounds\\combo.ogg"
	self.tauntcount = 0
	self.tauntresistcount = 0
	self.tauntresistpercent = 0
	
	self.lastTotem = {[1]="",[2]="",[3]="",[4]="",}
	
	OptiTaunt:CreateSpellTables()
	
	--[[
	-- Only show class dependent ABILITIES and do some other class depending stuff	
	if (self.playerclass == "DRUID") then
		self.ability_list = { L["Growl"], L["Challenging Roar"], L["Frenzied Regeneration"], L["Survival Instincts"], L["Barkskin"], L["Skull Bash"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.ability_list = { L["Taunt"], L["Shield Wall"], L["Last Stand"], L["Challenging Shout"], L["Enraged Regeneration"], L["Disarm"], L["Pummel"], L["Rallying Cry"], L["Concussion Blow"] }
	elseif (self.playerclass == "PALADIN") then
		self.ability_list = { L["Righteous Defense"], L["Avenger's Shield"], L["Hand of Reckoning"], L["Divine Protection"], L["Ardent Defender"], L["Lay on Hands"], L["Hand of Protection"], L["Rebuke"], L["Ardent Defender"], L["Divine Guardian"], L["Guardian of Ancient Kings"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.ability_list = { L["Dark Command"], L["Death Grip"], L["Vampiric Blood"], L["Icebound Fortitude"], L["Anti-Magic Shell"], L["Pillar of Frost"], L["Dancing Rune Weapon"], L["Strangulate"], L["Mind Freeze"] }
	else
		self.ability_list = { }
	end
	
	-- Only show class dependent DEBUFFS
	if (self.playerclass == "DRUID") then
		self.debuff_list = { L["Delirious Slash"], L["Rune of Blood"], L["Necrotic Strike"], L["Rend Flesh"], L["Banish"], L["Black Brand"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_list = { L["Delirious Slash"], L["Rune of Blood"], L["Necrotic Strike"], L["Rend Flesh"], L["Banish"], L["Black Brand"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_list = { L["Delirious Slash"], L["Rune of Blood"], L["Necrotic Strike"], L["Rend Flesh"], L["Banish"], L["Black Brand"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_list = { L["Delirious Slash"], L["Rune of Blood"], L["Necrotic Strike"], L["Rend Flesh"], L["Banish"], L["Black Brand"] }
	else
		self.debuff_list = { }
	end
	
	-- Only show class dependent DISARMS
	if (self.playerclass == "DRUID") then
		self.debuff_disarm_list = { }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_disarm_list = { L["Disarm"], L["Dismantle"], L["Chimera Shot - Scorpid"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_disarm_list = { L["Disarm"], L["Dismantle"], L["Chimera Shot - Scorpid"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_disarm_list = { L["Disarm"], L["Dismantle"], L["Chimera Shot - Scorpid"] }
	else
		self.debuff_disarm_list = { }
	end
	
	-- Only show class dependent STUN
	if (self.playerclass == "DRUID") then
		self.debuff_stun_list = { L["Blood Sap"], L["Shovelled!"], L["Hoarfrost"], L["Despair Stricken"], L["Kidney Shot"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_stun_list = { L["Blood Sap"], L["Shovelled!"], L["Hoarfrost"], L["Despair Stricken"], L["Kidney Shot"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_stun_list = { L["Blood Sap"], L["Shovelled!"], L["Hoarfrost"], L["Despair Stricken"], L["Kidney Shot"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_stun_list = { L["Blood Sap"], L["Shovelled!"], L["Hoarfrost"], L["Despair Stricken"], L["Kidney Shot"] }
	else
		self.debuff_stun_list = { }
	end
	
	-- Only show class dependent MAGIC debuffs
	if (self.playerclass == "DRUID") then
		self.debuff_magic_list = { L["Polymorph: Spider"], L["Consuming Shadows"], L["Chains of Shadow"], L["Soul Sickness"], L["Impending Despair"], L["Shared Suffering"], L["Chains of Ice"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_magic_list = { L["Polymorph: Spider"], L["Consuming Shadows"], L["Chains of Shadow"], L["Soul Sickness"], L["Impending Despair"], L["Shared Suffering"], L["Chains of Ice"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_magic_list = { L["Polymorph: Spider"], L["Consuming Shadows"], L["Chains of Shadow"], L["Soul Sickness"], L["Impending Despair"], L["Shared Suffering"], L["Chains of Ice"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_magic_list = { L["Polymorph: Spider"], L["Consuming Shadows"], L["Chains of Shadow"], L["Soul Sickness"], L["Impending Despair"], L["Shared Suffering"], L["Chains of Ice"] }
	else
		self.debuff_magic_list = { }
	end
	
	-- Only show class dependent CURSE
	if (self.playerclass == "DRUID") then
		self.debuff_curse_list = { L["Veil of Shadow"], L["Curse of Doom"], L["Cursed Arrow"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_curse_list = { L["Veil of Shadow"], L["Curse of Doom"], L["Cursed Arrow"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_curse_list = { L["Veil of Shadow"], L["Curse of Doom"], L["Cursed Arrow"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_curse_list = { L["Veil of Shadow"], L["Curse of Doom"], L["Cursed Arrow"] }
	else
		self.debuff_curse_list = { }
	end
	
	-- Only show class dependent POISON
	if (self.playerclass == "DRUID") then
		self.debuff_poison_list = { L["Blinding Dirt"], L["Deadly Poison"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_poison_list = { L["Blinding Dirt"], L["Deadly Poison"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_poison_list = { L["Blinding Dirt"], L["Deadly Poison"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_poison_list = { L["Blinding Dirt"], L["Deadly Poison"] }
	else
		self.debuff_poison_list = { }
	end
	
	-- Only show class dependent DISEASE
	if (self.playerclass == "DRUID") then
		self.debuff_disease_list = { L["Leeching Rot"], L["Blight"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.debuff_disease_list = { L["Leeching Rot"], L["Blight"] }
	elseif (self.playerclass == "PALADIN") then
		self.debuff_disease_list = { L["Leeching Rot"], L["Blight"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.debuff_disease_list = { L["Leeching Rot"], L["Blight"] }
	else
		self.debuff_disease_list = { }
	end
	
	-- Only show class dependent HEAL REQUIRED
	if (self.playerclass == "DRUID") then
		self.healreq_list = { L["Glacial Strike"] }
	elseif (self.playerclass == "WARRIOR") then 
		self.healreq_list = { L["Glacial Strike"] }
	elseif (self.playerclass == "PALADIN") then
		self.healreq_list = { L["Glacial Strike"] }
	elseif (self.playerclass == "DEATHKNIGHT") then
		self.healreq_list = { L["Glacial Strike"] }
	else
		self.healreq_list = { }
	end
	]]
	-- PullTimer output
	self.PullTimer_list = { L["Output"] }
	
	-- Add custom channels
	--OptiTaunt:CreateOutputSettings()
	
	OptiTaunt:CreatePullTimerSettings(self.PullTimer_list)
	
	OptiTaunt:LDB_OnEnable()
	
	
	-- set to 'true' for debugging messages / 'false' for no debugging messages
	self.isDebug = false

	DEFAULT_CHAT_FRAME:AddMessage("|cffff6d00OptiTaunt|r |cff8080ff"..OptiTaunt.TOCversion.." "..OptiTaunt.REVversion.."|r by "..OptiTaunt.author.." loaded.");

end

function OptiTaunt:CreateSpellTables()
	
	self.ability_list = {}
	self.trinket_list = {}
	self.ability_whisper = {}
	
--	for _,v in pairs(OptiTaunt_PlayerSpellDB[self.playerclass]) do
	for _,w in pairs(OptiTaunt.db.profile.abilities) do
	--	for _,w in pairs(v) do
			local spellName
			if type(w.SpellID) == "number" then
				spellName = GetSpellInfo(w.SpellID)
			elseif type(w.SpellID) == "table" then
				spellName = GetSpellInfo(w.SpellID[1])
			else
				spellName = "?"
			end
			--print(spellName)
			if w.Category ~= "Trinket" and not table_contains(self.ability_list, spellName) then
				table.insert(self.ability_list, spellName)
			elseif w.Category == "Trinket" and not table_contains(self.ability_list, spellName) then
				table.insert(self.trinket_list, spellName)
			end
			if (w.Whisper ~= nil or w.Category == "Taunt") and not table_contains(self.ability_whisper, spellName) then
				table.insert(self.ability_whisper, spellName)				
			end
	--	end
	end
	
	
	self.debuff_list = {}
	self.debuff_disarm_list = { }
	self.debuff_stun_list = { }
	self.debuff_magic_list = { }
	self.debuff_curse_list = { }
	self.debuff_poison_list = { }
	self.debuff_disease_list = { }
	self.healreq_list = { }
	
	for _,v in pairs(OptiTaunt_SpellDB) do
		for _,w in pairs(v) do
			local spellName
			if type(w.SpellID) == "table" then
				spellName = GetSpellInfo(w.SpellID[1])
			elseif type(w.SpellID) == "number" then
				spellName = GetSpellInfo(w.SpellID)
			end
			if w.Category == "Debuff" then
				if not table_contains(self.debuff_list, spellName) then
					table.insert(self.debuff_list, spellName)
				end
			elseif w.Category == "Disarm" then
				if self.playerclass == "DRUID" then
					self.debuff_disarm_list = { }
				else
					if not table_contains(self.debuff_disarm_list, spellName) then
						table.insert(self.debuff_disarm_list, spellName)
					end
				end
			elseif w.Category == "Stun" then
				if not table_contains(self.debuff_stun_list, spellName) then
					table.insert(self.debuff_stun_list, spellName)
				end
			elseif w.Category == "Magic" then
				if not table_contains(self.debuff_magic_list, spellName) then
					table.insert(self.debuff_magic_list, spellName)
				end
			elseif w.Category == "Curse" then
				if not table_contains(self.debuff_curse_list, spellName) then
					table.insert(self.debuff_curse_list, spellName)
				end
			elseif w.Category == "Poison" then
				if not table_contains(self.debuff_poison_list, spellName) then
					table.insert(self.debuff_poison_list, spellName)
				end
			elseif w.Category == "Disease" then
				if not table_contains(self.debuff_disease_list, spellName) then
					table.insert(self.debuff_disease_list, spellName)
				end
			elseif w.Category == "Healreq" then
				if not table_contains(self.healreq_list, spellName) then
					table.insert(self.healreq_list, spellName)
				end
			end
		end
	end
	
	OptiTaunt:CreateOutputSettings()
	
end

function OptiTaunt:OnProfileEnable()
	self.opt = self.db.profile
end

function OptiTaunt:OnEnable()
    self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	self:RegisterEvent("CHANNEL_UI_UPDATE")
	self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
	
	OptiTaunt.frame = CreateFrame("Frame", "OptiTaunt_EventFrame", UIParent)
	local frame = _G["OptiTaunt_EventFrame"]
	frame:RegisterEvent("SPELL_UPDATE_COOLDOWN")
	frame:RegisterEvent("PLAYER_TOTEM_UPDATE")
	frame:SetScript("OnEvent",
		function(self, event, ...)
			local srcGUID, srcName = UnitGUID("player"), UnitName("player")
			OptiTaunt:PlayerSpellDB_Event(event, srcGUID, srcName, nil, nil, ...)
		end)
end

function OptiTaunt:OnDisable()
    self:UnregisterAllEvents()
	
	--OptiTaunt.frame = CreateFrame("Frame", "OptiTaunt_EventFrame", UIParent)
	local frame = _G["OptiTaunt_EventFrame"]
	frame:UnregisterAllEvents()
	frame:SetScript("OnEvent", nil)
end

-- debug function (must be activted under function 'OnInitialize()' - set 'self.isDebug = false' to 'self.isDebug = true')
function OptiTaunt:dbg(msg)
	if ( self.isDebug ) then
		OptiTaunt:Print(msg)
	end
end


--[[
---- Category (abilities) menu creation -----------------------------------------------------------
]]

function OptiTaunt:CreateOutputSettings()
	OptiTaunt:CreateAbilityOutputSettings(self.ability_list, "ability_options")
	OptiTaunt:CreateAbilityOutputSettings(self.trinket_list, "trinket_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_list, "debuff_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_disarm_list, "disarm_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_stun_list, "debuff_stun_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_magic_list, "debuff_magic_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_curse_list, "debuff_curse_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_poison_list, "debuff_poison_options")
	OptiTaunt:CreateDebuffOutputSettings(self.debuff_disease_list, "debuff_disease_options")
	OptiTaunt:CreateDebuffOutputSettings(self.healreq_list, "healreq_options")
end

-- Ability list creation, content is provided by 'OnInitialize()'
function OptiTaunt:CreateAbilityOutputSettings(list, optstring)

	--something might have changed, so rebuild ability menu from scratch just to be safe
	local ability, activate
	self.channels = {GetChannelList()}
	--self.options.args.ability_options.args
	OptiTaunt.options.args[optstring].args = {}
				
	--rebuild abilites
	for ability=1,table.getn(list) do
		OptiTaunt.options.args[optstring].args[ability] = {}
		OptiTaunt.options.args[optstring].args[ability] = {
			type  = 'group',
			name  = list[ability],
			desc  = list[ability],
			args  = {},
		}
		OptiTaunt.options.args[optstring].args[ability].args.spellname = {
			type = "text",
			order = 1,
			name = "Spell Name",
			desc = "The name/warning that should be posted (does not work if >Post SpellLink< is enabled.)",
			get = function () return OptiTaunt.db.profile.abilities[list[ability]].SpellName end,
			set = function (v) if v==nil or v=="" or v==" " then OptiTaunt.db.profile.abilities[list[ability]].SpellName = GetSpellInfo(OptiTaunt.db.profile.abilities[list[ability]].SpellID[1]) else OptiTaunt.db.profile.abilities[list[ability]].SpellName = v end end,
			usage = "",
		}
		OptiTaunt.options.args[optstring].args[ability].args.spellid = {
			type = "text",
			order = 1.1,
			name = "Spell ID",
			desc = "The Spell IDs that trigger this output.",
			get = function () local spellids for i=1,table.getn(OptiTaunt.db.profile.abilities[list[ability]].SpellID) do  if i==1 then spellids=OptiTaunt.db.profile.abilities[list[ability]].SpellID[1] else spellids=spellids..";"..OptiTaunt.db.profile.abilities[list[ability]].SpellID[i] end end return spellids end,
			set = function (v) OptiTaunt.db.profile.abilities[list[ability]].SpellID = {} for id in string.gmatch(v, "[^;][%w%s%p:][^;]+") do if not table_contains(OptiTaunt.db.profile.abilities[list[ability]].SpellID, tonumber(id)) then table.insert(OptiTaunt.db.profile.abilities[list[ability]].SpellID, tonumber(id)) end end end,
			usage = "",
		}
		OptiTaunt.options.args[optstring].args[ability].args.duration = {
			type = "text",
			order = 1.2,
			name = "Spell Duration",
			desc = "The duration of the spell.\n(not needed for spells that have a SPELL_AURA effect)",
			get = function () return OptiTaunt.db.profile.abilities[list[ability]].Duration end,
			set = function (v) if v==nil or tonumber(v)==nil or v=="" or v==" " then v = 0 end OptiTaunt.db.profile.abilities[list[ability]].Duration = tonumber(v) end,
			usage = "",
		}
		OptiTaunt.options.args[optstring].args[ability].args.cooldown = {
			type = "text",
			order = 1.3,
			name = "Spell Cooldown",
			desc = "The cooldown of the spell. (not needed)",
			get = function () return OptiTaunt.db.profile.abilities[list[ability]].CD end,
			set = function (v) if v==nil or tonumber(v)==nil or v=="" or v==" " then v = 0 end OptiTaunt.db.profile.abilities[list[ability]].CD = tonumber(v) end,
			usage = "",
		}
		OptiTaunt.options.args[optstring].args[ability].args.category = {}
		OptiTaunt.options.args[optstring].args[ability].args.category = {
			type = 'text',
			order = 2,
			name = "Category",
			desc = "Select which Text Warnings to display",
			validate = { "Ability" , "Aura" , "CD" , "Interrupt" , "Summon" , "Taunt" , "Timer" , "Totem" , "Trinket" },
			get = function() return OptiTaunt.db.profile.abilities[list[ability]].Category end,
			set = "SetCategory",
			passValue = list[ability],
		}
		OptiTaunt.options.args[optstring].args[ability].args.event = {}
		OptiTaunt.options.args[optstring].args[ability].args.event = {
			type  = 'group',
			order = 2.1,
			name  = EVENTS_LABEL,
			desc  = CALENDAR_EVENT_PICKER_TITLE,
			args  = {},
		}
--[[		OptiTaunt.options.args[optstring].args[ability].args.announce = {}
		OptiTaunt.options.args[optstring].args[ability].args.announce = {
			type  = 'group',
			order = 3,
			name  = CHAT_ANNOUNCE,
			desc  = "Which time event should be posted?",
			args  = {},
		}]]
		OptiTaunt.options.args[optstring].args[ability].args.output = {}
		OptiTaunt.options.args[optstring].args[ability].args.output = {
			type  = 'group',
			order = 4,
			name  = L["Output"],
			desc  = L["Output"],
			args  = {},
		}
	end

--Announce
	--START..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS, KEY_END..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS, COMBAT_TEXT_SHOW_AURA_FADE_TEXT..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURA_FADE
	
--Output
	--rebuild ability options
	for ability=1,table.getn(list) do
		
		--raid/group
		OptiTaunt.options.args[optstring].args[ability].args.output.args[1] = {
		type = 'toggle',
		order = 2,
		name = L["opt_name_raidparty"],
		desc = L["opt_desc_raidparty"],
		get = function()
				return self.db.profile.abilities[list[ability]].output.raid
			  end,
		set = function()
			    self.db.profile.abilities[list[ability]].output.raid = 
				not self.db.profile.abilities[list[ability]].output.raid
		      end,
		}
		
		--say
		OptiTaunt.options.args[optstring].args[ability].args.output.args[2] = {
		type = 'toggle',
		order = 1,
		name = L["opt_name_say"],
		desc = L["opt_desc_say"],
		get = function()
				return self.db.profile.abilities[list[ability]].output.say
			  end,
		set = function()
			    self.db.profile.abilities[list[ability]].output.say = 
				not self.db.profile.abilities[list[ability]].output.say
		      end,
		}
		
		--yell
		OptiTaunt.options.args[optstring].args[ability].args.output.args[3] = {
		type = 'toggle',
		order = 2.1,
		name = L["opt_name_yell"],
		desc = L["opt_desc_yell"],
		get = function()
				return self.db.profile.abilities[list[ability]].output.yell
			  end,
		set = function()
			    self.db.profile.abilities[list[ability]].output.yell = 
				not self.db.profile.abilities[list[ability]].output.yell
		      end,
		}
		
		--whisper (disabled for activation announcements)
		--negate for all
		OptiTaunt.options.args[optstring].args[ability].args.output.args[5] = nil
		--setup for all spells that should be whispered
		for _,v in pairs(self.ability_whisper) do
			if v ~= nil and v == list[ability] then
				OptiTaunt.options.args[optstring].args[ability].args.output.args[5] = {
					type = 'toggle',
					order = 4,
					name = L["opt_name_autowhisper"],
					desc = L["opt_desc_autowhisper"],
					get = function()
							return self.db.profile.abilities[list[ability]].output.whisper
						  end,
					set = function()
							self.db.profile.abilities[list[ability]].output.whisper = 
							not self.db.profile.abilities[list[ability]].output.whisper
						  end,
				}
			end
		end
		
		
		--Raidwarning
		OptiTaunt.options.args[optstring].args[ability].args.output.args[4] = {
		type = 'toggle',
		order = 3,
		name = L["opt_name_raidwarning"],
		desc = L["opt_desc_raidwarning"],
		get = function()
				return self.db.profile.abilities[list[ability]].output.raidwarning
			  end,
		set = function()
			    self.db.profile.abilities[list[ability]].output.raidwarning = 
				not self.db.profile.abilities[list[ability]].output.raidwarning
		      end,
		}
		
		--custom channels
		for i=6,table.getn(self.channels)/2+5 do
			OptiTaunt.options.args[optstring].args[ability].args.output.args[i] = {
				type = 'toggle',
				name = self.channels[(i-5)*2],
				desc = self.channels[(i-5)*2],
			
				get = 	function()
							local rc = false
							for j=1,table.getn(self.db.profile.abilities[list[ability]].output.channel) do
								if self.db.profile.abilities[list[ability]].output.channel[j] == self.channels[(i-5)*2] then 
									rc = true 
								end
							end
							return rc
						end,
				set = 	function()
							local deletion = false
							for j=1,table.getn(self.db.profile.abilities[list[ability]].output.channel) do
								if self.db.profile.abilities[list[ability]].output.channel[j] == self.channels[(i-5)*2] then
									table.remove(self.db.profile.abilities[list[ability]].output.channel, j)
									deletion=true
								end
							end
							if not deletion then
								table.insert(self.db.profile.abilities[list[ability]].output.channel, self.channels[(i-5)*2])
							end
						end,
				}
		end
	end
--Events
	local eventlist = {
		"SPELL_AURA_APPLIED",
		"SPELL_AURA_REMOVED",
		"SPELL_CAST_SUCCESS",
		"  TIMER via duration",
		"SPELL_INTERRUPT",
		"SPELL_MISSED",
		"SPELL_SUMMON",
		"SPELL_UPDATE_COOLDOWN",
		"PLAYER_TOTEM_UPDATE",
	}
	for ability=1,table.getn(list) do
		for eventname=1,table.getn(eventlist) do
			OptiTaunt.options.args[optstring].args[ability].args.event.args[eventname] = {
			type = 'toggle',
			order = eventname,
			name = eventlist[eventname],
			desc = eventlist[eventname],
			get = function()
					return self.db.profile.abilities[list[ability]].event[eventlist[eventname]]
				  end,
			set = function()
					self.db.profile.abilities[list[ability]].event[eventlist[eventname]] = 
					not self.db.profile.abilities[list[ability]].event[eventlist[eventname]]
				  end,
			}
		end
	end
	
end

-- Debuffs
function OptiTaunt:CreateDebuffOutputSettings(list, optstring)

	--something might have changed, so rebuild debuff menu from scratch just to be safe
	local debuff, activate
	self.channels = {GetChannelList()}
	--self.options.args.debuff_options.args
	OptiTaunt.options.args.debuff_menu.args[optstring].args = {}
	OptiTaunt.options.args.debuff_menu.args[optstring].args = {}
				
	--rebuild abilites
	for debuff=1,table.getn(list) do
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff] = {}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff] = {
			type  = 'group',
			name  = list[debuff],
			desc  = list[debuff],
			args  = {},
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.spellname = {
			type = "text",
			order = 1,
			name = "Spell Name",
			desc = "The name/warning that should be posted (does not work if >Post SpellLink< is enabled.)",
			get = function () return OptiTaunt.db.profile.debuffs[list[debuff]].SpellName end,
			set = function (v) if v==nil or v=="" or v==" " then OptiTaunt.db.profile.debuffs[list[debuff]].SpellName = GetSpellInfo(OptiTaunt.db.profile.debuffs[list[debuff]].SpellID[1]) else OptiTaunt.db.profile.debuffs[list[debuff]].SpellName = v end end,
			usage = "",
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.spellid = {
			type = "text",
			order = 1.1,
			name = "Spell ID",
			desc = "The Spell IDs that trigger this output.",
			get = function () local spellids for i=1,table.getn(OptiTaunt.db.profile.debuffs[list[debuff]].SpellID) do  if i==1 then spellids=OptiTaunt.db.profile.debuffs[list[debuff]].SpellID[1] else spellids=spellids..";"..OptiTaunt.db.profile.debuffs[list[debuff]].SpellID[i] end end return spellids end,
			set = function (v) OptiTaunt.db.profile.debuffs[list[debuff]].SpellID = {} for id in string.gmatch(v, "[^;][%w%s%p:][^;]+") do if not table_contains(OptiTaunt.db.profile.debuffs[list[debuff]].SpellID, tonumber(id)) then table.insert(OptiTaunt.db.profile.debuffs[list[debuff]].SpellID, tonumber(id)) end end end,
			usage = "",
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.duration = {
			type = "text",
			order = 1.2,
			name = "Spell Duration",
			desc = "The duration of the spell.\n(not needed for spells that have a SPELL_AURA effect)",
			get = function () return OptiTaunt.db.profile.debuffs[list[debuff]].Duration end,
			set = function (v) if v==nil or tonumber(v)==nil or v=="" or v==" " then v = 0 end OptiTaunt.db.profile.debuffs[list[debuff]].Duration = tonumber(v) end,
			usage = "",
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.cooldown = {
			type = "text",
			order = 1.3,
			name = "Spell Cooldown",
			desc = "The cooldown of the spell. (not needed)",
			get = function () return OptiTaunt.db.profile.debuffs[list[debuff]].CD end,
			set = function (v) if v==nil or tonumber(v)==nil or v=="" or v==" " then v = 0 end OptiTaunt.db.profile.debuffs[list[debuff]].CD = tonumber(v) end,
			usage = "",
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.category = {}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.category = {
			type = 'text',
			order = 2,
			name = "Category",
			desc = "Select which Text Warnings to display",
			validate = { "debuff" , "Aura" , "CD" , "Interrupt" , "Summon" , "Taunt" , "Timer" , "Totem" , "Trinket" },
			get = function() return OptiTaunt.db.profile.debuffs[list[debuff]].Category end,
			set = "SetCategory",
			passValue = list[debuff],
		}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.event = {}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.event = {
			type  = 'group',
			order = 2.1,
			name  = EVENTS_LABEL,
			desc  = CALENDAR_EVENT_PICKER_TITLE,
			args  = {},
		}
--[[		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.announce = {}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.announce = {
			type  = 'group',
			order = 3,
			name  = CHAT_ANNOUNCE,
			desc  = "Which time event should be posted?",
			args  = {},
		}]]
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output = {}
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output = {
			type  = 'group',
			order = 4,
			name  = L["Output"],
			desc  = L["Output"],
			args  = {},
		}
	end

--Announce
	--START..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS, KEY_END..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURAS, COMBAT_TEXT_SHOW_AURA_FADE_TEXT..OPTION_TOOLTIP_COMBAT_TEXT_SHOW_AURA_FADE
	
--Output
	--rebuild debuff options
	for debuff=1,table.getn(list) do
		
		--raid/group
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[1] = {
		type = 'toggle',
		order = 2,
		name = L["opt_name_raidparty"],
		desc = L["opt_desc_raidparty"],
		get = function()
				return OptiTaunt.db.profile.debuffs[list[debuff]].output.raid
			  end,
		set = function()
			    OptiTaunt.db.profile.debuffs[list[debuff]].output.raid = 
				not OptiTaunt.db.profile.debuffs[list[debuff]].output.raid
		      end,
		}
		
		--say
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[2] = {
		type = 'toggle',
		order = 1,
		name = L["opt_name_say"],
		desc = L["opt_desc_say"],
		get = function()
				return OptiTaunt.db.profile.debuffs[list[debuff]].output.say
			  end,
		set = function()
			    OptiTaunt.db.profile.debuffs[list[debuff]].output.say = 
				not OptiTaunt.db.profile.debuffs[list[debuff]].output.say
		      end,
		}
		
		--yell
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[3] = {
		type = 'toggle',
		order = 2.1,
		name = L["opt_name_yell"],
		desc = L["opt_desc_yell"],
		get = function()
				return OptiTaunt.db.profile.debuffs[list[debuff]].output.yell
			  end,
		set = function()
			    OptiTaunt.db.profile.debuffs[list[debuff]].output.yell = 
				not OptiTaunt.db.profile.debuffs[list[debuff]].output.yell
		      end,
		}
		
		--whisper (disabled for activation announcements)
		--negate for all
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[5] = nil
		--setup for all spells that should be whispered
		--[=[
		for _,v in pairs(self.debuff_whisper) do
			if v ~= nil and v == list[debuff] then
				OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[5] = {
					type = 'toggle',
					order = 4,
					name = L["opt_name_autowhisper"],
					desc = L["opt_desc_autowhisper"],
					get = function()
							return OptiTaunt.db.profile.debuffs[list[debuff]].output.whisper
						  end,
					set = function()
							OptiTaunt.db.profile.debuffs[list[debuff]].output.whisper = 
							not OptiTaunt.db.profile.debuffs[list[debuff]].output.whisper
						  end,
				}
			end
		end
		]=]
		
		--Raidwarning
		OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[4] = {
		type = 'toggle',
		order = 3,
		name = L["opt_name_raidwarning"],
		desc = L["opt_desc_raidwarning"],
		get = function()
				return OptiTaunt.db.profile.debuffs[list[debuff]].output.raidwarning
			  end,
		set = function()
			    OptiTaunt.db.profile.debuffs[list[debuff]].output.raidwarning = 
				not OptiTaunt.db.profile.debuffs[list[debuff]].output.raidwarning
		      end,
		}
		
		--custom channels
		for i=6,table.getn(self.channels)/2+5 do
			OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.output.args[i] = {
				type = 'toggle',
				name = self.channels[(i-5)*2],
				desc = self.channels[(i-5)*2],
			
				get = 	function()
							local rc = false
							for j=1,table.getn(OptiTaunt.db.profile.debuffs[list[debuff]].output.channel) do
								if OptiTaunt.db.profile.debuffs[list[debuff]].output.channel[j] == self.channels[(i-5)*2] then 
									rc = true 
								end
							end
							return rc
						end,
				set = 	function()
							local deletion = false
							for j=1,table.getn(OptiTaunt.db.profile.debuffs[list[debuff]].output.channel) do
								if OptiTaunt.db.profile.debuffs[list[debuff]].output.channel[j] == self.channels[(i-5)*2] then
									table.remove(OptiTaunt.db.profile.debuffs[list[debuff]].output.channel, j)
									deletion=true
								end
							end
							if not deletion then
								table.insert(OptiTaunt.db.profile.debuffs[list[debuff]].output.channel, self.channels[(i-5)*2])
							end
						end,
				}
		end
	end
--Events
	local eventlist = {
		"SPELL_AURA_APPLIED",
		"SPELL_AURA_REMOVED",
		"SPELL_CAST_SUCCESS",
		"  TIMER via duration",
		"SPELL_INTERRUPT",
		"SPELL_MISSED",
		"SPELL_SUMMON",
		"SPELL_UPDATE_COOLDOWN",
		"PLAYER_TOTEM_UPDATE",
	}
	for debuff=1,table.getn(list) do
		for eventname=1,table.getn(eventlist) do
			OptiTaunt.options.args.debuff_menu.args[optstring].args[debuff].args.event.args[eventname] = {
			type = 'toggle',
			order = eventname,
			name = eventlist[eventname],
			desc = eventlist[eventname],
			get = function()
					return OptiTaunt.db.profile.debuffs[list[debuff]].event[eventlist[eventname]]
				  end,
			set = function()
					OptiTaunt.db.profile.debuffs[list[debuff]].event[eventlist[eventname]] = 
					not OptiTaunt.db.profile.debuffs[list[debuff]].event[eventlist[eventname]]
				  end,
			}
		end
	end
	
end

-- PullTimer output creation, content is provided by 'OnInitialize()'
function OptiTaunt:CreatePullTimerSettings(list)

	--
	local value, activate
	self.channels = {GetChannelList()}
	--self.options.args.pulltimeroptions.args.output.args = {}
				
	--rebuild values
	for value=1,table.getn(list) do
		self.options.args.pulltimeroptions.args[value] = {}
		self.options.args.pulltimeroptions.args[value] = {
			type  = 'group',
			order = 1,
			name  = list[value],
			desc  = list[value],
			args  = {},
		}
	end
		
	--rebuild value options
	for value=1,table.getn(list) do
		
		--raid/group
		self.options.args.pulltimeroptions.args[value].args[1] = {
		type = 'toggle',
		order = 2,
		name = L["opt_name_raidparty"],
		desc = L["opt_desc_raidparty"],
		get = function()
				return OptiTaunt.db.profile.other.PullTimer.output.raid
			  end,
		set = function()
			    OptiTaunt.db.profile.other.PullTimer.output.raid = 
				not OptiTaunt.db.profile.other.PullTimer.output.raid
		      end,
		}
		
		--say
		self.options.args.pulltimeroptions.args[value].args[2] = {
		type = 'toggle',
		order = 1,
		name = L["opt_name_say"],
		desc = L["opt_desc_say"],
		get = function()
				return OptiTaunt.db.profile.other.PullTimer.output.say
			  end,
		set = function()
			    OptiTaunt.db.profile.other.PullTimer.output.say = 
				not OptiTaunt.db.profile.other.PullTimer.output.say
		      end,
		}
		
		--yell
		self.options.args.pulltimeroptions.args[value].args[3] = {
		type = 'toggle',
		order = 2.1,
		name = L["opt_name_yell"],
		desc = L["opt_desc_yell"],
		get = function()
				return OptiTaunt.db.profile.other.PullTimer.output.yell
			  end,
		set = function()
			    OptiTaunt.db.profile.other.PullTimer.output.yell = 
				not OptiTaunt.db.profile.other.PullTimer.output.yell
		      end,
		}
		
		--Whisper
		self.options.args.pulltimeroptions.args[value].args[4] = nil
		
		--Raidwarning
		self.options.args.pulltimeroptions.args[value].args[5] = {
		type = 'toggle',
		order = 3,
		name = L["opt_name_raidwarning"],
		desc = L["opt_desc_raidwarning"],
		get = function()
				return OptiTaunt.db.profile.other.PullTimer.output.raidwarning
			  end,
		set = function()
			    OptiTaunt.db.profile.other.PullTimer.output.raidwarning = 
				not OptiTaunt.db.profile.other.PullTimer.output.raidwarning
		      end,
		}
		
		--custom channels
		for i=6,table.getn(self.channels)/2+5 do
			self.options.args.pulltimeroptions.args[value].args[i] = {
				type = 'toggle',
				name = self.channels[(i-5)*2],
				desc = self.channels[(i-5)*2],
			
				get = 	function()
							local rc = false
							for j=1,table.getn(OptiTaunt.db.profile.other.PullTimer.output.channel) do
								if OptiTaunt.db.profile.other.PullTimer.output.channel[j] == self.channels[(i-5)*2] then 
									rc = true 
								end
							end
							return rc
						end,
				set = 	function()
							local deletion = false
							for j=1,table.getn(self.db.profile.outputsettings[L:GetReverseTranslation(list[value])].channel) do
								if OptiTaunt.db.profile.other.PullTimer.output.channel[j] == self.channels[(i-5)*2] then
									table.remove(OptiTaunt.db.profile.other.PullTimer.output.channel, j)
									deletion=true
								end
							end
							if not deletion then
								table.insert(OptiTaunt.db.profile.other.PullTimer.output.channel, self.channels[(i-5)*2])
							end
						end,
				}
		end
	end
end


-- Tooltip generation (taunt statistics)
function OptiTaunt:OnTooltipUpdate()
	
	if (self.tauntresistcount == 0) then
		self.tauntresistpercent = 0
	else
		self.tauntresistpercent = math.floor((self.tauntresistcount / self.tauntcount) * 10000)/100
	end
	--[[	
	tablet:SetHint(L["Tooltip"])
	
	local cat = tablet:AddCategory(
       'text', L["Statistics"],
       'columns', 2,
       'child_textR', 1,
       'child_textG', 1,
       'child_textB', 0,
       'child_textR2', 1,
       'child_textG2', 1,
       'child_textB2', 1
   )
   
   cat:AddLine(
       'text', L["Taunts"],
       'text2', self.tauntcount
   )
   
   cat:AddLine(
       'text', L["Resisted"],
       'text2', self.tauntresistcount.." ("..self.tauntresistpercent.."%)"
   ) 
   ]]
   
	GameTooltip:AddLine(OptiTaunt.options.args.title.name)
	GameTooltip:AddLine(L["Statistics"])
	GameTooltip:AddDoubleLine(L["Taunts"], self.tauntcount, 1,1,0, 1,1,1);
	GameTooltip:AddDoubleLine(L["Resisted"], self.tauntresistcount.." ("..self.tauntresistpercent.."%)", 1,1,0, 1,1,1);
	GameTooltip:AddLine(L["Tooltip"],0,0.9,0,1)
	GameTooltip:Show()
    
end


function OptiTaunt:CHANNEL_UI_UPDATE()
	
	OptiTaunt:dbg("---Entering OptiTaunt:CHANNEL_UI_UPDATE---")
	OptiTaunt:CreateOutputSettings()

end

local PvP_Zones = {
	--Battleground
	"Alterac Valley",
	"Arathi Basin",
	"Battle for Gilneas",
	"Eye of the Storm",
	"Isle of Conquest",
	"Strand of the Ancients",
	"Twin Peaks",
	"Warsong Gulch",
	--Open World
	"Tol Barad",
	"Tol Barad Peninsula",
	"Wintergrasp",
	--Arena
	"Blade's Edge Arena",
	"Dalaran Arena",
	"Nagrand Arena",
	"Ruins of Lordaeron",
	"The Ring of Valor",	
}
OptiTaunt_PvPLockdown = 0;
function OptiTaunt:ZONE_CHANGED_NEW_AREA()
	--print("OptiTaunt:ZONE_CHANGED_NEW_AREA")	--Debug
	local B = LibStub("LibBabble-Zone-3.0")
	local BR = B:GetReverseLookupTable()
	local zone = GetZoneText();
	OptiTaunt_PvPLockdown = 0;
	for k,v in pairs(PvP_Zones) do
		--assert(BR[zone] == v, "Unbekannte Zone: "..zone)
		if BR[zone] == v then
			--print("You are in "..zone.." / "..v)	--Debug
			OptiTaunt_PvPLockdown = 1;
		end
	end
	--assert(BR[zone] == "Tol Barad Peninsula", "Unbekannte Zone: "..zone)	--Debug
	--assert(BR["Some localized word that doesn't exist"] == nil, "NIL Zone")	--Debug
end

OptiTaunt_EventDebug = 0

function OptiTaunt:COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventtype, hideCaster, srcGUID, srcName, srcFlags, srcRaidFlags, dstGUID, dstName, dstFlags, dstRaidFlags, ...)

	-- see http://www.wowwiki.com/API_COMBAT_LOG_EVENT#Examples
	
	--debug stuff
	OptiTaunt:dbg("---Entering OptiTaunt:COMBAT_LOG_EVENT_UNFILTERED 4.2 ---")
	--[[local message = format("%s> %s, %s, %s, %s, %s, %s, %s",
	                       timestamp, eventtype, srcGUID, srcName or "nil", srcFlags or "nil", dstGUID, dstName or "nil", dstFlags or "nil");
	for i = 9, select("#", ...) do
		message = message..", "..(select(i, ...) or "nil");
	end
	OptiTaunt:dbg(message)]]
	
	if ( ( OptiTaunt_PvPLockdown == 1 ) and ( self.db.profile.pvpCheck == false ) ) or ( OptiTaunt_PvPLockdown == 0 ) then
		if (srcGUID == UnitGUID("player")) then
			if "SPELL_AURA_APPLIED"
			or "SPELL_AURA_REMOVED"
			or "SPELL_CAST_SUCCESS"
			or "SPELL_INTERRUPT"
			or "SPELL_MISSED"
			or "SPELL_SUMMON"
			or "SPELL_UPDATE_COOLDOWN"
			or "PLAYER_TOTEM_UPDATE"
			then
				OptiTaunt:PlayerSpellDB_Event(eventtype, srcGUID, srcName, dstGUID, dstName, ...)
			end
		elseif (dstGUID == UnitGUID("player")) and (srcGUID ~= UnitGUID("player")) then
			if "SPELL_AURA_APPLIED"
			or "SPELL_AURA_REMOVED"
			or "SPELL_INTERRUPT"
			then
			--	OptiTaunt:DeBuffSpellDB_Event(eventtype, srcGUID, srcName, dstGUID, dstName, ...)
			end
		end
	end
end



--Timer (for AoETaunt)
function OptiTaunt:Timer(timestamp, spellId, spellName, duration)

	-- timestamp, spellId, spellName, duration

	local fname = string.gsub(spellName, " ", "_")

	local frame = CreateFrame("Frame", "OptiTaunt_TimerFrame_"..fname);
	
	if not duration or type(duration) ~= "number" then return end
	
	--OptiTaunt:dbg("---Entering OptiTaunt:Timer ---")
	--OptiTaunt:dbg("Frame: "..frame:GetName().."\nSpellName: "..spellName)

	if (self.db.profile.announce.ability) and (self.db.profile.announce.abilityontarget) then
		local d=duration;
		local u=GetTime()+d;
		local t=u;
		frame:SetScript("OnUpdate",function() if GetTime()>=t then OptiTaunt:giveAlert(nil, spellName, nil, spellId, nil, "ABILITYENDS");t=nil;frame:SetScript("OnUpdate",nil)end end)
	end
	
end


-- Alert system
function OptiTaunt:giveAlert(dbID,spellName,customName,spellId,mob,msgtype,extraSpellName,extraSpellID,duration,debuff)
	-- see http://www.wowwiki.com/API_gsub and http://www.wowwiki.com/HOWTO:_Use_Pattern_Matching
	if not customName or customName == "" or customName == " " then
		customName = spellName
	end
	local ability = customName
	local target_ability = extraSpellName
	if self.db.profile.postSpellLink then
		if spellId ~= nil then
			ability = GetSpellLink(spellId)
		else
			ability = ""
		end
		if extraSpellID ~= nil then
			target_ability = GetSpellLink(extraSpellID)
		else
			target_ability = extraSpellName
		end
	end
	if not duration or duration == nil then
		duration = "X"
	else
		self.abilityduration = duration
	end
	
	local spellType, output, subtype
	if debuff == nil then
		spellType = "ability"
		output = "abilities"
	else
		spellType = msgtype
		output = "debuffs"
	end
	if msgtype == "PULLCOUNT" or msgtype == "PULL" then
		output = "other"
		subtype = "PullTimer"
	end
	
	-- parse warningmessage
	if (msgtype == "MISS") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.resist, "{t}", mob), "{a}", ability)
	elseif (msgtype == "IMMUNE") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.immune, "{t}", mob), "{a}", ability)
	elseif (msgtype == "ABILITY") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.ability, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "ABILITYENDS") then
		self.warningmessage = string.gsub(self.db.profile.message.abilityend, "{a}", ability)
	elseif (msgtype == "ABILITYONTARGET") then
		self.warningmessage = string.gsub(string.gsub(string.gsub(self.db.profile.message.abilityontarget, "{a}", ability), "{t}", mob), "{d}", self.abilityduration)
	elseif (msgtype == "ABILITYONTARGETENDS") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.abilityontargetend, "{a}", ability), "{t}", mob)
	elseif (msgtype == "INSTANTCAST") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.instantcast, "{a}", ability), "{t}", mob)
	elseif (msgtype == "COUNTERSPELL") then
		self.warningmessage = string.gsub(string.gsub(string.gsub(self.db.profile.message.counterspell, "{s}", target_ability), "{t}", mob), "{d}", self.abilityduration)
	elseif (msgtype == "PROC") then
		self.warningmessage = string.gsub(self.db.profile.message.proc, "{a}", ability)
	elseif (msgtype == "DEBUFF") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuff, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "DEBUFFENDS") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffend, "{a}", ability))
	elseif (msgtype == "DISARM") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.disarm, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "DISARMENDS") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.disarmend, "{a}", ability))
	elseif (msgtype == "STUN") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffstun, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "MAGIC") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffmagic, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "CURSE") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffcurse, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "POISON") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffpoison, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "DISEASE") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.debuffdisease, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "HEALREQ") then
		self.warningmessage = string.gsub(string.gsub(self.db.profile.message.healreq, "{a}", ability), "{d}", self.abilityduration)
	elseif (msgtype == "PULLCOUNT") then
		self.warningmessage = string.gsub(self.db.profile.message.pullcount, "{d}", duration)
	elseif (msgtype == "PULL") then
		self.warningmessage = self.db.profile.message.pull
	end
	
	--only for testing
	--if (self.isDebug) then
	--	SendChatMessage(self.warningmessage, "SAY")
	--end
	
	ability = spellName
	target_ability = extraSpellName
	
	-- Say
	if self.db.profile[output][ability].output.say then
		SendChatMessage(self.warningmessage, "SAY")
	end
	
	-- Yell
	if self.db.profile[output][ability].output.yell then
		SendChatMessage(self.warningmessage, "YELL")
	end
	
	-- Party- or Raidchat warning
	if self.db.profile[output][ability].output.raid then
		if (GetNumGroupMembers() > 0) then
			SendChatMessage(self.warningmessage, "RAID")
		elseif (GetNumSubgroupMembers() > 0) then
			SendChatMessage(self.warningmessage, "PARTY")
		end
	end
	
	-- Raidwarning
	if self.db.profile[output][ability].output.raidwarning then
		if (GetNumGroupMembers() > 0) and UnitIsGroupAssistant() then
			SendChatMessage(self.warningmessage, "RAID_WARNING")
		end
	end
	
	-- custom channel warning
	for j=1,table.getn(self.db.profile[output][ability].output.channel) do
		if (self.db.profile[output][ability].output.channel[j] ~= nil) then 
			local index = GetChannelName(self.db.profile[output][ability].output.channel[j])
			if (index~=nil) then 
				SendChatMessage(self.warningmessage, "CHANNEL", nil, index); 
			end
		end
	end
	
	if ((msgtype ~= "ABILITY") and (msgtype ~= "ABILITYENDS") and (msgtype ~= "ABILITYONTARGET") and (msgtype ~= "ABILITYONTARGETENDS") and (msgtype ~= "INSTANTCAST") and (msgtype ~= "COUNTERSPELL") and (msgtype ~= "PROC") and (msgtype ~= "DEBUFF") and (msgtype ~= "DEBUFFENDS") and (msgtype ~= "DISARM") and (msgtype ~= "DISARMENDS") and (msgtype ~= "STUN") and (msgtype ~= "MAGIC") and (msgtype ~= "CURSE") and (msgtype ~= "POISON") and (msgtype ~= "DISEASE") and (msgtype ~= "HEALREQ") and (msgtype ~= "PULLCOUNT") and (msgtype ~= "PULL")) then
			
		-- Autowhisper
		if self.db.profile[output][ability].output.whisper and (UnitName("targettarget") ~= nil) and (UnitPlayerControlled("targettarget")) then
			_, englishClass = UnitClass("targettarget");
		
			if (not self.db.profile.whisperWarriors and englishClass == "WARRIOR") then
			-- This (below) is planned but so far we are not sure if a we are talking to a tankadin or a feral druid...
			--if (not self.db.profile.whisperWarriors and englishClass == "WARRIOR" or "PALADIN" or "DRUID") then
				--
			else
				-- we will do class dependent warnings, just for fun
				if (englishClass == "WARRIOR") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Warrior"]
				elseif (englishClass == "DRUID") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Druid"]
				elseif (englishClass == "MAGE") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Mage"]
				elseif (englishClass == "MONK") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Warrior"]
				elseif (englishClass == "WARLOCK") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Warlock"]
				elseif (englishClass == "PALADIN") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Paladin"]
				elseif (englishClass == "PRIEST") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Priest"]
				elseif (englishClass == "HUNTER") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Hunter"]
				elseif (englishClass == "ROGUE") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Rogue"]
				elseif (englishClass == "SHAMAN") then
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Shaman"]
				else 
					self.ResistWhisperMessage = L["Resist-Whisper-Message-Deathknight"]
				end
				
				SendChatMessage(self.warningmessage.." "..self.ResistWhisperMessage, "WHISPER", nil, UnitName("targettarget"));
			end
		end
	end
end

-- Audio alert
function OptiTaunt:giveAudioAlert()
	if (self.db.profile.giveAudioWarning) then
			PlaySound("GAMEHIGHLIGHTFRIENDLYUNIT"); --workaround from wowace-forums for buggy soundsystem
			PlaySoundFile(self.tauntsoundfile);
	end
end


-- prints taunt statistics
function OptiTaunt:PrintTauntStatistics()

local TauntStaticticsTitle = "|cffff6d00OptiTaunt >|r "

	if ((GetNumGroupMembers() == 0) and (GetNumSubgroupMembers() == 0)) then
			DEFAULT_CHAT_FRAME:AddMessage(TauntStaticticsTitle..L["Taunts"].." "..self.tauntcount.."  "..L["Resisted"].." "..self.tauntresistcount.." ("..self.tauntresistpercent.."%)")
	elseif (GetNumGroupMembers() > 0) then
			SendChatMessage("OptiTaunt > "..L["Taunts"].." "..self.tauntcount.."  "..L["Resisted"].." "..self.tauntresistcount.." ("..self.tauntresistpercent.."%)", "RAID")
	elseif (GetNumSubgroupMembers() > 0) then
			SendChatMessage("OptiTaunt > "..L["Taunts"].." "..self.tauntcount.."  "..L["Resisted"].." "..self.tauntresistcount.." ("..self.tauntresistpercent.."%)", "PARTY")
	end
end

-- Reset Counter
function OptiTaunt:ResetCounters()
	self.tauntcount = 0
	self.tauntresistcount = 0
	self.tauntresistpercent = 0
end


--[[
---- Toggle Options -------------------------------------------------------------------------------
]]

-- Announcement
function OptiTaunt:IsShowInRaidAnnouncement()
    return self.db.profile.showInRaidAnnouncement
end

function OptiTaunt:ToggleShowInRaidAnnouncement()
    self.db.profile.showInRaidAnnouncement = not self.db.profile.showInRaidAnnouncement
end

function OptiTaunt:IsShowInRaidPartyChat()
    return self.db.profile.showInRaidPartyChat
end

function OptiTaunt:ToggleShowInRaidPartyChat()
    self.db.profile.showInRaidPartyChat = not self.db.profile.showInRaidPartyChat
end


-- AutoWhisper
function OptiTaunt:IsAutoWhisper()
    return self.db.profile.autoWhisper
end

function OptiTaunt:ToggleAutoWhisper()
    self.db.profile.autoWhisper = not self.db.profile.autoWhisper
end


-- Post SpellLink instead of SpellName
function OptiTaunt:IsSpellLink()
    return self.db.profile.postSpellLink
end

function OptiTaunt:ToggleSpellLink()
    self.db.profile.postSpellLink = not self.db.profile.postSpellLink
end


-- AudioWarning
function OptiTaunt:IsGiveAudioWarning()
    return self.db.profile.giveAudioWarning
end

function OptiTaunt:ToggleGiveAudioWarning()
    self.db.profile.giveAudioWarning = not self.db.profile.giveAudioWarning
end


-- AggroCheck
function OptiTaunt:IsAggroCheck()
    return self.db.profile.aggroCheck
end

function OptiTaunt:ToggleAggroCheck()
    self.db.profile.aggroCheck = not self.db.profile.aggroCheck
end


-- Whisper Warriors
function OptiTaunt:IsWhisperWarriors()
    return self.db.profile.whisperWarriors
end

function OptiTaunt:ToggleWhisperWarriors()
    self.db.profile.whisperWarriors = not self.db.profile.whisperWarriors
end


-- PvP
function OptiTaunt:IsPvPCheck()
    return self.db.profile.pvpCheck
end

function OptiTaunt:TogglePvPCheck()
    self.db.profile.pvpCheck = not self.db.profile.pvpCheck
end


-- Resist
function OptiTaunt:GetResistMessage()
	return self.db.profile.message.resist
end
	
function OptiTaunt:SetResistMessage(arg1)
	self.db.profile.message.resist = arg1
end

function OptiTaunt:IsAnnounceResist()
	return self.db.profile.announce.resist
end

function OptiTaunt:ToggleAnnounceResist()
	self.db.profile.announce.resist = not self.db.profile.announce.resist
end


-- Immune
function OptiTaunt:GetImmuneMessage()
	return self.db.profile.message.immune
end
	
function OptiTaunt:SetImmuneMessage(arg1)
	self.db.profile.message.immune = arg1
end

function OptiTaunt:IsAnnounceImmune()
	return self.db.profile.announce.immune
end

function OptiTaunt:ToggleAnnounceImmune()
	self.db.profile.announce.immune = not self.db.profile.announce.immune
end


-- Ability
function OptiTaunt:GetAbilityMessage()
	return self.db.profile.message.ability
end
	
function OptiTaunt:SetAbilityMessage(arg1)
	self.db.profile.message.ability = arg1
end

function OptiTaunt:IsAnnounceAbility()
	return self.db.profile.announce.ability
end

function OptiTaunt:ToggleAnnounceAbility()
	self.db.profile.announce.ability = not self.db.profile.announce.ability
end

-- AbilityEnd
function OptiTaunt:GetAbilityEndMessage()
	return self.db.profile.message.abilityend
end
	
function OptiTaunt:SetAbilityEndMessage(arg1)
	self.db.profile.message.abilityend = arg1
end

function OptiTaunt:IsAnnounceAbilityEnd()
	return self.db.profile.announce.abilityend
end

function OptiTaunt:ToggleAnnounceAbilityEnd()
	self.db.profile.announce.abilityend = not self.db.profile.announce.abilityend
end


--AbilityOnTarget
function OptiTaunt:GetAbilityOnTargetMessage()
	return self.db.profile.message.abilityontarget
end
	
function OptiTaunt:SetAbilityOnTargetMessage(arg1)
	self.db.profile.message.abilityontarget = arg1
end

function OptiTaunt:IsAnnounceAbilityOnTarget()
	return self.db.profile.announce.abilityontarget
end

function OptiTaunt:ToggleAnnounceAbilityOnTarget()
	self.db.profile.announce.abilityontarget = not self.db.profile.announce.abilityontarget
end

--AbilityOnTargetEnd
function OptiTaunt:GetAbilityOnTargetEndMessage()
	return self.db.profile.message.abilityontargetend
end
	
function OptiTaunt:SetAbilityOnTargetEndMessage(arg1)
	self.db.profile.message.abilityontargetend = arg1
end

function OptiTaunt:IsAnnounceAbilityOnTargetEnd()
	return self.db.profile.announce.abilityontargetend
end

function OptiTaunt:ToggleAnnounceAbilityOnTargetEnd()
	self.db.profile.announce.abilityontargetend = not self.db.profile.announce.abilityontargetend
end

--InstantCast
function OptiTaunt:GetInstantCastMessage()
	return self.db.profile.message.instantcast
end
	
function OptiTaunt:SetInstantCastMessage(arg1)
	self.db.profile.message.instantcast = arg1
end

--Counterspell
function OptiTaunt:GetCounterspellMessage()
	return self.db.profile.message.counterspell
end
	
function OptiTaunt:SetCounterspellMessage(arg1)
	self.db.profile.message.counterspell = arg1
end

--Proc
function OptiTaunt:GetProcMessage()
	return self.db.profile.message.proc
end
	
function OptiTaunt:SetProcMessage(arg1)
	self.db.profile.message.proc = arg1
end

-- Disarm
function OptiTaunt:GetDisarmMessage()
	return self.db.profile.message.disarm
end
	
function OptiTaunt:SetDisarmMessage(arg1)
	self.db.profile.message.disarm = arg1
end

function OptiTaunt:IsAnnounceDisarm()
	return self.db.profile.announce.disarm
end

function OptiTaunt:ToggleAnnounceDisarm()
	self.db.profile.announce.disarm = not self.db.profile.announce.disarm
end

-- DisarmEnd
function OptiTaunt:GetDisarmEndMessage()
	return self.db.profile.message.disarmend
end
	
function OptiTaunt:SetDisarmEndMessage(arg1)
	self.db.profile.message.disarmend = arg1
end

function OptiTaunt:IsAnnounceDisarmEnd()
	return self.db.profile.announce.disarmend
end

function OptiTaunt:ToggleAnnounceDisarmEnd()
	self.db.profile.announce.disarmend = not self.db.profile.announce.disarmend
end


-- Debuff
function OptiTaunt:GetDebuffMessage()
	return self.db.profile.message.debuff
end
	
function OptiTaunt:SetDebuffMessage(arg1)
	self.db.profile.message.debuff = arg1
end

function OptiTaunt:IsAnnounceDebuff()
	return self.db.profile.announce.debuff
end

function OptiTaunt:ToggleAnnounceDebuff()
	self.db.profile.announce.debuff = not self.db.profile.announce.debuff
end

-- DebuffEnd
function OptiTaunt:GetDebuffEndMessage()
	return self.db.profile.message.debuffend
end
	
function OptiTaunt:SetDebuffEndMessage(arg1)
	self.db.profile.message.debuffend = arg1
end

function OptiTaunt:IsAnnounceDebuffEnd()
	return self.db.profile.announce.debuffend
end

function OptiTaunt:ToggleAnnounceDebuffEnd()
	self.db.profile.announce.debuffend = not self.db.profile.announce.debuffend
end

-- Stun
function OptiTaunt:GetDebuffStunMessage()
	return self.db.profile.message.debuffstun
end
	
function OptiTaunt:SetDebuffStunMessage(arg1)
	self.db.profile.message.debuffstun = arg1
end

function OptiTaunt:IsAnnounceDebuffStun()
	return self.db.profile.announce.debuffstun
end

function OptiTaunt:ToggleAnnounceDebuffStun()
	self.db.profile.announce.debuffstun = not self.db.profile.announce.debuffstun
end

-- Magic debuff
function OptiTaunt:GetDebuffMagicMessage()
	return self.db.profile.message.debuffmagic
end
	
function OptiTaunt:SetDebuffMagicMessage(arg1)
	self.db.profile.message.debuffmagic = arg1
end

function OptiTaunt:IsAnnounceDebuffMagic()
	return self.db.profile.announce.debuffmagic
end

function OptiTaunt:ToggleAnnounceDebuffMagic()
	self.db.profile.announce.debuffmagic = not self.db.profile.announce.debuffmagic
end

-- Curse
function OptiTaunt:GetDebuffCurseMessage()
	return self.db.profile.message.debuffcurse
end
	
function OptiTaunt:SetDebuffCurseMessage(arg1)
	self.db.profile.message.debuffcurse = arg1
end

function OptiTaunt:IsAnnounceDebuffCurse()
	return self.db.profile.announce.debuffcurse
end

function OptiTaunt:ToggleAnnounceDebuffCurse()
	self.db.profile.announce.debuffcurse = not self.db.profile.announce.debuffcurse
end

-- Poison
function OptiTaunt:GetDebuffPoisonMessage()
	return self.db.profile.message.debuffpoison
end
	
function OptiTaunt:SetDebuffPoisonMessage(arg1)
	self.db.profile.message.debuffpoison = arg1
end

function OptiTaunt:IsAnnounceDebuffPoison()
	return self.db.profile.announce.debuffpoison
end

function OptiTaunt:ToggleAnnounceDebuffPoison()
	self.db.profile.announce.debuffpoison = not self.db.profile.announce.debuffpoison
end

-- Disease
function OptiTaunt:GetDebuffDiseaseMessage()
	return self.db.profile.message.debuffdisease
end
	
function OptiTaunt:SetDebuffDiseaseMessage(arg1)
	self.db.profile.message.debuffdisease = arg1
end

function OptiTaunt:IsAnnounceDebuffDisease()
	return self.db.profile.announce.debuffdisease
end

function OptiTaunt:ToggleAnnounceDebuffDisease()
	self.db.profile.announce.debuffdisease = not self.db.profile.announce.debuffdisease
end

-- Heal required
function OptiTaunt:GetHealreqMessage()
	return self.db.profile.message.healreq
end
	
function OptiTaunt:SetHealreqMessage(arg1)
	self.db.profile.message.healreq = arg1
end

function OptiTaunt:IsAnnounceHealreq()
	return self.db.profile.announce.healreq
end

function OptiTaunt:ToggleAnnounceHealreq()
	self.db.profile.announce.healreq = not self.db.profile.announce.healreq
end

-- PullTimer
function OptiTaunt:GetPullCountMessage()
	return self.db.profile.message.pullcount
end
function OptiTaunt:SetPullCountMessage(arg1)
	self.db.profile.message.pullcount = arg1
end

function OptiTaunt:GetPullMessage()
	return self.db.profile.message.pull
end
function OptiTaunt:SetPullMessage(arg1)
	self.db.profile.message.pull = arg1
end

function OptiTaunt:GetPullTimerRange()
	return self.db.profile.other.PullTimer.pulltimerrange
end
function OptiTaunt:SetPullTimerRange(arg1)
	self.db.profile.other.PullTimer.pulltimerrange = arg1
end

-- Whisper Warriors
function OptiTaunt:IsMinimapIcon()
	if OptiTaunt.db.profile.minimapIcon.hide == true then
		return false
	else
		return true
	end
end

function OptiTaunt:ToggleMinimapIcon()
    if OptiTaunt.ShowLDBIcon == 1 then
		OptiTaunt:LDB_Disable()
	else
		OptiTaunt:LDB_Enable()
	end
end

CreateFrame("Frame", "OptiTaunt_PullTimerFrame");
function OptiTaunt:PullTimer()
	--OptiTaunt:dbg("---Entering OptiTaunt:PullTimer ---")
	
	local function post(message)
		if (GetNumGroupMembers() > 0) then
			SendChatMessage(message, "RAID")
		elseif (GetNumSubgroupMembers() > 0) then
			SendChatMessage(message, "PARTY")
		else
			print(message)
		end
	end
	
	self.PullTime = self.db.profile.other.PullTimer.pulltimerrange
	
	if not self.PullTime or self.PullTime == nil then
		self.PullTime = 3
	end
	
	local frame = _G["OptiTaunt_PullTimerFrame"]
	
	if frame:GetScript("OnUpdate") ~= nil then return end
	
	local timetopull = tonumber(self.PullTime)
	local starttime = GetTime()
	local endtime = starttime + self.PullTime
	
	local remainingtime = timetopull - 1
	local actualtime = starttime + 1
	
	local pullcountmessage = self.db.profile.message.pullcount
	local pullmessage = self.db.profile.message.pull
	local msg = string.gsub(pullcountmessage, "{d}", timetopull)
	--post(msg)
	local PullOutput = "PullTimer"
	
	OptiTaunt:giveAlert(nil, PullOutput, nil, nil, nil, "PULLCOUNT", nil, nil, timetopull)
	
	local d=1;
	local u=GetTime()+d;
	local t=u;
	
	frame:SetScript("OnUpdate",function() 
		if GetTime()>=t then
			if remainingtime > 0 then
				--msg = string.gsub(pullcountmessage, "{d}", remainingtime)
				--post(msg)
				OptiTaunt:giveAlert(nil, PullOutput, nil, nil, nil, "PULLCOUNT", nil, nil, remainingtime)
				remainingtime = remainingtime - 1
				u=GetTime()+d;
				t=u;
			elseif remainingtime == 0 then
				--post(pullmessage)
				OptiTaunt:giveAlert(nil, PullOutput, nil, nil, nil, "PULL")
				t=nil;
				frame:SetScript("OnUpdate",nil)
			else
				t=nil;
				frame:SetScript("OnUpdate",nil)
			end
		end
	end)
		
	
	--frame:SetScript("OnUpdate",function() if GetTime()>=t then OptiTaunt:giveAlert(nil, spellName, nil, spellId, nil, "ABILITYENDS");t=nil;frame:SetScript("OnUpdate",nil)end end)
	--OptiTaunt_PullTimerFrame:SetScript("OnUpdate",nil)
	
end


--
--	DataBroker and LDBIcon
--
local LDB		= LibStub("LibDataBroker-1.1", true)
local LDBIcon	= LibStub("LibDBIcon-1.0", true)
local data_obj

function OptiTaunt:LDB_OnEnable()
	data_obj = LDB:NewDataObject("OptiTaunt", {
		type	= "launcher",
		label	= "OptiTaunt",
		text	= " ",
		icon	= "Interface\\Icons\\Spell_Nature_Reincarnation",
		OnEnter	= function()
			  end,
		-- OnLeave is an empty function because some LDB displays refuse to display a plugin that has an OnEnter but no OnLeave.
		OnLeave	= function()
			  end,
		OnClick = function(display, button)
					if button == "LeftButton" then
						OptiTaunt:PullTimer()
					elseif button == "RightButton" then
						AceLibrary("Dewdrop-2.0"):Register(display, 'children', function()
							AceLibrary("Dewdrop-2.0"):FeedAceOptionsTable(OptiTaunt.options)
						end);
						AceLibrary("Dewdrop-2.0"):Open(display, 'children', function()
							AceLibrary("Dewdrop-2.0"):FeedAceOptionsTable(OptiTaunt.options)
						end);
					elseif button == "MiddleButton" then
						print(display:GetName())
					end
			  end,
		OnTooltipShow = function(tooltip)
			if not tooltip or not tooltip.AddLine then return end
			tooltip:AddLine(OptiTaunt.options.args.title.name)
			tooltip:AddLine(L["Statistics"],1,1,1,1)
			tooltip:AddDoubleLine(L["Taunts"], self.tauntcount, 1,1,0, 1,1,1);
			tooltip:AddDoubleLine(L["Resisted"], self.tauntresistcount.." ("..self.tauntresistpercent.."%)", 1,1,0, 1,1,1);
			tooltip:AddLine("   "..L["Tooltip"],0,0.9,0,1)
			tooltip:Show()
		end,
	})
	--UpdateDataFeed()
	
	if LDBIcon then
		LDBIcon:Register("OptiTaunt", data_obj, OptiTaunt.db.profile.minimapIcon)
	end
	OptiTaunt.ShowLDBIcon = 1
	
end

function OptiTaunt:LDB_Enable()
	OptiTaunt.db.profile.minimapIcon.hide = false
	LDBIcon:Refresh("OptiTaunt", OptiTaunt.db.profile.minimapIcon)
	OptiTaunt.ShowLDBIcon = 1
end
function OptiTaunt:LDB_Disable()
	OptiTaunt.db.profile.minimapIcon.hide = true
	LDBIcon:Refresh("OptiTaunt", OptiTaunt.db.profile.minimapIcon)
	OptiTaunt.ShowLDBIcon = 0
end




OptiTauntTimerTESTMODE = 0;	--Debug	Set to '1' for testing.
local CEnd = "|r"
local CDK = "|cffC41F3B"
local CDru = "|cffFF7D0A"
local CHun = "|cffABD473"
local CMag = "|cff69CCF0"
local CPal = "|cffF58CBA"
local CPri = "|cffFFFFFF"
local CRog = "|cffFFF569"
local CSha = "|cff0070DE"
local CWl = "|cff9482C9"
local CWar = "|cffC79C6E"
function OptiTaunt:Spell_Cast_Success_Timer(timestamp, eventtype, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellId, spellName, spellSchool)

	OptiTaunt:dbg("---Entering OptiTaunt:Spell_Cast_Success_Timer---")
	
	local showCStimer = true
	local isInstance, InstanceMode = IsInInstance()
	if (showCStimer == true) then
		if ( ( OptiTauntTimerTESTMODE == 1 ) --[[or ( isInstance == true )]] ) then
			if spellId == 6552 then  --Warrior Pummel
				OptiTaunt:CreateCandyBar(CWar..srcName..CEnd, spellName, 10)
			elseif spellId == 47528 then --DK Mind Freeze
				OptiTaunt:CreateCandyBar(CDK..srcName..CEnd, spellName, 10)
			elseif spellId == 47476 then --DK Strangulate
				OptiTaunt:CreateCandyBar(CDK..srcName..CEnd, spellName, 10)
			elseif spellId == 49802 then --Druid Maim
				OptiTaunt:CreateCandyBar(CDru..srcName..CEnd, spellName, 10)
			elseif spellId == 22570 then --Druid Maim(Old Rank1)
				OptiTaunt:CreateCandyBar(CDru..srcName..CEnd, spellName, 10)
			elseif spellId == 5211 then  --Druid Bash
				OptiTaunt:CreateCandyBar(CDru..srcName..CEnd, spellName, 60)
			elseif spellId == 80964 or spellId == 80965 then  --Druid Skull Bash (Bear / Cat)
				OptiTaunt:CreateCandyBar(CDru..srcName..CEnd, spellName, 60)
			elseif spellId == 78675 or spellId == 81261 then  --Druid Solar Beam
				OptiTaunt:CreateCandyBar(CDru..srcName..CEnd, spellName, 60)
			elseif spellId == 57994 then -- Shaman Wind Shear
				OptiTaunt:CreateCandyBar(CSha..srcName..CEnd, spellName, 6)
			elseif spellId == 1766 then  --Rogue Kick
				OptiTaunt:CreateCandyBar(CRog..srcName..CEnd, spellName, 10)
			elseif spellId == 2139 then  --Mage Counterspell
				OptiTaunt:CreateCandyBar(CMag..srcName..CEnd, spellName, 24)
			elseif spellId == 116705 then  --Monk Spear Hand Strike
				OptiTaunt:CreateCandyBar(CMag..srcName..CEnd, spellName, 15)
			elseif spellId == 96231 then  --Paladin Rebuke
				OptiTaunt:CreateCandyBar(CPal..srcName..CEnd, spellName, 10)
			end			
		end		
	end
	
	OptiTaunt:dbg("---Leaving OptiTaunt:Spell_Cast_Success_Timer---")
	
end

function OptiTaunt:CreateCandyBar(player, spell, duration)

	local candy = LibStub("LibCandyBar-3.0")
	local texture = "Interface\\AddOns\\oRA3\\images\\statusbar"
	local mybar = candy:New(texture, 200, 16)
	mybar:SetLabel(player.." - "..spell)
	mybar:SetDuration(duration)
	mybar:SetColor(1, 1, 1, 1)
	mybar:ClearAllPoints();
	mybar:SetPoint("CENTER", nil, 0, 0)
	mybar:Start()
	
	--/run local candy = LibStub("LibCandyBar-3.0"); local texture = "Interface\\AddOns\\oRA3\\images\\statusbar"; local mybar = candy:New(texture, 100, 16); mybar:SetLabel("Yay!"); mybar:SetDuration(60); mybar:Start();mybar:ClearAllPoints();  mybar:SetPoint("CENTER", nil, 0, 0)
	
end

function OptiTaunt:TestAutoDB()
	local class = OptiTaunt.playerclass
	for _,v in pairs(OptiTaunt_PlayerSpellDB[class]) do
		for _,w in pairs(v) do
			local spellName = GetSpellInfo(w.SpellID)
			print(spellName)
		end
	end
end

OptiTaunt_postDebug = 0
local function post(message)
	if OptiTaunt_postDebug == 0 then return end
--[[	if (GetNumGroupMembers() > 0) then
		SendChatMessage(message, "RAID")
	elseif (GetNumSubgroupMembers() > 0) then
		SendChatMessage(message, "PARTY")
	else
		print(message)
	end]]
	print(message)
end

OptiTaunt_TestPSDB = 1
OptiTaunt_SUCD_ExpirationDelay = {}
function OptiTaunt:PlayerSpellDB_Event(eventtype, srcGUID, srcName, dstGUID, dstName, ...)
	local spellId, spellName, spellSchool = ...	--SPELL_ vars
	local class = OptiTaunt.playerclass
	if OptiTaunt_TestPSDB == 0 then return end	--return if TESTMODE is false
	local spell, name, duration
	local auraType	--AURA vars
	local missType, amountMissed	--MISS vars
	local extraSpellID, extraSpellName	--INTERRUPT vars
	
	if dstName == OptiTaunt.playername or dstName == nil then
		dstName = "ME"
	end
	
	--	print(eventtype)
	--	print(spellId)
	--	print(...)
	--[[
	local doreturn = 1
	local events = {
		"SPELL_AURA_APPLIED",
		"SPELL_AURA_REMOVED",
		"SPELL_CAST_SUCCESS",
		"SPELL_INTERRUPT",
		"SPELL_MISSED",
		"SPELL_SUMMON",
		"SPELL_UPDATE_COOLDOWN",
		"PLAYER_TOTEM_UPDATE",
	}
	for k,v in pairs(events) do
		if eventtype == v then
			doreturn = 0
		end
	end
	if doreturn == 1 then return end
	]]
--	if (srcGUID == UnitGUID("player")) then
		for k,v in pairs(OptiTaunt.db.profile.abilities) do
			local spellfromtable
			for i,w in pairs(v.SpellID) do
				if w == spellId then
					spellfromtable = w
				end
			end
			if spellfromtable ~= nil and spellfromtable == spellId then
				--print("Found SpellID: "..spellId)
				name = spellName	--GetSpellInfo(v.SpellID)
				if eventtype == "SPELL_AURA_APPLIED" and (OptiTaunt.db.profile.abilities[k].event[eventtype] or (v.Category == "Aura" or v.Category == nil)) and v.Category ~= "Taunt" then
					if v.Category == nil then
						v.Category = "Aura"
					end
					auraType = select(4,...)
					auraType = tostring(auraType)
					local RaidTargetIndex
					
					duration, RaidTargetIndex = OptiTaunt:GetDstInfo(dstGUID, auraType, spellName)
					
					if v.SpellName ~= nil and v.SpellName ~= "" and v.SpellName ~= " " then
						name = v.SpellName
					end
				--	post("Aura applied: "..spellName.." @ "..dstName.." - Duration: "..duration)
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					if dstName == "ME" then
						OptiTaunt:giveAlert(nil, spellName, name, spellId, nil, "ABILITY", nil, nil, duration)
					else
						if type(RaidTargetIndex) == "number" then
							dstName = "{rt"..RaidTargetIndex.."}"..dstName.."{rt"..RaidTargetIndex.."}"
						end
						OptiTaunt:giveAlert(nil, spellName, name, spellId, dstName, "ABILITYONTARGET", nil, nil, duration)
					end
				elseif eventtype == "SPELL_AURA_REMOVED" and OptiTaunt.db.profile.abilities[k].event[eventtype] and v.Category ~= "Taunt" then
				--	post("Aura removed: "..spellName.." from "..dstName)
					if v.SpellName ~= nil and v.SpellName ~= "" and v.SpellName ~= " " then
						name = v.SpellName
					end
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					if dstName == "ME" then
						OptiTaunt:giveAlert(nil, spellName, name, spellId, nil, "ABILITYENDS")
					else
						OptiTaunt:giveAlert(nil, spellName, name, spellId, dstName, "ABILITYONTARGETENDS")
					end
				elseif eventtype == "SPELL_CAST_SUCCESS" and OptiTaunt.db.profile.abilities[k].event[eventtype] and v.Category ~= "Aura" and v.Category ~= "Totem" then
					if v.Category == nil then
						v.Category = "Spell"
					end
					local RaidTargetIndex					
					_, RaidTargetIndex = OptiTaunt:GetDstInfo(dstGUID, nil, spellName)					
				--	post("Spell Casted: "..spellName)
					if v.SpellName ~= nil and v.SpellName ~= "" and v.SpellName ~= " " then
						name = v.SpellName
					end
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					if v.Category == "Taunt" then
						local realUnitID = OptiTaunt:GetUnitID(dstGUID)
						if UnitPlayerControlled(realUnitID) then
							return
						else
							--dst is a NPC
						end
					end
					if v.Category == "Interrupt" then
						local extraSpellName, extraSpellID = OptiTaunt:GetDstSpellInfo(dstGUID), nil
						if extraSpellName == nil then
							extraSpellName = SPELL_CASTING
						end
						if type(RaidTargetIndex) == "number" then
							dstName = "{rt"..RaidTargetIndex.."}"..dstName.."{rt"..RaidTargetIndex.."}"
						end
						OptiTaunt:giveAlert(nil, spellName, nil, spellId, dstName, "COUNTERSPELL", extraSpellName, extraSpellID, v.Duration)
						return
					end
					if dstName == "ME" then
						OptiTaunt:giveAlert(nil, spellName, name, spellId, nil, "ABILITY", nil, nil, v.Duration)
					else
						if type(RaidTargetIndex) == "number" then
							dstName = "{rt"..RaidTargetIndex.."}"..dstName.."{rt"..RaidTargetIndex.."}"
						end
						OptiTaunt:giveAlert(nil, spellName, name, spellId, dstName, "ABILITYONTARGET", nil, nil, v.Duration)
					end
					if v.Category == "Timer" or OptiTaunt.db.profile.abilities[k].event["  TIMER via duration"] then
						OptiTaunt:Timer(nil, spellId, spellName, v.Duration)
					end
				elseif eventtype == "SPELL_INTERRUPT" and OptiTaunt.db.profile.abilities[k].event[eventtype] then
					extraSpellID, extraSpellName = select(4,...)
				--	post("Spell Interrupt: "..extraSpellName.." @ "..dstName)
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					local RaidTargetIndex					
					_, RaidTargetIndex = OptiTaunt:GetDstInfo(dstGUID)
					if type(RaidTargetIndex) == "number" then
						dstName = "{rt"..RaidTargetIndex.."}"..dstName.."{rt"..RaidTargetIndex.."}"
					end
					OptiTaunt:giveAlert(nil, spellName, nil, spellId, dstName, "COUNTERSPELL", extraSpellName, extraSpellID, v.Duration)
				elseif eventtype == "SPELL_MISSED" and OptiTaunt.db.profile.abilities[k].event[eventtype] then
					missType, amountMissed = select(4,...)
				--	post("Spell Missed: "..spellName.." - Reason: "..missType)
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					if v.Category == "Taunt" then
						local realUnitID = OptiTaunt:GetUnitID(dstGUID)
						if UnitPlayerControlled(realUnitID) then
							return
						else
							--dst is a NPC
						end
					end
					local RaidTargetIndex					
					_, RaidTargetIndex = OptiTaunt:GetDstInfo(dstGUID)
					if type(RaidTargetIndex) == "number" then
						dstName = "{rt"..RaidTargetIndex.."}"..dstName.."{rt"..RaidTargetIndex.."}"
					end
					if missType == "IMMUNE" and OptiTaunt.db.profile.announce.immune then
						OptiTaunt:giveAlert(nil, spellName, nil, spellId, dstName, "IMMUNE")
					elseif missType ~= "IMMUNE" and OptiTaunt.db.profile.announce.resist then
						OptiTaunt:giveAlert(nil, spellName, name, spellId, dstName, "MISS")
					end
					if v.Category == "Taunt" then
						OptiTaunt:giveAudioAlert()
					end
				elseif eventtype == "SPELL_SUMMON" and OptiTaunt.db.profile.abilities[k].event[eventtype] then
					if v.SpellName ~= nil and v.SpellName ~= "" and v.SpellName ~= " " then
						name = v.SpellName
					end
					OptiTaunt.lastTotem[1] = spellName
					
				--	post("Summoned: "..spellName.." - Duration: "..v.Duration)
					
					spellName = k	--Change actual spellName to the spellName the ID is grouped to
					OptiTaunt:giveAlert(nil, spellName, name, spellId, nil, "ABILITY", nil, nil, v.Duration)
					if OptiTaunt.db.profile.abilities[k].event["  TIMER via duration"] then
						OptiTaunt:Timer(nil, spellId, spellName, v.Duration)
					end
				else
					--print("No Event for SpellName: "..name)
					if v.Category == "Aura" then
						--print("Reason: Spell_Cast_Success was ignored because of AURA.")
					end
				end
			else
				--print("No fitting spells.")
			end
			if eventtype == "SPELL_UPDATE_COOLDOWN" and OptiTaunt.db.profile.abilities[k].event[eventtype] then
				for i,w in pairs(v.SpellID) do
					local name = GetSpellInfo(w)
					local start, duration = GetSpellCooldown(w)
					local realtime = start + 10
					--print("Spell: "..name)
					--print("Start: "..start)
					--print("CD: "..duration)
					if not OptiTaunt_SUCD_ExpirationDelay[k] then OptiTaunt_SUCD_ExpirationDelay[k] = 0 end
					if start > 0 and duration > 20 then
						local t = GetTime()
						if OptiTaunt_SUCD_ExpirationDelay[k] == 0 and GetTime() <= realtime then
						--	post("Used "..name)
							OptiTaunt:giveAlert(nil, k, name, w, nil, "ABILITY", nil, nil, duration)
						end
						OptiTaunt_SUCD_ExpirationDelay[k] = 1
						local d=duration;
						local u=start+d;
						local t=u;
						local frame = OptiTaunt.frame
						frame:SetScript("OnUpdate",function() if GetTime()>=t then OptiTaunt_SUCD_ExpirationDelay[k]=0;t=nil;frame:SetScript("OnUpdate",nil)end end)
					elseif start == 0 or duration == 0 then
						OptiTaunt_SUCD_ExpirationDelay[k] = 0
					end
				end					
			end
			if eventtype == "PLAYER_TOTEM_UPDATE" and OptiTaunt.db.profile.abilities[k].event[eventtype] then
			--	print("PLAYER_TOTEM_UPDATE")
			--	print(...)
				local arg1 = ...
				if arg1 == nil then return end
				local haveTotem, spellName, startTime, duration, icon = GetTotemInfo(arg1)
				--print(GetTotemInfo(arg1))
				local spell
				if OptiTaunt.lastTotem[arg1] ~= nil and OptiTaunt.lastTotem[arg1] ~= "" then
					spell = GetSpellInfo(OptiTaunt.lastTotem[arg1])
				else
					spell = ""
				end
				if haveTotem and spellName ~= "" and ( OptiTaunt.lastTotem[arg1] == "" or spellName == spell ) then
				--	for k,v in pairs(OptiTaunt.db.profile.abilities) do
						for i,w in pairs(v.SpellID) do
							local name = GetSpellInfo(w)
							if name == spellName then		
							--	post("Totem: "..spellName.." - Duration: "..duration)
								OptiTaunt:giveAlert(nil, spellName, v.SpellName, w, nil, "ABILITY", nil, nil, duration)
								OptiTaunt.lastTotem[arg1] = w
							end
						end
				--	end
				else
					if OptiTaunt.lastTotem[arg1] ~= "" then
						local spellName = GetSpellInfo(OptiTaunt.lastTotem[arg1])	--OptiTaunt.lastTotem[arg1]
					--	post("Totem DIED: "..spellName)
						OptiTaunt:giveAlert(nil, spellName, name, OptiTaunt.lastTotem[arg1], nil, "ABILITYENDS", nil, nil, duration)
						OptiTaunt.lastTotem[arg1] = ""
					end
				end
			end
		
		end
--	end
	
	spellId, spellName, spellSchool = nil, nil, nil	--SPELL_ vars
	spell, name, duration = nil, nil, nil
	auraType = nil	--AURA vars
	missType, amountMissed = nil, nil	--MISS vars
	extraSpellID, extraSpellName = nil, nil	--INTERRUPT vars
	
end

function OptiTaunt:DeBuffSpellDB_Event(eventtype, srcGUID, srcName, dstGUID, dstName, ...)
	local spellId, spellName, spellSchool = ...	--SPELL_ vars
	local class = OptiTaunt.playerclass
	if OptiTaunt_TestPSDB == 0 then return end	--return if TESTMODE is false
	local spell, name, duration
	local auraType	--AURA vars
	
	if dstName == OptiTaunt.playername or dstName == nil then
		dstName = "ME"
	end
	
	--	print(eventtype)
	--	print(spellId)
	--	print(...)	
	--[[
	local doreturn = 1
	local events = {
		"SPELL_AURA_APPLIED",
		"SPELL_AURA_REMOVED",
		"SPELL_INTERRUPT",
	}
	for k,v in pairs(events) do
		if eventtype == v then
			doreturn = 0
		end
	end
	if doreturn == 1 then return end
	]]
--	if (dstGUID == UnitGUID("player")) and (srcGUID ~= UnitGUID("player")) then
		auraType = select(4,...)
		auraType = tostring(auraType)
		if auraType == nil then return end
		for k,v in pairs(OptiTaunt_SpellDB[auraType]) do
			if type(v.SpellID) == "number" then
				spell = v.SpellID
			elseif type(v.SpellID) == "table" then
				for k,v in pairs(v.SpellID) do
					if v == spellId then
						spell = v
					end
				end
			end
			if spell ~= nil and spell == spellId then
				--print("Found SpellID: "..spellId)
				name = spellName	--GetSpellInfo(v.SpellID)
				if eventtype == "SPELL_AURA_APPLIED" then
					if auraType == "BUFF" then
						duration = tostring(select(6,UnitBuff("player", spellName)))
					elseif auraType == "DEBUFF" then
						duration = tostring(select(6,UnitDebuff("player", spellName)))
						OptiTaunt:giveAlert(nil, spellName, nil, spellId, nil, "DEBUFF")
					end
					post(auraType.." applied: "..spellName.." - Duration: "..duration.." - by "..srcName)
				elseif eventtype == "SPELL_AURA_REMOVED" then
					post(auraType.." removed: "..spellName)
					OptiTaunt:giveAlert(nil, spellName, nil, spellId, nil, "DEBUFFENDS")
				else
					print("No Event for SpellName: "..spellName)
				end				
			else
				--print("No fitting spells.")
			end
		end
--	end
	
	spellId, spellName, spellSchool = nil, nil, nil	--SPELL_ vars
	spell, name, duration = nil, nil, nil
	auraType = nil	--AURA vars
	
end

function OptiTaunt:CreateEventDB()

	OptiTaunt.events = {}
	
	local eventtype = {
		"SPELL_AURA_APPLIED",
		"SPELL_AURA_REMOVED",
		"SPELL_CAST_SUCCESS",
		"SPELL_INTERRUPT",
		"SPELL_MISSED",
		"SPELL_SUMMON",
		"SPELL_UPDATE_COOLDOWN",
		"PLAYER_TOTEM_UPDATE",
	}
	
	local options = {
		"announce",
		"Category",
		"CD",
		"Duration",
		"output",
	}
	
	for k,v in pairs(OptiTaunt.db.profile.abilities) do
		for i,w in pairs(v.SpellID) do
		--	OptiTaunt.db.profile.abilities[k].event[eventtype]
			
		--	OptiTaunt.events
			
				for n,e in pairs(eventtype) do
					if OptiTaunt.db.profile.abilities[k].event[e] then						
						if not OptiTaunt.events[e] then OptiTaunt.events[e] = {} end						
						if not OptiTaunt.events[e][w] then OptiTaunt.events[e][w] = {} end
						for _,o in pairs(options) do
							OptiTaunt.events[e][w][o] = v[o]
						end						
					end
				end
			
			
			
			
		end
	end

end


function OptiTaunt:GetUnitID(dstGUID)
	local realUnitID
	local units = {"player","pet","vehicle","target","focus","mouseover",}
	local suffix = {"","target","targettarget",}
	
	for _,u in pairs(units) do				--basic UnitIds
		for _,s in pairs(suffix) do
			local unitid = u..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
	end
	for i=1,5 do							--Arena
		for _,s in pairs(suffix) do
			local unitid = "arena"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
	end
	for i=1,4 do							--Boss
		local unitid = "boss"..i
		if (dstGUID == UnitGUID(unitid)) then
			realUnitID = unitid
		end
	end
	for i=1,GetNumSubgroupMembers() do		--Party
		for _,s in pairs(suffix) do
			local unitid = "party"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
		for _,s in pairs(suffix) do			--Party Pets
			local unitid = "partypet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
	end
	for i=1,GetNumGroupMembers() do		--Raid
		for _,s in pairs(suffix) do
			local unitid = "raid"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
		for _,s in pairs(suffix) do			--Raid Pets
			local unitid = "raidpet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				realUnitID = unitid
			end
		end
	end
	
	return realUnitID
	
end

function OptiTaunt:GetDstInfo(dstGUID, auraType, spellName)
	local func, duration, RaidTargetIndex
	local units = {"player","pet","vehicle","target","focus","mouseover",}
	local suffix = {"","target","targettarget",}
	
	local function aura(unitid, spellName)
		if not auraType or auraType == nil then
			return
		end
		if auraType == "BUFF" then
			func = UnitBuff
		elseif auraType == "DEBUFF" then
			func = UnitDebuff
		end
		duration = tostring(select(6,func(unitid, spellName)))
		return duration
	end
	
	for _,u in pairs(units) do				--basic UnitIds
		for _,s in pairs(suffix) do
			local unitid = u..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
	end
	for i=1,5 do							--Arena
		for _,s in pairs(suffix) do
			local unitid = "arena"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
	end
	for i=1,4 do							--Boss
		local unitid = "boss"..i
		if (dstGUID == UnitGUID(unitid)) then
			aura(unitid, spellName)
			RaidTargetIndex = GetRaidTargetIndex(unitid)
		end
	end
	for i=1,GetNumSubgroupMembers() do		--Party
		for _,s in pairs(suffix) do
			local unitid = "party"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
		for _,s in pairs(suffix) do			--Party Pets
			local unitid = "partypet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
	end
	for i=1,GetNumGroupMembers() do		--Raid
		for _,s in pairs(suffix) do
			local unitid = "raid"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
		for _,s in pairs(suffix) do			--Raid Pets
			local unitid = "raidpet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				aura(unitid, spellName)
				RaidTargetIndex = GetRaidTargetIndex(unitid)
			end
		end
	end
	
	return duration, RaidTargetIndex
	
end

function OptiTaunt:GetDstSpellInfo(dstGUID)
	local func, extraSpellName
	local units = {"player","pet","vehicle","target","focus","mouseover",}
	local suffix = {"","target","targettarget",}
	
	for _,u in pairs(units) do				--basic UnitIds
		for _,s in pairs(suffix) do
			local unitid = u..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
	end
	for i=1,5 do							--Arena
		for _,s in pairs(suffix) do
			local unitid = "arena"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
	end
	for i=1,4 do							--Boss
		local unitid = "boss"..i
		if (dstGUID == UnitGUID(unitid)) then
			extraSpellName = UnitCastingInfo(unitid)
		end
	end
	for i=1,GetNumSubgroupMembers() do		--Party
		for _,s in pairs(suffix) do
			local unitid = "party"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
		for _,s in pairs(suffix) do			--Party Pets
			local unitid = "partypet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
	end
	for i=1,GetNumGroupMembers() do		--Raid
		for _,s in pairs(suffix) do
			local unitid = "raid"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
		for _,s in pairs(suffix) do			--Raid Pets
			local unitid = "raidpet"..i..s
			if (dstGUID == UnitGUID(unitid)) then
				extraSpellName = UnitCastingInfo(unitid)
			end
		end
	end
	
	return extraSpellName
	
end

function OptiTaunt:TestSpellDB()
	local default_abilitiy_output = {}
	for k,v in pairs(OptiTaunt_PlayerSpellDB[self.playerclass]) do
		print(k..": ")
		print(v)
		for l,w in pairs(v) do
			print(l..": ")
			print(w)
			if not table_contains(default_abilitiy_output, w.SpellName) then
				default_abilitiy_output[w.SpellName] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				}
			end
		end
	end
end


function OptiTaunt:AddAbility(spellId,category)
	--	/run OptiTaunt:AddAbility({6673})
	
	if type(spellId) == "table" then
		--ok
	elseif type(spellId) == "number" then
		spellId = {spellId}
	elseif type(spellId) == "string" and type(tonumber(spellId)) == "number" then
		spellId = {tonumber(spellId)}
	else
		print("|cffff6d00OptiTaunt|r: spellID is not matching requirements")
		return
	end
	
	local spellName = GetSpellInfo(spellId[1])
	
	if not OptiTaunt.db.profile.abilities[spellName] or OptiTaunt.db.profile.abilities[spellName] == nil then
		OptiTaunt.db.profile.abilities[spellName] = {
				["SpellName"] = spellName,
				["SpellID"] = spellId,
				["CD"] = 0,
				["Duration"] = 0,
			--	["Category"] = "Ability",
				["output"] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				},
				["event"] = {
					SPELL_AURA_APPLIED = false,
					SPELL_AURA_REMOVED = false,
					SPELL_CAST_SUCCESS = false,
					SPELL_INTERRUPT = false,
					SPELL_MISSED = false,
					SPELL_SUMMON = false,
					SPELL_UPDATE_COOLDOWN = false,
				},
				["announce"] = {},
		}
	end
	
	if not table_contains(OptiTaunt.ability_list, spellName) then
		table.insert(OptiTaunt.ability_list, spellName)
	end
	
	if category ~= nil then
		OptiTaunt.db.profile.abilities[spellName]["Category"] = category
		OptiTaunt:SetCategory(spellName, category)
	end
	
	OptiTaunt:CreateAbilityOutputSettings(self.ability_list, "ability_options")
	
end

function OptiTaunt:AddDebuff(spellId,category)
	--	/run OptiTaunt:AddAbility({6673})
	
	if type(spellId) == "table" then
		--ok
	elseif type(spellId) == "number" then
		spellId = {spellId}
	elseif type(spellId) == "string" and type(tonumber(spellId)) == "number" then
		spellId = {tonumber(spellId)}
	else
		print("|cffff6d00OptiTaunt|r: spellID is not matching requirements")
		return
	end
	
	local spellName = GetSpellInfo(spellId[1])
	
	if not OptiTaunt.db.profile.abilities[spellName] or OptiTaunt.db.profile.abilities[spellName] == nil then
		OptiTaunt.db.profile.abilities[spellName] = {
				["SpellName"] = spellName,
				["SpellID"] = spellId,
				["CD"] = 0,
				["Duration"] = 0,
			--	["Category"] = "Ability",
				["output"] = {
					raid = true,
					say = false,
					whisper = false,
					raidwarning = false,
					channel = {},
				},
				["event"] = {
					SPELL_AURA_APPLIED = false,
					SPELL_AURA_REMOVED = false,
					SPELL_CAST_SUCCESS = false,
					SPELL_INTERRUPT = false,
					SPELL_MISSED = false,
					SPELL_SUMMON = false,
					SPELL_UPDATE_COOLDOWN = false,
				},
				["announce"] = {},
		}
	end
	
	if not table_contains(OptiTaunt.ability_list, spellName) then
		table.insert(OptiTaunt.ability_list, spellName)
	end
	
	if category ~= nil then
		OptiTaunt.db.profile.abilities[spellName]["Category"] = category
		OptiTaunt:SetCategory(spellName, category)
	end
	
	OptiTaunt:CreateOutputSettings()
	
end

function OptiTaunt:SetCategory(name, category)
	--print(name) print(category)
	
	OptiTaunt.db.profile.abilities[name].Category = category
	
	local ev = {
		SPELL_AURA_APPLIED = false,
		SPELL_AURA_REMOVED = false,
		SPELL_CAST_SUCCESS = false,
		SPELL_INTERRUPT = false,
		SPELL_MISSED = false,
		SPELL_SUMMON = false,
		SPELL_UPDATE_COOLDOWN = false,
	}
	
	if category == "Ability" then
		ev.SPELL_CAST_SUCCESS = true
	elseif category == "Aura" then
		ev.SPELL_AURA_APPLIED = true
		ev.SPELL_AURA_REMOVED = true
	elseif category == "CD" then
		ev.SPELL_UPDATE_COOLDOWN = true
	elseif category == "Interrupt" then
		ev.SPELL_INTERRUPT = true
		ev.SPELL_MISSED = true
	elseif category == "Summon" then
		ev.SPELL_SUMMON = true
	elseif category == "Taunt" then
		ev.SPELL_MISSED = true
	elseif category == "Timer" then
		ev.SPELL_CAST_SUCCESS = true
		ev["  TIMER via duration"] = true
	elseif category == "Totem" then
		ev.PLAYER_TOTEM_UPDATE = true
	elseif category == "Trinket" then
		ev.SPELL_AURA_APPLIED = true
		ev.SPELL_AURA_REMOVED = true
	else
		ev.SPELL_CAST_SUCCESS = true
	end
	
	OptiTaunt.db.profile.abilities[name].event = ev
	
end

