--[[

	-----------------------------------
	nnCast (Announcer for caster) 0.6.1
	-----------------------------------
	캐스터용 알림이

--]]

nnCast = LibStub("AceAddon-3.0"):NewAddon("nnCast", "AceConsole-3.0", "AceEvent-3.0")

-------------------------------------------------------------------------------------
-- 전역 변수 (내부)
-------------------------------------------------------------------------------------
	local db
	local userAddon

	local buffList
	local debuffList
	local missedList
	local successList
	local messageList
	local targetDebuffList

	local trackedEvents = {}

-------------------------------------------------------------------------------------
-- 전역 상수 (내부)
-------------------------------------------------------------------------------------
	-------
	-- 표시
	-------
	local addonList = {
		["AUTO"] = "자동",
		["BCT"] = "BCT - Blizzard",
		["BCF"] = "BCF - Blink",
		["SCT"] = "SCT",
		["MSBT"] = "MSBT",
		["PARROT"] = "Parrot",
		["ERROR_FRAME"] = "오류창",
		["NONE"] = "없음"
	}

	-------
	-- 채널
	-------
	local channelList = {
		["SAY"] = "일반",
		["YELL"] = "외침",
		["PARTY"] = "파티",
		["RAID"] = "공격대",
		["RAID_WARNING"] = "공격대 경보",
		["USER_CHANNEL"] = "사용자 채널",
		["CHAT_FRAME"] = "대화창",
		["NONE"] = "없음"
	}

	-----------------
	-- 주문 실패 종류
	-----------------
	local missList = {
		["DODGE"]	= "피함",
		["ABSORB"]	= "흡수",
		["RESIST"]	= "저항함",
		["PARRY"]	= "막음",
		["MISS"]	= "빗맞힘",
		["BLOCK"]	= "방어함",
		["REFLECT"]	= "반사함",
		["DEFLECT"]	= "빗맞음",
		["IMMUNE"]	= "면역",
		["EVADE"]	= "빗나감",
	}

	---------------
	-- 기본 설정 값
	---------------
	local defaults = {
		profile = {
			onLoadClear = true,
			dispel = true,
			dispelEnemy = false,
			ccbreak = true,
			interrupt = true,
			success = true,
			missed = true,
			buff = true,
			debuff = true,
			targetdebuff = true,
			channel = {
				dispel = "PARTY",
				ccbreak = "PARTY",
				interrupt = "PARTY",
				hostileinterrupt = "PARTY",
				success = "PARTY",
				missed = "PARTY",
				buff = "PARTY",
				debuff = "PARTY",
				targetdebuff = "PARTY",
			},
			whisper = {
				dispel = "",
				ccbreak = "",
				interrupt = "",
				hostileinterrupt = "",
				success = "",
				missed = "",
				buff = "",
				debuff = "",
				targetdebuff = "",
			},
			userChannel = "",
			addon = "AUTO",
			useChatFrame = true,
		},
	}

	---------------
	-- 외부 UI 관련
	---------------
	local UI_NOTIFICATION	= 0
	local UI_INCOMING		= 1
	local UI_OUTGOING		= 2

	local color = {
		default	= { r = 1.0, g = 1.0, b = 1.0 },
		good	= { r = 1.0, g = 1.0, b = 0.0 },
		bad		= { r = 1.0, g = 0.0, b = 0.0 },
	}

	local ui = {
		dispel = {
			color = color.default,
			position = UI_INCOMING,
			critical = false,
			func = nil,
		},
		ccbreak = {
			color = color.bad,
			position = UI_OUTGOING,
			critical = false,
			func = nil,
		},
		interrupt = {
			color = color.good,
			position = UI_NOTIFICATION,
			critical = true,
			func = nil,
		},
		hostileinterrupt = {
			color = color.bad,
			position = UI_NOTIFICATION,
			critical = true,
			func = nil,
		},
		success = {
			color = color.default,
			position = UI_OUTGOING,
			critical = false,
			func = nil,
		},
		missed = {
			color = color.bad,
			position = UI_NOTIFICATION,
			critical = true,
			func = nil,
		},
		buff = {
			color = color.bad,
			position = UI_INCOMING,
			critical = false,
			func = nil,
		},
		debuff = {
			color = color.bad,
			position = UI_NOTIFICATION,
			critical = true,
			func = nil,
		},
		targetdebuff = {
			color = color.good,
			position = UI_OUTGOING,
			critical = false,
			func = nil,
		},
	}

	----------------
	-- 커맨드 핸들러
	----------------
	local commandHandler = function(option)
		nnCast:Print(GetAddOnMetadata("nnCast", "Version"))

		if option == "reset" then
			nnCast:Print("설정이 기본 값으로 초기화 되었습니다.")
			nnCast.db:ResetProfile()
		else
			InterfaceOptionsFrame_OpenToFrame("nnCast")
		end
	end

-------------------------------------------------------------------------------------
-- 함수
-------------------------------------------------------------------------------------
	--------------
	-- 서버명 제거
	--------------
	local function RemoveServerName(name)

		if name ~= nil then
			local loc = string.find(name, "-")
			if loc then
				name = string.sub(name, 0, loc - 1)
			end
		end

		return name

	end

	--------------
	-- 스킬명 치환
	--------------
	local function GetSkillName(sourceGUID, sourceSkill)

		if sourceSkill == nil then
			sourceSkill = "평타"
		end

		if sourceGUID == UnitGUID("pet") then
			return UnitName("pet") .. "의 " .. sourceSkill
		else
			return sourceSkill
		end

	end

	-------------------
	-- UI Callback 생성
	-------------------
	function nnCast:MakeUIFunc(key)

		local set = ui[key]

		local r = set.color.r
		local g = set.color.g
		local b = set.color.b
		local position
		local critical = set.critical

		----------------------
		-- ScrollingCombatText
		----------------------
		if userAddon == 6 then
			if set.position == UI_NOTIFICATION then
				position = SCT.FRAME1
			elseif set.position == UI_INCOMING then
				position = SCT.MSG
			elseif set.position == UI_OUTGOING then
				position = SCT.FRAME2
			end

			return function(msg)
				SCT:DisplayCustomEvent(msg, {r = r, g = g, b = b}, critical, position)
			end

		------------------------------
		-- Mik's Scrolling Battle Text
		------------------------------
		elseif userAddon == 5 then
			if set.position == UI_NOTIFICATION then
				position = MikSBT.DISPLAYTYPE_NOTIFICATION
			elseif set.position == UI_INCOMING then
				position = MikSBT.DISPLAYTYPE_INCOMING
			elseif set.position == UI_OUTGOING then
				position = MikSBT.DISPLAYTYPE_OUTGOING
			end

			return function(msg)
				MikSBT.DisplayMessage(msg, position, critical, r * 255, g * 255, b * 255)
			end

		---------
		-- Parrot
		---------
		elseif userAddon == 4 then
			return function(msg)
				Parrot:ShowMessage(msg, "Notification", critical, r, g, b)
			end

		----------------------
		-- BlinkCombatFeedBack
		----------------------
		elseif userAddon == 3 then
			if critical then
				critical = "crit"
			else
				critical = "gravity"
			end

			return function(msg)
				BCF_DisplayMessage({text = msg, rgb = {r = r, g = g, b = b}, paneltype = critical, font = "Fonts\\2002b.TTF"})
			end

		----------------------
		-- Blizzard_CombatText
		----------------------
		elseif userAddon == 2 then
			if critical then
				critical = "crit"
			else
				critical = ""
			end

			return function(msg)
				CombatText_AddMessage(msg, CombatText_StandardScroll, r, g, b, critical)
			end

		-------------------
		-- 자체 에러 프레임
		-------------------
		elseif userAddon == 1 then
			return function(msg)
				UIErrorsFrame:AddMessage(msg, r, g, b, 1.0, UIERRORS_HOLD_TIME)
			end

		---------
		-- 미사용
		---------
		else
			return function(msg) end
		end

	end

	---------------------
	-- UI Callback 바인딩
	---------------------
	function nnCast:BindUIFunc()

		for key in pairs(ui) do
			ui[key].func = self:MakeUIFunc(key)
		end

	end

	----------
	-- UI 선택
	----------
	function nnCast:selectUserAddon()

		----------------------
		-- ScrollingCombatText
		----------------------
		if (db.addon == "SCT" or db.addon == "AUTO") and _G.SCT then
			userAddon = 6

		------------------------------
		-- Mik's Scrolling Battle Text
		------------------------------
		elseif (db.addon == "MSBT" or db.addon == "AUTO") and _G.MikSBT then
			userAddon = 5

		---------
		-- Parrot
		---------
		elseif (db.addon == "PARROT" or db.addon == "AUTO") and _G.Parrot then
			userAddon = 4

		----------------------
		-- BlinkCombatFeedBack
		----------------------
		elseif (db.addon == "BCF" or db.addon == "AUTO") and _G.BlinkCombatFeedbackFrame then
			userAddon = 3

		----------------------
		-- Blizzard_CombatText
		----------------------
		elseif (db.addon == "BCT" or db.addon == "AUTO") and SHOW_COMBAT_TEXT and tostring(SHOW_COMBAT_TEXT) ~= "0" then
			userAddon = 2

		-------------------
		-- 자체 에러 프레임
		-------------------
		elseif db.addon == "ERROR_FRAME" or db.addon == "AUTO" then
			userAddon = 1

		---------
		-- 미사용
		---------
		else
			userAddon = 0
		end

		self:BindUIFunc()

	end

	--------------
	-- 메시지 출력
	--------------
	function nnCast:Message(msgType, msgFormat, ...)

		msg = string.format(msgFormat, ...)

		----------
		-- UI 출력
		----------
		if not userAddon then
			self:selectUserAddon()
		end
		ui[msgType].func(msg)

		------------
		-- 채널 출력
		------------
		local channel = db.channel[msgType]

		if channel == "RAID_WARNING" then
			msg = "[" .. UnitName("player") .. "] " .. msg
		end

		if	channel == "SAY" or channel == "YELL"
		or (channel == "PARTY" and GetNumPartyMembers() > 0)
		or (channel == "RAID" and GetNumRaidMembers() > 0)
		or (channel == "RAID_WARNING" and (IsRaidLeader() or IsRaidOfficer()))
		then
			SendChatMessage(msg, channel)
		elseif channel == "USER_CHANNEL" then
			local userChannel = GetChannelName(db.userChannel)
			if userChannel > 0 then
				SendChatMessage(msg, "CHANNEL", nil, userChannel)
			end
		elseif (channel ~= "NONE" and db.useChatFrame) or channel == "CHAT_FRAME" then
			self:Print(msg)
		end

		--------------
		-- 귓속말 출력
		--------------
		if db.whisper[msgType] ~= "" then
			SendChatMessage(msg, "WHISPER", GetDefaultLanguage("player"), db.whisper[msgType])
		end

	end

	---------------------
	-- 귓속말 대상 초기화
	---------------------
	function nnCast:ClearWhisper()

		for i in pairs(db.whisper) do
			db.whisper[i] = ""
		end

	end

	--------------------------
	-- 귓속말 대상 유효성 검사
	--------------------------
	function nnCast:CheckWhisper()

		for i in pairs(db.whisper) do
			if not string.find(db.whisper[i], "^[a-zA-Z가-힣]*$") then
				db.whisper[i] = ""
			end
		end

		if not string.find(db.userChannel, "^[a-zA-Z가-힣0-9_]*$") then
			db.userChannel = ""
		end

	end

	----------------------
	-- 초기화 및 설정 로드
	----------------------
	function nnCast:SetupOptions()

		self.options = {
			type = "group",
			name = "nnCast",
			childGroups = "tab",
			args = {
				---------
				-- Common
				---------
				common = {
					type = "group",
					name = "공통",
					desc = "공통 설정",
					order = 10,
					args = {
						addon = {
							type = "select",
							name = "출력 UI",
							desc = "메세지를 표시할 UI/Addon 선택.",
							get = function(i) return self.db.profile.addon end,
							set = function(i, v)
								self.db.profile.addon = v
								nnCast:selectUserAddon()
							end,
							values = addonList,
							order = 10,
						},
						userChannel = {
							type = "input",
							name = "사용자 채널",
							desc = "사용자 채널로 사용할 채널명",
							get = function(i) return self.db.profile.userChannel end,
							set = function(i, v) self.db.profile.userChannel = v end,
							usage = "<채널명>", -- 채널명 유효성(임시) : 영문, 한글, 숫자, 언더바
							pattern = "^[a-zA-Z가-힣0-9_]*$",
							order = 20,
						},
						useChatFrame = {
							type = "toggle",
							name = "파티가 아닐 때도 메시지 출력",
							desc = "파티에 속해 있지 않을 때에도 대화창에 메시지를 출력합니다. (자신에게만 출력)",
							get = function(i) return self.db.profile.useChatFrame end,
							set = function(i, v) self.db.profile.useChatFrame = v end,
							width = "double",
							order = 30,
						},
						clearWhisper = {
							type = "execute",
							name = "귓속말 대상 초기화",
							desc = "귓속말 알림 대상으로 설정된 ID들을 모두 삭제합니다.",
							func = function(v) nnCast:ClearWhisper() end,
							order = 40,
						},
						onLoadClearWhisper = {
							type = "toggle",
							name = "로드 시 자동으로 초기화",
							desc = "nnCast 로드 시 자동으로 설정된 ID들을 모두 삭제합니다.",
							get = function(i) return self.db.profile.onLoadClear end,
							set = function(i, v) self.db.profile.onLoadClear = v end,
							width = "double",
							order = 50,
						},
					},
				},
				---------
				-- Dispel
				---------
				dispel = {
					type = "group",
					name = "해제",
					desc = "주문 해제 관련 설정",
					order = 10,
					args = {
						dispel = {
							type = "toggle",
							name = "주문 해제 알림",
							desc = "주문 해제 알림 기능을 사용합니다.",
							get = function(i) return self.db.profile.dispel end,
							set = function(i, v)
								self.db.profile.dispel = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						ccbreak = {
							type = "toggle",
							name = "군중 제어 해제 알림",
							desc = "자신의 공격에 의해 군중 제어 스킬이 해제 되었을때 알립니다.",
							get = function(i) return self.db.profile.ccbreak end,
							set = function(i, v)
								self.db.profile.ccbreak = v
								nnCast:LoadEvents()
							end,
							order = 20,
						},
						channel = {
							type = "select",
							name = "주문 해제 알림 채널",
							desc = "주문 해제 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.dispel end,
							set = function(i, v)
								self.db.profile.channel.dispel = v
								self.db.profile.channel.ccbreak = v
							end,
							values = channelList,
							disabled = function() return not self.db.profile.dispel and not self.db.profile.ccbreak end,
							order = 30,
						},
						enemy = {
							type = "toggle",
							name = "공격적인 해제만 알림",
							desc = "우호적 대상에 대한 해제는 알리지 않습니다.",
							get = function(i) return self.db.profile.dispelEnemy end,
							set = function(i, v) self.db.profile.dispelEnemy = v end,
							disabled = function() return not self.db.profile.dispel end,
							order = 40,
						},
						whisper = {
							type = "input",
							name = "주문 해제 귓속말 알림 대상",
							desc = "주문 해제 귓속말 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.dispel end,
							set = function(i, v)
								self.db.profile.whisper.dispel = v
								self.db.profile.whisper.ccbreak = v
							end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.dispel and not self.db.profile.ccbreak end,
							order = 50,
						},
					},
				},
				------------
				-- Interrupt
				------------
				interrupt = {
					type = "group",
					name = "차단",
					desc = "주문 차단 관련 설정",
					order = 20,
					args = {
						interrupt = {
							type = "toggle",
							name = "주문 차단 알림",
							desc = "주문을 차단하거나, 당했을때 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.interrupt end,
							set = function(i, v)
								self.db.profile.interrupt = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						channel = {
							type = "select",
							name = "주문 차단 알림 채널",
							desc = "주문 차단 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.interrupt end,
							set = function(i, v)
								self.db.profile.channel.interrupt = v
								self.db.profile.channel.hostileinterrupt = v
							end,
							values = channelList,
							disabled = function() return not self.db.profile.interrupt end,
							order = 20,
						},
						whisper = {
							type = "input",
							name = "주문 차단 귓속말 알림 대상",
							desc = "주문 차단 귓속말 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.interrupt end,
							set = function(i, v)
								self.db.profile.whisper.interrupt = v
								self.db.profile.whisper.hostileinterrupt = v
							end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.interrupt end,
							order = 30,
						},
					},
				},
				-----------------
				-- Success/Missed
				-----------------
				success = {
					type = "group",
					name = "적중/실패",
					desc = "주문 적중/실패 관련 설정",
					order = 30,
					args = {
						success = {
							type = "toggle",
							name = "주문 적중 알림",
							desc = "지정된 주문이 적중 했을때 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.success end,
							set = function(i, v)
								self.db.profile.success = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						missed = {
							type = "toggle",
							name = "주문 실패 알림",
							desc = "지정된 주문이 실패 했을때 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.missed end,
							set = function(i, v)
								self.db.profile.missed = v
								nnCast:LoadEvents()
							end,
							order = 20,
						},
						channel = {
							type = "select",
							name = "주문 적중/실패 알림 채널",
							desc = "주문 적중/실패 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.success end,
							set = function(i, v)
								self.db.profile.channel.success = v
								self.db.profile.channel.missed = v
							end,
							values = channelList,
							disabled = function() return not self.db.profile.success and not self.db.profile.missed end,
							order = 30,
						},
						whisper = {
							type = "input",
							name = "주문 적중/실패 귓속말 알림 대상",
							desc = "주문 적중/실패 귓속말 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.success end,
							set = function(i, v)
								self.db.profile.whisper.success = v
								self.db.profile.whisper.missed = v
							end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.success and not self.db.profile.missed end,
							order = 40,
						},
					},
				},
				-------
				-- Buff
				-------
				buff = {
					type = "group",
					name = "버프",
					desc = "버프 관련 설정",
					order = 40,
					args = {
						buff = {
							type = "toggle",
							name = "주요 버프 알림",
							desc = "지정된 버프가 걸렸을 때 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.buff end,
							set = function(i, v)
								self.db.profile.buff = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						channel = {
							type = "select",
							name = "주요 버프 알림 채널",
							desc = "주요 버프 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.buff end,
							set = function(i, v) self.db.profile.channel.buff = v end,
							values = channelList,
							disabled = function() return not self.db.profile.buff end,
							order = 20,
						},
						whisper = {
							type = "input",
							name = "주요 버프 귓속말 알림 대상",
							desc = "주요 버프 귓속말 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.buff end,
							set = function(i, v) self.db.profile.whisper.buff = v end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.buff end,
							order = 30,
						},
					},
				},
				---------
				-- Debuff
				---------
				debuff = {
					type = "group",
					name = "디버프",
					desc = "디버프 관련 설정",
					order = 50,
					args = {
						debuff = {
							type = "toggle",
							name = "주요 디버프 알림",
							desc = "지정된 디버프가 걸렸을 때 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.debuff end,
							set = function(i, v)
								self.db.profile.debuff = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						channel = {
							type = "select",
							name = "주요 디버프 알림 채널",
							desc = "주요 디버프 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.debuff end,
							set = function(i, v) self.db.profile.channel.debuff = v end,
							values = channelList,
							disabled = function() return not self.db.profile.debuff end,
							order = 20,
						},
						whisper = {
							type = "input",
							name = "주요 버프/디버프 귓속말 알림 대상",
							desc = "주요 버프/디버프 귓속말 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.debuff end,
							set = function(i, v) self.db.profile.whisper.debuff = v end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.debuff end,
							order = 30,
						},
					},
				},
				---------------
				-- TargetDebuff
				---------------
				targetdebuff = {
					type = "group",
					name = "대상 디버프",
					desc = "대상 디버프 관련 설정",
					order = 60,
					args = {
						targetdebuff = {
							type = "toggle",
							name = "대상 디버프 알림",
							desc = "대상의 디버프를 알리는 기능을 사용합니다.",
							get = function(i) return self.db.profile.targetdebuff end,
							set = function(i, v)
								self.db.profile.targetdebuff = v
								nnCast:LoadEvents()
							end,
							order = 10,
						},
						channel = {
							type = "select",
							name = "대상 디버프 알림 채널",
							desc = "대상 디버프 알림 메세지를 출력할 채널 선택.",
							get = function(i) return self.db.profile.channel.targetdebuff end,
							set = function(i, v) self.db.profile.channel.targetdebuff = v end,
							values = channelList,
							disabled = function() return not self.db.profile.targetdebuff end,
							order = 20,
						},
						whisper = {
							type = "input",
							name = "대상 디버프 귓속말 알림 대상",
							desc = "대상 디버프 알림 대상의 ID를 입력하세요.",
							get = function(i) return self.db.profile.whisper.targetdebuff end,
							set = function(i, v) self.db.profile.whisper.targetdebuff = v end,
							usage = "<사용자명>",
							pattern = "^[a-zA-Z가-힣]*$",
							disabled = function() return not self.db.profile.targetdebuff end,
							order = 30,
						},
					},
				},
			},
			plugins = {},
		}
		self.db = LibStub("AceDB-3.0"):New("nnCastDB", defaults)
		self.options.plugins.profiles = { profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) }

		LibStub("AceConfig-3.0"):RegisterOptionsTable("nnCast", self.options)
		LibStub("AceConfigDialog-3.0"):AddToBlizOptions("nnCast", "nnCast")

	end

	---------
	-- 초기화
	---------
	function nnCast:OnInitialize()

		self:SetupOptions()

		db = self.db.profile

		buffList 	= self:GetBuffList()
		debuffList 	= self:GetDebuffList(select(2, UnitClass("player")))
		missedList 	= self:GetMissedList()
		successList = self:GetSuccessList()
		messageList	= self:GetMessageList()
		targetDebuffList = self:GetTargetDebuffList()

		if db.onLoadClear then
			self:ClearWhisper()
		end
		self:CheckWhisper()

		self:RegisterChatCommand("nncast", commandHandler)
		self:RegisterChatCommand("nnc", commandHandler)

	end

	function nnCast:OnEnable()

		self:LoadEvents()

	end

	function nnCast:OnDisable()

		self:UnregisterAllEvents()

	end

	--------------
	-- 이벤트 로드
	--------------
	function nnCast:LoadEvents()

		self:UnregisterAllEvents()

		self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")

		---------
		-- Dispel
		---------
		if db.dispel then
			if select(2, UnitClass("player")) == "MAGE" then
				trackedEvents["SPELL_STOLEN"] = true
			end
			trackedEvents["SPELL_DISPEL"] = true
		end

		----------
		-- CcBreak
		----------
		if db.ccbreak then
			trackedEvents["SPELL_AURA_BROKEN"] = true
			trackedEvents["SPELL_AURA_BROKEN_SPELL"] = true
		end

		------------
		-- Interrupt
		------------
		if db.interrupt then
			trackedEvents["SPELL_INTERRUPT"] = true
		end

		---------
		-- Missed
		---------
		if db.missed then
			trackedEvents["SPELL_MISSED"] = true
		end

		----------
		-- Success
		----------
		if db.success then
			trackedEvents["SPELL_CAST_SUCCESS"] = true
		end

		---------------------------
		-- Buff/Debuff/TargetDebuff
		---------------------------
		if db.buff or db.debuff or db.targetdebuff then
			trackedEvents["SPELL_AURA_APPLIED"] = true
		end

	end

	function nnCast:COMBAT_LOG_EVENT_UNFILTERED(arg1, timestamp, event, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...)

		if not trackedEvents[event] then
			return
		end

		---------
		-- Dispel
		---------
		if event == "SPELL_DISPEL" or event == "SPELL_STOLEN" then
			self:DispelHandler(sourceGUID, destName, destFlags, select(2, ...), select(5, ...))

		----------
		-- CcBreak
		----------
		elseif event == "SPELL_AURA_BROKEN" or event == "SPELL_AURA_BROKEN_SPELL" then
			self:CcBreakHandler(sourceGUID, destName, select(2, ...), select(5, ...))

		------------
		-- Interrupt
		------------
		elseif event == "SPELL_INTERRUPT" then
			if sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet") then
				self:InterruptHandler(sourceGUID, destName, select(2, ...), select(5, ...))
			elseif destGUID == UnitGUID("player") or destGUID == UnitGUID("pet") then
				self:HostileInterruptHandler(sourceName, destGUID, select(2, ...), select(5, ...))
			end

		---------
		-- Missed
		---------
		elseif event == "SPELL_MISSED" then
			self:MissedHandler(sourceGUID, destName, select(2, ...), select(4, ...))

		----------
		-- Success
		----------
		elseif event == "SPELL_CAST_SUCCESS" and destName ~= nil then
			self:SuccessHandler(sourceGUID, destName, select(2, ...))

		---------------------------
		-- Buff/Debuff/TargetDebuff
		---------------------------
		elseif event == "SPELL_AURA_APPLIED" then
			self:AuraHandler(destGUID, select(2, ...), select(4, ...))
		end

	end

	---------
	-- Dispel
	---------
	function nnCast:DispelHandler(sourceGUID, destName, destFlags, sourceSkill, destSkill)

		if db.dispelEnemy and (bit.band(destFlags, COMBATLOG_OBJECT_REACTION_FRIENDLY) == COMBATLOG_OBJECT_REACTION_FRIENDLY) then
			return
		end

		if sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet") then
			self:Message("dispel", messageList["dispel"], GetSkillName(sourceGUID, sourceSkill), RemoveServerName(destName), destSkill)
		end

	end

	----------
	-- CcBreak
	----------
	function nnCast:CcBreakHandler(sourceGUID, destName, sourceSkill, destSkill)

		if sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet") then
			self:Message("ccbreak", messageList["ccbreak"], GetSkillName(sourceGUID, destSkill), RemoveServerName(destName), sourceSkill)
		end

	end

	------------
	-- Interrupt
	------------
	function nnCast:InterruptHandler(sourceGUID, destName, sourceSkill, destSkill)

		self:Message("interrupt", messageList["interrupt"], GetSkillName(sourceGUID, sourceSkill), RemoveServerName(destName), destSkill)

	end

	----------------------
	-- Interrupt (hostile)
	----------------------
	function nnCast:HostileInterruptHandler(sourceName, destGUID, sourceSkill, destSkill)

		self:Message("hostileinterrupt", messageList["hostileinterrupt"], sourceSkill, RemoveServerName(sourceName), GetSkillName(destGUID, destSkill))

	end

	---------
	-- Missed
	---------
	function nnCast:MissedHandler(sourceGUID, destName, sourceSkill, missType)

		if (sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet")) and missedList[sourceSkill] then
			self:Message("missed", messageList["missed"], GetSkillName(sourceGUID, sourceSkill), RemoveServerName(destName), missList[missType])
		end

	end

	----------
	-- Success
	----------
	function nnCast:SuccessHandler(sourceGUID, destName, sourceSkill)

		if (sourceGUID == UnitGUID("player") or sourceGUID == UnitGUID("pet")) and successList[sourceSkill] then
			self:Message("success", messageList["success"], GetSkillName(sourceGUID, sourceSkill), RemoveServerName(destName))
		end

	end

	---------------------------
	-- Buff/Debuff/TargetDebuff
	---------------------------
	function nnCast:AuraHandler(destGUID, sourceSkill, buffType)

		if destGUID == UnitGUID("player") or destGUID == UnitGUID("pet") then
			-------
			-- Buff
			-------
			if db.buff and buffType == "BUFF" and buffList[sourceSkill] then
				self:Message("buff", messageList["buff"], GetSkillName(destGUID, sourceSkill))

			---------
			-- Debuff
			---------
			elseif db.debuff and buffType == "DEBUFF" and debuffList[sourceSkill] then
--[[
				for i = 1, 40 do
					local id = GetPlayerBuff(i, "HARMFUL")
					if id > 0 then
						local name, duration = GetPlayerBuffName(id), GetPlayerBuffTimeLeft(id)
						if name == sourceSkill then
							self:Message("debuff", messageList["debuff"], GetSkillName(destGUID, sourceSkill), duration)
							break
						end
					end
				end
--]]
				self:Message("debuff", messageList["debuff"], GetSkillName(destGUID, sourceSkill))
			end
		---------------
		-- TargetDebuff
		---------------
		elseif db.targetdebuff and buffType == "DEBUFF" and targetDebuffList[sourceSkill] then
			if destGUID == UnitGUID("playertarget") then
				for i = 1, 40 do
					local name, _, _, _, _, duration = UnitDebuff("playertarget", i)
					if name == sourceSkill and duration then
						self:Message("targetdebuff", messageList["targetdebuff_duration"], sourceSkill, RemoveServerName(UnitName("playertarget")), duration)
						break
					end
				end
--[[
			else
				self:Message("targetdebuff", messageList["targetdebuff"], sourceSkill, RemoveServerName(destName))
--]]
			end
		end

	end