--[[
	KillingSpree authored by Douglas C (chexsum) <chexsum@gmail.com>

	Announces consecutive killing blows. 

]]
KillingSpree = Rock:NewAddon("KillingSpree", "LibRockDB-1.0", "LibRockComm-1.0", "LibRockHook-1.0", "LibRockEvent-1.0", "LibRockTimer-1.0", "LibRockConfig-1.0", "LibRockConsole-1.0", "LibFeedback-1.0", "LibFuBarPlugin-3.0")
local KillingSpree, self = KillingSpree, KillingSpree

local _G = _G
local bit = _G.bit
local time = _G.time
local math = _G.math
local string = _G.string
local pairs = _G.pairs
local ipairs = _G.ipairs
local select = _G.select
local tonumber = _G.tonumber
local PlaySound = _G.PlaySound
local PlaySoundFile = _G.PlaySoundFile
local GetBuildInfo = _G.GetBuildInfo
local IsInInstance = _G.IsInInstance
local IsAddOnLoaded = _G.IsAddOnLoaded
local LowHealthFrame = _G.LowHealthFrame
local UIFrameFlash = _G.UIFrameFlash
local UIParent = _G.UIParent
local UnitSex = _G.UnitSex
local UnitName = _G.UnitName
local UnitFactionGroup = _G.UnitFactionGroup
local GetNumRaidMembers = _G.GetNumRaidMembers
local GetNumPartyMembers = _G.GetNumPartyMembers
local GetCorpseRecoveryDelay = _G.GetCorpseRecoveryDelay

local STRING_COLOR_BLACK = string.format("|cff%02x%02x%02x", 0, 0, 0)
local STRING_COLOR_WHITE = string.format("|cff%02x%02x%02x", 255, 255, 255)
local STRING_COLOR_YELLOW= string.format("|cff%02x%02x%02x", 255, 255, 0)
local STRING_COLOR_ORANGE = string.format("|cff%02x%02x%02x", 255, 128, 0)
local STRING_COLOR_CRIMSON = string.format("|cff%02x%02x%02x", 255, 0, 0)
local STRING_COLOR_MAROON = string.format("|cff%02x%02x%02x", 128, 0, 0)
local STRING_COLOR_GREEN = string.format("|cff%02x%02x%02x", 0, 255, 0)
local STRING_COLOR_BLUE = string.format("|cff%02x%02x%02x", 0, 0, 255)

KillingSpree:SetCommPrefix("KS2")
KillingSpree:SetDatabase('KillingSpreeDB', 'KillingSpreePCDB')
KillingSpree:SetConfigSlashCommand('/KILLINGSPREE', '/KS')
KillingSpree:SetFuBarOption('iconPath', "Interface\\AddOns\\KillingSpree\\icon")
--KillingSpree:SetFuBarOption('hasNoColor', true)

function KillingSpree:OnInitialize()
	self.guidregistry = LibStub("LibGUIDRegistry-0.1", true)
	self.sharedmedia = LibStub("LibSharedMedia-3.0", true)
	self.scheme = {}
	self.schemes = {
		"UT2k3",
		"UT2k4Male",
		"UT2k4Male2",
		"UT2k4Female",
		"UT2k4Female2"
	}
	self.taunts = {
		[1] = {"bam.wav", "BAM!"},
		[2] = {"chickenlol.wav", "LOL /chicken!"},
		[3] = {"crap.wav", "Aww crap, you died!"},
		[4] = {"danceallday.wav", "I can dance all day!"},
		[5] = {"doh.wav", "Doh, you died!"},
		[6] = {"fuckinlag.wav", "Fuckin lag!"},
		[7] = {"haha.wav", "Ha ha, you died!"},
		[8] = {"hahanoobs", "Ha ha, noobs!"},
		[9] = {"headshot.wav", "Boom, headshot!"},
		[10] = {"headshotslow.wav", "Boooom, headshot!"},
		[11] = {"ious.wav", "Its official, you suck!"},
		[12] = {"lawl.wav", "LOL, you died!"},
		[13] = {"noob.wav", "Noob, you died!"},
		[14] = {"nothing.wav", "You own nothing!"},
		[15] = {"pathetic.wav", "That was pathetic!"},
		[16] = {"slap.wav", "/slap, you died!"},
		[17] = {"weak.wav", "You weak pathetic fool!"},
		[18] = {"yeah.wav", "Oh yeah, you died!"},
		[19] = {"youarenothing.wav", "You are nothing!"},
		[20] = {"youlikethat.wav", "You like that, dont you!"},
	}
	for scheme = 1, #self.schemes do
		self.scheme[self.schemes[scheme]] = {
			['firstblood'] = { "firstblood.wav", "First Blood!" },
			['headshot'] = { "headshot.wav", "Headshot!" },
			['youhavelost'] = { "youhavelost.wav", "You have lost the match!" },
			['youhavewon'] = { "youhavewon.wav", "You have won the match!" },
			['multi'] = {
				[2] = { "doublekill.wav", "Double Kill!" },
				[3] = { "multikill.wav", "Multi Kill!" },
				[4] = { "megakill.wav", "Mega Kill!" },
				[5] = { "ultrakill.wav", "Ultra Kill!" },
				[6] = { "monsterkill.wav", "Monster Kill!" },
			},
			['spree'] = {
				[10] = { "killingspree.wav", "Killing Spree!" },
				[15] = { "rampage.wav", "Rampage!" },
				[20] = { "dominating.wav", "Dominating!" },
				[25] = { "unstoppable.wav", "Unstoppable!" },
				[30] = { "godlike.wav", "God-like!" },
			},
			['count'] = {
				[1] = { "1.wav", "One!" },
				[2] = { "2.wav", "Two!" },
				[3] = { "3.wav", "Three!" },
				[4] = { "4.wav", "Four!" },
				[5] = { "5.wav", "Five!" },
			},
		}
		if self.schemes[scheme] ~= "UT2k3" then
			self.scheme[self.schemes[scheme]]['humiliation'] = { "humiliation.wav", "Humiliating defeat!" }
			self.scheme[self.schemes[scheme]]['flawless'] = { "flawless.wav", "Flawless victory!" }
			self.scheme[self.schemes[scheme]]['multi'][7] = { "ludicrouskill.wav", "Ludicrous Kill!" }
			self.scheme[self.schemes[scheme]]['multi'][8] = { "holyshit.wav", "Holy Shit!" }
			self.scheme[self.schemes[scheme]]['spree'][35] = { "wickedsick.wav", "Wicked Sick!" }
		end
	end
	if UnitSex('player') == 3 then
		self.defaultscheme = "UT2k4Female"
	else
		self.defaultscheme = "UT2k4Male"
	end
	self:SetDatabaseDefaults('profile', {
		scheme = self.defaultscheme,
		showIcon = true,
		showText = true,
		suppress = true,
		broadcast = true,
		messages = true,
		visuals = true,
		sounds = true,
		taunts = true,
		group = false,
		slain = false,
		pvp = false,
		level = false,
		death = false,
		instance = false,
		overall = 3000,
		spree = 300,
		multi = 60,
		xoffset = 0,
		yoffset = 150,
		locked = true,
		frame = "feedback",
		fontsize = "22",
		fonttype = "Arial Narrow",
		fontoutline = "OUTLINE",
		fontcolor = {
			r = 1.0,
			b = 1.0,
			g = 1.0,
			a = 1.0,
		},
	})
	self:SetDatabaseDefaults('char', {
		kills = {
			multi = 0,
			spree = 0,
			overall = 0,
		},
	})
	self:SetConfigTable({
		type = 'group',
		name = "Killing Spree",
		desc = "Consecutive kill announcer",
		icon = "Interface\\AddOns\\KillingSpree\\icon",
		args = {
			header0 = {
				order = 0,
				type = 'header',
				name = "commands",
				hidden = function()
					return self.db.char.kills.multi + self.db.char.kills.spree + self.db.char.kills.overall == 0
				end,
			},
			record = {
				order = 1,
				type = 'execute',
				name = "Display records",
				desc = "Displays the multi, spree and overall records",
				func = function()
					self:Print(string.format("multi record: %s.", self.db.char.kills.multi))
					self:Print(string.format("spree record: %s.", self.db.char.kills.spree))
					self:Print(string.format("overall record: %s.", self.db.char.kills.overall))
				end,
				hidden = function()
					return self.db.char.kills.multi + self.db.char.kills.spree + self.db.char.kills.overall == 0
				end,
			},
			reset = {
				order = 2,
				type = 'execute',
				name = "Reset records",
				desc = "Resets the multi, spree and overall records",
				func = function()
					self.db.char.kills.multi, self.db.char.kills.spree, self.db.char.kills.overall = 0, 0, 0
				end,
				hidden = function()
					return self.db.char.kills.multi + self.db.char.kills.spree + self.db.char.kills.overall == 0
				end,
			},
			header1 = {
				order = 100,
				type = 'header',
				name = "data options",
			},
			scheme = {
				order = 101,
				type = 'choice',
				name = "Sound scheme",
				desc = "Selects the sound scheme",
				get = function()
					return self.db.profile.scheme
				end,
				set = function(scheme)
					self.db.profile.scheme = scheme
					self:Announce("New sound scheme selected", string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['spree'][10][1]))
				end,
				choices = self.schemes,
			},
			multi = {
				order = 102,
				type = 'number',
				name = "Multi timer",
				desc = "Sets the multi kill timer",
				get = function()
					return self.db.profile.multi
				end,
				set = function(multi)
					if multi ~= 0 and (multi > self.db.profile.spree or multi > self.db.profile.spree) then
						self:Print("multi timer must be less than spree timer and overall timer or 0")
					else
						self.db.profile.multi = multi
						self:UpdateVictims()
					end
				end,
				min = 0,
				max = 60,
				step = 1,
			},
			spree = {
				order = 103,
				type = 'number',
				name = "Spree timer",
				desc = "Sets the spree kill timer",
				get = function()
					return self.db.profile.spree
				end,
				set = function(spree)
					if spree ~= 0 and (spree < self.db.profile.multi or spree > self.db.profile.overall) then
						self:Print("spree timer must be greater than multi timer and less than overall timer or 0")
					else
						self.db.profile.spree = spree
						self:UpdateVictims()
					end
				end,
				min = 0,
				max = 600,
				step = 10,
			},
			overall = {
				order = 104,
				type = 'number',
				name = "Overall timer",
				desc = "Sets the overall kill timer",
				get = function()
					return self.db.profile.overall
				end,
				set = function(overall)
					if overall ~= 0 and (overall < self.db.profile.multi or overall < self.db.profile.spree) then
						self:Print("spree timer must be greater than multi timer and spree timer or 0")
					else
						self.db.profile.overall = overall
						self:UpdateVictims()
					end
				end,
				min = 0,
				max = 6000,
				step = 100,
			},
			death = {
				order = 105,
				type = 'boolean',
				name = "Death reset",
				desc = "Reset the kills upon death",
				get = function()
					return self.db.profile.death
				end,
				set = function(death)
					self.db.profile.death = death
				end,
			},
			instance = {
				order = 106,
				type = 'boolean',
				name = "Instance reset",
				desc = "Reset the kills upon instance change",
				get = function()
					return self.db.profile.instance
				end,
				set = function(instance)
					self.db.profile.instance = instance
				end,
			},
			suppress = {
				order = 107,
				type = 'boolean',
				name = "Suppress repeated messages",
				desc = "Toggles repeated messages",
				get = function()
					return self.db.profile.suppress
				end,
				set = function(suppress)
					self.db.profile.suppress = suppress
				end,
			},
			broadcast = {
				order = 108,
				type = 'boolean',
				name = "Broadcast messages",
				desc = "Toggles broadcasting messages",
				get = function()
					return self.db.profile.broadcast
				end,
				set = function(broadcast)
					self.db.profile.broadcast = broadcast
				end,
			},
			messages = {
				order = 109,
				type = 'boolean',
				name = "Show messages",
				desc = "Toggles displaying messages",
				get = function()
					return self.db.profile.messages
				end,
				set = function(messages)
					self.db.profile.messages = messages
				end,
			},
			sounds = {
				order = 110,
				type = 'boolean',
				name = "Play sounds",
				desc = "Toggles playing sounds",
				get = function()
					return self.db.profile.sounds
				end,
				set = function(sounds)
					self.db.profile.sounds = sounds
				end,
			},
			visuals = {
				order = 111,
				type = 'boolean',
				name = "Show visuals",
				desc = "Toggles visual effects",
				get = function()
					return self.db.profile.visuals
				end,
				set = function(visuals)
					self.db.profile.visuals = visuals
				end,
			},
			taunts = {
				order = 112,
				type = 'boolean',
				name = "Enable taunts",
				desc = "Toggles death taunts",
				get = function()
					return self.db.profile.taunts
				end,
				set = function(taunts)
					self.db.profile.taunts = taunts
				end,
			},
			group = {
				order = 113,
				type = 'boolean',
				name = "Grouped check",
				desc = "Toggles grouped check",
				get = function()
					return self.db.profile.group
				end,
				set = function(group)
					self.db.profile.group = group
					self:UpdateVictims()
				end,
			},
			slain = {
				order = 114,
				type = 'boolean',
				name = "Slain check",
				desc = "Toggles slain by check",
				get = function()
					return self.db.profile.slain
				end,
				set = function(slain)
					self.db.profile.slain = slain
					self:UpdateVictims()
				end,
			},
			pvp = {
				order = 115,
				type = 'boolean',
				name = "PvP check",
				desc = "Toggles PvP check",
				get = function()
					return self.db.profile.pvp
				end,
				set = function(pvp)
					self.db.profile.pvp = pvp
					self:UpdateVictims()
				end,
			},
			level = {
				order = 116,
				type = 'boolean',
				name = "Level check",
				desc = "Toggles level check",
				get = function()
					return self.db.profile.level
				end,
				set = function(level)
					self.db.profile.level = level
					self:UpdateVictims()
				end,
			},
			locked = {
				order = 198,
				type = 'boolean',
				name = "Lock frame",
				desc = "Locks the message frame",
				get = function()
					return self.db.profile.locked
				end,
				set = function()
					self.db.profile.locked = not self.db.profile.locked
					if self.db.profile.locked then
						self:LockFeedback("Notification")
					else
						self:UnlockFeedback("Notification")
					end
				end,
				hidden = function()
					return self.db.profile.frame ~= "feedback"
				end,
			},
			frame = {
				order = 199,
				type = 'choice',
				name = "Message frame",
				desc = "Choose the message frame to use",
				get = function()
					return self.db.profile.frame
				end,
				set = function(frame)
					if (frame == "sct" and IsAddOnLoaded("sct")) or (frame == "msbt" and IsAddOnLoaded("MikScrollingBattleText")) or (frame == "parrot" and IsAddOnLoaded("Parrot")) or (frame == "blizzard" and SHOW_COMBAT_TEXT == "1") or frame == "feedback" then
						self.db.profile.frame = frame
					end
				end,
				choices = {
					["feedback"] = "Feedback",
					["blizzard"] = "Blizzard",
					["parrot"] = "Parrot",
					["msbt"] = "MSBT",
					["sct"] = "SCT",
				},
				hidden = function()
					return not IsAddOnLoaded("MikScrollingBattleText") and not IsAddOnLoaded("Parrot") and not IsAddOnLoaded("sct") and SHOW_COMBAT_TEXT ~= "1"
				end,
			},
			header2 = {
				order = 200,
				type = 'header',
				hidden = function()
					return self.db.profile.frame ~= "feedback"
				end,
			},
			fonttype = {
				order = 201,
				type = 'choice',
				name = "Font type",
				desc = "Changes the message font type",
				get = function()
					return self.db.profile.fonttype
				end,
				set = function(fonttype)
					self.db.profile.fonttype = fonttype
					self:Announce("New font type selected")
				end,
				choices = self.sharedmedia:List('font'),
				hidden = function()
					return self.db.profile.frame ~= "feedback"
				end,
			},
			fontsize = {
				order = 202,
				type = 'choice',
				name = "Font size",
				desc = "Changes the message font size",
				get = function()
					return tostring(tonumber(self.db.profile.fontsize))
				end,
				set = function(fontsize)
					self.db.profile.fontsize = tostring(tonumber(fontsize))
					self:Announce("New font size selected")
				end,
				choices = {
					["18"] = "Tiny",
					["22"] = "Small",
					["26"] = "Medium",
					["30"] = "Large",
					["34"] = "Huge",
				},
				hidden = function()
					return self.db.profile.frame ~= "feedback"
				end,
			},
			fontoutline = {
				order = 203,
				type = 'choice',
				name = "Font outline",
				desc = "Changes the message font outline",
				get = function()
					return self.db.profile.fontoutline
				end,
				set = function(fontoutline)
					self.db.profile.fontoutline = fontoutline
					self:Announce("New font outline selected")
				end,
				choices = {
					[""] = "None",
					["OUTLINE"] = "Thin",
					["THICKOUTLINE"] = "Thick",
				},
				hidden = function()
					return self.db.profile.frame ~= "feedback"
				end,
			},
			fontcolor = {
				order = 204,
				type = 'color',
				name = "Font color",
				desc = "Changes the message font color",
				get = function()
					return self.db.profile.fontcolor.r, self.db.profile.fontcolor.g, self.db.profile.fontcolor.b, self.db.profile.fontcolor.a
				end,
				set = function(red, green, blue, alpha)
					self.db.profile.fontcolor = {
						r = red,
						g = green,
						b = blue,
						a = alpha or 1.0,
					}
					self:Announce("New font color selected")
				end,
				hasAlpha = function()
					return self.db.profile.frame == "feedback"
				end,
			},
		},
	})
end

function KillingSpree:OnEnable()
	self:CreateFeedback("Notification", { xoffset = self.db.profile.xoffset, yoffset = self.db.profile.yoffset })
	self:SetVariables()
	self.users = {}
	self.dead = false
	self.grouped = false
	self.instance = select(2, IsInInstance())
	self.factiongroup = UnitFactionGroup('player')
	self:AddCallback("OnFeedbackFrameMoved")
	self:AddCallback("OnFeedbackFrameLocked")
	self:AddCallback("OnFeedbackFrameUnlocked")
	self:AddCommListener("KS2", "GROUP", "OnCommReceive")
	self:AddEventListener("PLAYER_DEAD", "PlayerDead")
	self:AddEventListener("PLAYER_ALIVE", "PlayerDead")
	self:AddEventListener("PLAYER_UNGHOST", "PlayerDead")
	self:AddEventListener("RAID_ROSTER_UPDATE", "UpdateUsers")
	self:AddEventListener("PARTY_MEMBERS_CHANGED", "UpdateUsers")
	self:AddEventListener("ZONE_CHANGED_NEW_AREA", "UpdateInstance")
	self:AddEventListener("CHAT_MSG_BG_SYSTEM_NEUTRAL", "BattleGroundEvent")
	self:AddEventListener("COMBAT_LOG_EVENT_UNFILTERED", "CombatLogEvent")
end

function KillingSpree:OnFeedbackFrameMoved(event, frame, xoffset, yoffset)
	self.db.profile.xoffset, self.db.profile.yoffset = xoffset, yoffset
end

function KillingSpree:OnFeedbackFrameLocked(event, frame)
	self.db.profile.locked = true
end

function KillingSpree:OnFeedbackFrameUnlocked(event, frame)
	self.db.profile.locked = false
end

function KillingSpree:OnUpdateFuBarText()
	local text = ""
	if self.db.profile.multi > 0 then
		text = text .. STRING_COLOR_ORANGE .. self.multi .. STRING_COLOR_CRIMSON .. ":"
	end
	if self.db.profile.spree > 0 then
		text = text .. STRING_COLOR_YELLOW .. self.spree .. STRING_COLOR_CRIMSON .. ":"
	end
	if self.db.profile.overall > 0 then
		text =  text .. STRING_COLOR_WHITE .. self.overall
	end
	self:SetFuBarText(text)
end

function KillingSpree:OnUpdateFuBarTooltip()
	GameTooltip:AddLine(" Killing Spree ", 1, 0, 0)
	if self.db.profile.multi + self.db.profile.spree + self.db.profile.overall > 0 then
		GameTooltip:AddLine(" ", 1, 0, 0)
		GameTooltip:AddLine("   Records:  ", 1, 0, 0)
		if self.db.profile.multi > 0 then
			GameTooltip:AddDoubleLine(string.format("     %s     ", "Multi kills"), string.format("     %d    ", self.db.char.kills.multi), 1, 0.5, 0, 1, 0.5, 0)
		end
		if self.db.profile.spree > 0 then
			GameTooltip:AddDoubleLine(string.format("     %s     ", "Spree kills"), string.format("     %d    ", self.db.char.kills.spree), 1, 1, 0, 1, 1, 0)
		end
		if self.db.profile.overall > 0 then
			GameTooltip:AddDoubleLine(string.format("     %s     ", "Overall kills"), string.format("     %d    ", self.db.char.kills.overall), 1, 1, 1, 1, 1, 1)
		end
	end
	if self.multi + self.spree + self.overall > 0 then
		GameTooltip:AddLine(" ", 1, 0, 0)
		GameTooltip:AddLine("   Victims:  ", 1, 0, 0)
		for victim = self.kills, 1, -1 do
			if self.victims[victim] and not ((self.db.profile.slain and not self.victims[victim].slayer == "You") or (self.db.profile.group and not self.victims[victim].group) or (self.db.profile.pvp and not self.victims[victim].player) or (self.db.profile.level and self.victims[victim].trivial)) then
				local difference = time() - self.victims[victim].tod
				if self.db.profile.multi > 0 and difference < self.db.profile.multi then
					if not self.db.profile.slain then
						GameTooltip:AddDoubleLine(string.format("     %s     ", self.victims[victim].name), string.format("     %s     ", self.victims[victim].slayer), 1, 0.5, 0, 1, 0.5, 0);
					else
						GameTooltip:AddLine(string.format("     %s     ", self.victims[victim].name), 1, 0.5, 0)
					end
				elseif self.db.profile.spree > 0 and difference < self.db.profile.spree then
					if not self.db.profile.slain then
						GameTooltip:AddDoubleLine(string.format("     %s     ", self.victims[victim].name), string.format("     %s     ", self.victims[victim].slayer), 1, 1, 0, 1, 1, 0);
					else
						GameTooltip:AddLine(string.format("     %s     ", self.victims[victim].name), 1, 1, 0)
					end
				elseif self.db.profile.overall > 0 and difference < self.db.profile.overall then
					if not self.db.profile.slain then
						GameTooltip:AddDoubleLine(string.format("     %s     ", self.victims[victim].name), string.format("     %s     ", self.victims[victim].slayer), 1, 1, 1, 1, 1, 1);
					else
						GameTooltip:AddLine(string.format("     %s     ", self.victims[victim].name), 1, 1, 1)
					end
				end
			end
		end
	end
end

function KillingSpree:OnCommReceive(prefix, distribution, sender, message)
	if self.db.profile.group and not self.grouped then return end
	if prefix == "KS2" and sender ~= UnitName('player') then
		if type(message) == 'string' and message == "HELLO" and not self.users[sender] then
			self.users[sender] = true
			self:SendCommMessage("GROUP", "HELLO")
		elseif type(message) == 'table' and message.spree then
			self:Announce(string.format("%s is on a killing spree [x%s]", sender, message.spree))
		end
	end
end

function KillingSpree:CombatLogEvent(caller, event, timestamp, eventType, sourceGUID, sourceName, sourceFlags, destinationGUID, destinationName, destinationFlags, ...)
	if (eventType == "PARTY_KILL" or eventType == "UNIT_DEAD" or eventType == "UNIT_DESTROYED") and not ((bit.band(destinationFlags, COMBATLOG_OBJECT_TYPE_GUARDIAN) == COMBATLOG_OBJECT_TYPE_GUARDIAN) or (bit.band(destinationFlags, COMBATLOG_OBJECT_TYPE_OBJECT) == COMBATLOG_OBJECT_TYPE_OBJECT)or (bit.band(destinationFlags, COMBATLOG_OBJECT_TYPE_PET) == COMBATLOG_OBJECT_TYPE_PET)) then
		self:EnemyDead(eventType, sourceGUID, sourceName, sourceFlags, destinationGUID, destinationName, destinationFlags)
	end
end

function KillingSpree:BattleGroundEvent(caller, event, message)
	if self.db.profile.group and not self.grouped then return end
	local bgmessage, bgvictory0, bgvictory1, bgstart = string.lower(message), string.lower(VICTORY_TEXT0), string.lower(VICTORY_TEXT1)
	if (self.factiongroup == "Alliance" and string.find(bgmessage, bgvictory1)) or (self.factiongroup == "Horde" and string.find(bgmessage, bgvictory0)) then
		self:Announce(string.format("%s", self.scheme[self.db.profile.scheme]['youhavewon'][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['youhavewon'][1]))
	elseif (self.factiongroup == "Alliance" and string.find(bgmessage, bgvictory0)) or (self.factiongroup == "Horde" and string.find(bgmessage, bgvictory1)) then
		self:Announce(string.format("%s", self.scheme[self.db.profile.scheme]['youhavelost'][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['youhavelost'][1]))
	elseif string.find(bgmessage, "1 minute") or string.find(bgmessage, "one minute") then
		bgstart = 61
	elseif string.find(bgmessage, "30 seconds") or string.find(bgmessage, "thirty seconds") then
		bgstart = 31
	elseif string.find(bgmessage, "15 seconds") or string.find(bgmessage, "fifteen seconds") then
		bgstart = 16
	end
	if bgstart then
		self:AddTimer("COUNTDOWN:5", bgstart - 5, "Announce", string.format("%s", self.scheme[self.db.profile.scheme]['count'][5][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['count'][5][1]))
		self:AddTimer("COUNTDOWN:4", bgstart - 4, "Announce", string.format("%s", self.scheme[self.db.profile.scheme]['count'][4][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['count'][4][1]))
		self:AddTimer("COUNTDOWN:3", bgstart - 3, "Announce", string.format("%s", self.scheme[self.db.profile.scheme]['count'][3][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['count'][3][1]))
		self:AddTimer("COUNTDOWN:2", bgstart - 2, "Announce", string.format("%s", self.scheme[self.db.profile.scheme]['count'][2][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['count'][2][1]))
		self:AddTimer("COUNTDOWN:1", bgstart - 1, "Announce", string.format("%s", self.scheme[self.db.profile.scheme]['count'][1][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['count'][1][1]))
	end
end

function KillingSpree:PlayerDead(caller, event)
	if self.db.profile.death then self:SetVariables() end
	if self.db.profile.group and not self.grouped then return end
	if event == "PLAYER_ALIVE" or event == "PLAYER_UNGHOST" then
		self.dead = false
	elseif event == "PLAYER_DEAD" and not self.dead then
		local taunt = math.random(1, #self.taunts)
		self.dead = true
		if taunt and self.db.profile.taunts then
			self:Announce(string.format("%s", self.taunts[taunt][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\Taunts\\%s", self.taunts[taunt][1]))
		end
	end
end

function KillingSpree:EnemyDead(eventType, sourceGUID, sourceName, sourceFlags, destinationGUID, destinationName, destinationFlags)
	if sourceName and destinationName then
		self.kills = self.kills + 1
		self.victims[self.kills] = {}
		self.victims[self.kills].tod = time()
		self.victims[self.kills].name = destinationName
		self.victims[self.kills].slayer = (bit.band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_MINE) == COMBATLOG_OBJECT_AFFILIATION_MINE) and "You" or sourceName
		self.victims[self.kills].player = (bit.band(destinationFlags, COMBATLOG_OBJECT_CONTROL_PLAYER) == COMBATLOG_OBJECT_CONTROL_PLAYER)
		self.victims[self.kills].trivial = (UnitLevel('player') > (self.guidregistry:GetLevel(destinationGUID) or 0) + 3)
		self.victims[self.kills].group = self.grouped
		self:UpdateVictims()
		if not ((self.db.profile.slain and not self.victims[self.kills].slayer == "You") or (self.db.profile.group and not self.victims[self.kills].group) or (self.db.profile.pvp and not self.victims[self.kills].player) or (self.db.profile.level and self.victims[self.kills].trivial)) then
			if self.overall > 1 and self.scheme[self.db.profile.scheme]['spree'][self.spree] and (not self.db.profile.suppress or self.lastspree < self.spree) then
				self.lastspree = self.spree
				self.lastsound = self.scheme[self.db.profile.scheme]['spree'][self.spree][1]
				if self.db.profile.broadcast and (not self.db.profile.suppress or self.lastbroadcast < self.spree) then
					self.lastbroadcast = self.spree
					self:SendCommMessage("GROUP", { multi = self.multi, spree = self.spree, overall = self.overall })
				end
				self:Announce(string.format("%s [x%d]", self.scheme[self.db.profile.scheme]['spree'][self.spree][2], self.spree), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['spree'][self.spree][1]), true)
			elseif self.overall > 1 and self.scheme[self.db.profile.scheme]['multi'][self.multi] and (not self.db.profile.suppress or self.lastmulti < self.multi) then
				self.lastmulti = self.multi
				self.lastsound = self.scheme[self.db.profile.scheme]['multi'][self.multi][1]
				self:Announce(string.format("%s [x%d]", self.scheme[self.db.profile.scheme]['multi'][self.multi][2], self.multi), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['multi'][self.multi][1]), true)
			elseif self.overall == 1 and self.scheme[self.db.profile.scheme]['firstblood'] then
				self.lastsound = self.scheme[self.db.profile.scheme]['firstblood']
				self:Announce(string.format("%s", self.scheme[self.db.profile.scheme]['firstblood'][2]), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['firstblood'][1]), true)
			elseif self.scheme[self.db.profile.scheme]['headshot'] and (not self.db.profile.suppress and self.lastsound ~= self.scheme[self.db.profile.scheme]['headshot']) then
				self.lastsound = self.scheme[self.db.profile.scheme]['headshot']
				self:Announce(string.format("%s [x%s]", self.scheme[self.db.profile.scheme]['headshot'][2], self.overall), string.format("Interface\\AddOns\\KillingSpree\\snds\\%s\\%s", self.db.profile.scheme, self.scheme[self.db.profile.scheme]['headshot'][1]), true)
			else
				self:Announce(string.format("Kill! [x%s]", self.overall), "Sound\\Doodad\\BellTollTribal.wav", true)
			end
		end
	end
end

function KillingSpree:Announce(message, soundfile, func)
	if message and self.db.profile.messages then
		if self.db.profile.frame == "sct" and IsAddOnLoaded("sct") then
			SCT:DisplayMessage(message, self.db.profile.fontcolor)
		elseif self.db.profile.frame == "parrot" and IsAddOnLoaded("Parrot") then
			Parrot:ShowMessage(message, "Notification", false, self.db.profile.fontcolor.r, self.db.profile.fontcolor.g, self.db.profile.fontcolor.b)
		elseif self.db.profile.frame == "msbt" and IsAddOnLoaded("MikScrollingBattleText") then
			MikSBT.DisplayMessage(message, MikSBT.DISPLAYTYPE_NOTIFICATION, false, self.db.profile.fontcolor.r, self.db.profile.fontcolor.g, self.db.profile.fontcolor.b)
		elseif self.db.profile.frame == "blizzard" and SHOW_COMBAT_TEXT == "1" then
			CombatText_AddMessage(message, COMBAT_TEXT_SCROLL_FUNCTION, self.db.profile.fontcolor.r, self.db.profile.fontcolor.g, self.db.profile.fontcolor.b)
		else
			local data = {
				text = message,
				fonttype = self.sharedmedia:Fetch('font', self.db.profile.fonttype),
				fontsize = tonumber(self.db.profile.fontsize),
				fontcolor = self.db.profile.fontcolor,
				fontoutline = self.db.profile.fontoutline,
			}
			if soundfile and self.db.profile.sounds then
				data.soundfile = soundfile
			end
			if func and self.db.profile.visuals then
				data.func = function() UIFrameFlash(LowHealthFrame, 0.25, 0.25, 0.5, false, 0, 0) end
			end
			self:AddFeedbackData("Notification", data)
			return
		end
	end
	if soundfile and self.db.profile.sounds then
		PlaySoundFile(soundfile)
	end
	if func and self.db.profile.visuals then
		UIFrameFlash(LowHealthFrame, 0.25, 0.25, 0.5, false, 0, 0)
	end
end

function KillingSpree:UpdateVictims()
	local multi, spree, overall, refresh = 0, 0, 0
	for victim = self.kills, 1, -1 do
		if self.victims[victim] and not ((self.db.profile.slain and not self.victims[victim].slayer == "You") or (self.db.profile.group and not self.victims[victim].group) or (self.db.profile.pvp and not self.victims[victim].player) or (self.db.profile.level and self.victims[victim].trivial)) then
			local difference = time() - self.victims[victim].tod
			if self.db.profile.multi > 0 and difference < self.db.profile.multi then
				if not refresh or refresh > self.db.profile.multi - difference then
					refresh = self.db.profile.multi - difference
				end
				multi = multi + 1
			elseif self.db.profile.spree > 0 and difference < self.db.profile.spree then
				if not refresh or refresh >  self.db.profile.spree - difference then
					refresh = self.db.profile.spree - difference
				end
				spree = spree + 1
			elseif self.db.profile.overall > 0 and difference < self.db.profile.overall then
				if not refresh or refresh >  self.db.profile.overall - difference then
					refresh = self.db.profile.overall - difference
				end
				overall = overall + 1
			end
		end
	end
	if self.multi ~= multi or self.spree ~= spree + multi or self.overall ~= overall then
		self.overall = overall + spree + multi
		self.spree = spree + multi
		self.multi = multi
		self:UpdateFuBarPlugin()
		if self.multi > self.db.char.kills.multi then self.db.char.kills.multi = self.multi end
		if self.spree > self.db.char.kills.spree then self.db.char.kills.spree = self.spree end
		if self.overall > self.db.char.kills.overall then self.db.char.kills.overall = self.overall end
	end
	if refresh then
		self:RemoveTimer("REFRESH")
		self:AddTimer("REFRESH", refresh + 0.5, "UpdateVictims")
	end
end

function KillingSpree:UpdateUsers()
	if (GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0) and not self.grouped then
		self:SendCommMessage("GROUP", "HELLO")
		self.grouped = true
	elseif (GetNumRaidMembers() == 0 and GetNumPartyMembers() == 0) and self.grouped then
		self.grouped = false
		self.users = {}
	end
end

function KillingSpree:UpdateInstance()
	if self.dead then return end
	local instance = select(2, IsInInstance())
	if instance ~= self.instance then
		self.instance = instance
		if self.db.profile.instance then
			self:SetVariables()
		end
	end
end

function KillingSpree:SetVariables()
	self.spawned = time()
	self.victims = {}
	self.kills = 0
	self.multi = 0
	self.spree = 0
	self.overall = 0
	self.lastmulti = 0
	self.lastspree = 0
	self.lastsound = ""
	self.lastbroadcast = 0
	self:RemoveAllTimers()
	self:UpdateFuBarPlugin()
end
