
local bcf = {}
local addonName = "BlinkCombatFeedback"
local addonName_ko = "블링크의 전투메세지"
_G[addonName] = bcf

local info
local myClass = select(2, UnitClass("player"))
local bScroll = LibStub and LibStub("LibScroller-1.0")
local cp = LibStub and LibStub("LibCombatParser-1.0")
local db
local scrollAreas
local deletedScrollAreas = {}
local activeBuffsIconCache = {}
local activeDebuffsIconCache = {}
local cooldowns = {}
local hmChecker = {}
local stacks = {}
local STACK_DELAY_TIME = 0.3
local LAST_STACK_UPDATE = 0
local customEvents = {}
local customRegistedEvents = {}

local new, del, deepDel, doAll
do
	local list = setmetatable({}, {__mode='k'})
	function new(...)
		local t = next(list)
		if t then
			list[t] = nil
			for i = 1, select('#', ...) do
				t[i] = select(i, ...)
			end
			return t
		else
			return { ... }
		end
	end
	function del(t)
		setmetatable(t, nil)
		table.wipe(t)
		list[t] = true
		return nil
	end
	function deepDel(t)
		if type(t) ~= "table" then
			return nil
		end
		for k,v in pairs(t) do
			t[k] = deepDel(v)
		end
		return del(t)
	end
	function doAll(func, ...)
		for i=1, select('#', ...) do
			func(select(i, ...))
		end
	end
end

-- Power Types Localize
local STRING_POWER_MANA			= MANA
local STRING_POWER_RAGE			= RAGE
local STRING_POWER_ENERGY		= ENERGY
local STRING_POWER_HAPPINESS	= HAPPINESS
local STRING_POWER_RUNES		= RUNES
local STRING_POWER_RUNIC_POWER	= RUNIC_POWER
local STRING_POWER_FOCUS		= FOCUS

-- Power Types
local SPELL_POWER_HEALTH		= -2
local SPELL_POWER_MANA			= SPELL_POWER_MANA
local SPELL_POWER_RAGE			= SPELL_POWER_RAGE
local SPELL_POWER_FOCUS			= SPELL_POWER_FOCUS
local SPELL_POWER_ENERGY		= SPELL_POWER_ENERGY
local SPELL_POWER_HAPPINESS		= SPELL_POWER_HAPPINESS
local SPELL_POWER_RUNES			= SPELL_POWER_RUNES
local SPELL_POWER_RUNIC_POWER	= SPELL_POWER_RUNIC_POWER


-- The many school types
local STRING_SCHOOL_PHYSICAL	= STRING_SCHOOL_PHYSICAL
local STRING_SCHOOL_FIRE		= STRING_SCHOOL_FIRE
local STRING_SCHOOL_FROST		= STRING_SCHOOL_FROST
local STRING_SCHOOL_ARCANE		= STRING_SCHOOL_ARCANE
local STRING_SCHOOL_NATURE		= STRING_SCHOOL_NATURE
local STRING_SCHOOL_SHADOW		= STRING_SCHOOL_SHADOW
local STRING_SCHOOL_HOLY		= STRING_SCHOOL_HOLY
local STRING_SCHOOL_UNKNOWN		= STRING_SCHOOL_UNKNOWN

-- schools
local SCHOOL_MASK_NONE			= SCHOOL_MASK_NONE
local SCHOOL_MASK_PHYSICAL		= SCHOOL_MASK_PHYSICAL
local SCHOOL_MASK_HOLY			= SCHOOL_MASK_HOLY
local SCHOOL_MASK_FIRE			= SCHOOL_MASK_FIRE
local SCHOOL_MASK_NATURE		= SCHOOL_MASK_NATURE
local SCHOOL_MASK_FROST			= SCHOOL_MASK_FROST
local SCHOOL_MASK_SHADOW		= SCHOOL_MASK_SHADOW
local SCHOOL_MASK_ARCANE		= SCHOOL_MASK_ARCANE

-- complex school table
local SchoolStringTable = {
	-- Single Schools
	[SCHOOL_MASK_PHYSICAL]						= STRING_SCHOOL_PHYSICAL,
	[SCHOOL_MASK_HOLY]							= STRING_SCHOOL_HOLY,
	[SCHOOL_MASK_FIRE]							= STRING_SCHOOL_FIRE,
	[SCHOOL_MASK_NATURE]						= STRING_SCHOOL_NATURE,
	[SCHOOL_MASK_FROST]							= STRING_SCHOOL_FROST,
	[SCHOOL_MASK_SHADOW]						= STRING_SCHOOL_SHADOW,
	[SCHOOL_MASK_ARCANE]						= STRING_SCHOOL_ARCANE,
	-- Physical and a Magical
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FIRE]	= STRING_SCHOOL_FLAMESTRIKE,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FROST]	= STRING_SCHOOL_FROSTSTRIKE,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_ARCANE]	= STRING_SCHOOL_SPELLSTRIKE,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_NATURE]	= STRING_SCHOOL_STORMSTRIKE,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_SHADOW]	= STRING_SCHOOL_SHADOWSTRIKE,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_HOLY]	= STRING_SCHOOL_HOLYSTRIKE,
	-- Two Magical Schools
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST]		= STRING_SCHOOL_FROSTFIRE,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_ARCANE]		= STRING_SCHOOL_SPELLFIRE,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_NATURE]		= STRING_SCHOOL_FIRESTORM,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_SHADOW]		= STRING_SCHOOL_SHADOWFLAME,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_HOLY]		= STRING_SCHOOL_HOLYFIRE,
	[SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE]	= STRING_SCHOOL_SPELLFROST,
	[SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE]	= STRING_SCHOOL_FROSTSTORM,
	[SCHOOL_MASK_FROST + SCHOOL_MASK_SHADOW]	= STRING_SCHOOL_SHADOWFROST,
	[SCHOOL_MASK_FROST + SCHOOL_MASK_HOLY]		= STRING_SCHOOL_HOLYFROST,
	[SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE]	= STRING_SCHOOL_SPELLSTORM,
	[SCHOOL_MASK_ARCANE + SCHOOL_MASK_SHADOW]	= STRING_SCHOOL_SPELLSHADOW,
	[SCHOOL_MASK_ARCANE + SCHOOL_MASK_HOLY]		= STRING_SCHOOL_DIVINE,
	[SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW]	= STRING_SCHOOL_SHADOWSTORM,
	[SCHOOL_MASK_NATURE + SCHOOL_MASK_HOLY]		= STRING_SCHOOL_HOLYSTORM,
	[SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]		= STRING_SCHOOL_SHADOWLIGHT,
	-- Three or more schools
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE]																						= STRING_SCHOOL_ELEMENTAL,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW]											= STRING_SCHOOL_CHROMATIC,
	[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]						= STRING_SCHOOL_MAGIC,
	[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]	= STRING_SCHOOL_CHAOS,
};

-- Result Types
local STRING_RESULT_RESISTED	= TEXT_MODE_A_STRING_RESULT_RESIST --"저항"
local STRING_RESULT_BLOCKED		= TEXT_MODE_A_STRING_RESULT_BLOCK --"방어"
local STRING_RESULT_ABSORBED	= TEXT_MODE_A_STRING_RESULT_ABSORB --"흡수"
local STRING_RESULT_CRITICAL	= TEXT_MODE_A_STRING_RESULT_CRITICAL --"치명"
local STRING_RESULT_GLANCING	= TEXT_MODE_A_STRING_RESULT_GLANCING --"빗맞음"
local STRING_RESULT_CRUSHING	= TEXT_MODE_A_STRING_RESULT_CRUSHING --"강타"
local STRING_RESULT_SPLIT		= "분산"
local STRING_RESULT_DAMAGESHIELD= TEXT_MODE_A_STRING_RESULT_REFLECT --"반사"

-- environment damage types
local TABLE_ENVIRONMENT_TYPES	= {
	["DROWNING"]				= ACTION_ENVIRONMENTAL_DAMAGE_DROWNING, --"숨막힘"
	["FATIGUE"]					= ACTION_ENVIRONMENTAL_DAMAGE_FATIGUE, --"기진맥진"
	["FALLING"]					= ACTION_ENVIRONMENTAL_DAMAGE_FALLING, --"낙하"
	["FIRE"]					= ACTION_ENVIRONMENTAL_DAMAGE_FIRE, --"화염"
	["LAVA"]					= ACTION_ENVIRONMENTAL_DAMAGE_LAVA, --"용암"
	["SLIME"]					= ACTION_ENVIRONMENTAL_DAMAGE_SLIME, --"독성"
}

local defaultDB = {
	enable = true,						-- 애드온 사용 여부

	stack = true,						-- 메세지 모아보기

	showIncomingHit = true,				-- 받는 데미지 표시
	showIncomingPetHit = false,			-- 펫이 받는 데미지 표시
	showIncomingFocusHit = false,		-- 주시자가 받는 데미지 표시
	
	showOutgoingHit = false,				-- 주는 데미지 표시
	showOutgoingPetHit = false,			-- 펫이 주는 데미지 표시
	showOutgoingFocusHit = false,		-- 주시자가 주는 데미지 표시

	showOverKill = true,				-- 오버킬 표시
	
	showIncomingHeal = true,			-- 받는 힐 표시
	showOutgoingHeal = true,			-- 주는 힐 표시
	showOverHeal = true,				-- 오버 힐량 표시
	showEnergize = true,				-- 마나/분노/기력 회복 표시

	showEnterCombat = true,				-- 전투 시작/종료 표시

	showIcon = false,					-- 스킬 아이콘 표시
	showIconBorder = false,				-- 스킬 아이콘의 테두리를 표시
	showSpellName = false,				-- 스킬명 표시
	showDestName = true,				-- 힐 받는 대상 이름 표시
	showInterrupt = true,				-- 시전 차단 표시
	showExtraAttack = true,				-- 추가타격 표시
	showReputation = true,				-- 평판 수치변화 표시
	showEnchant = true,					-- 아이템 강화 표시

	showPowerColor = true,				-- 파워타입별 색상 표시
	showSchoolColor = true,				-- 데미지별 색상 표시

	showBuff = true,					-- 버프 표시
	showDebuff = true,					-- 디버프 표시
	showBuffFade = true,				-- 버프 사라짐 표시
	showDebuffFade = true,				-- 디버프 사라짐 표시

	showCombo = true,					-- 도적/드루이드 연계점수 표시
	scrollLevel = "BACKGROUND",					-- 1~7
	autoChangeReputation = true,		-- 평판 자동 변경
	useCustomEvent = true,				-- 사용자 정의 이벤트 사용할것인가?
	showHonorGain = true,				-- 명예점수 표시

	scrollAreas = {
	},
	enterCombat = {
		enterMsg = "+전투 시작",
		leaveMsg = "-전투 종료",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "up",
		color = "ff7f7f",
		align = "center",
	},
	incomingHit = {
		showMiss = true,			-- 내가 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "-%a %s",
		missMsg = "%s %m",
		drainMsg = "-%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		align = "right",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "-%a %s",
			drainMsg = "-%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Incoming",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	incomingPetHit = {
		showMiss = true,			-- 펫이 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "-%a %s",
		missMsg = "%s %m",
		drainMsg = "-%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "Incoming",
		scrollType = "curveleftdown",
		align = "right",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "-%a %s",
			drainMsg = "-%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Incoming",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	incomingFocusHit = {
		showMiss = true,			-- 주시자가 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "-%a %s",
		missMsg = "%s %m",
		drainMsg = "-%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "Incoming",
		scrollType = "curveleftdown",
		align = "right",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "-%a %s",
			drainMsg = "-%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Incoming",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	outgoingHit = {
		showMiss = true,			-- 몹이 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "%a %s",
		missMsg = "%s %m",
		drainMsg = "%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "Outgoing",
		scrollType = "curverightdown",
		align = "left",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "%a %s",
			drainMsg = "%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Outgoing",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	outgoingPetHit = {
		showMiss = true,			-- 펫이 때린거 몹이 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "%a %s",
		missMsg = "%s %m",
		drainMsg = "%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "Outgoing",
		scrollType = "curverightdown",
		align = "left",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "%a %s",
			drainMsg = "%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Outgoing",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	outgoingFocusHit = {
		showMiss = true,			-- 주시자가 때린거 몹이 피한
		showDot = true,				-- 주기적인 피해 표시
		msg = "%a %s",
		missMsg = "%s %m",
		drainMsg = "%a%p %s",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "Outgoing",
		scrollType = "curverightdown",
		align = "left",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "%a %s",
			drainMsg = "%a%p %s",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "Outgoing",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showDot = true,				-- 주기적인 피해 표시
		},
	},
	incomingHeal = {
		showHot = true,				-- 주기적인 회복 표시
		msg = "+%a %s %sc",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationUp",
		scrollType = "up",
		align = "center",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "+%a %s %sc",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "up",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showHot = true,				-- 주기적인 회복 표시
		},
	},
	outgoingHeal = {
		showHot = true,				-- 주기적인 회복 표시
		msg = "+%a %s %t",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		align = "center",
		iconalign = "right",
		filter = 0,
		crit = {
			msg = "+%a %s %t",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			outline = '',
			scrollArea = "up",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
			showHot = true,				-- 주기적인 회복 표시
		},
	},
	energize = {
		showHot = true,				-- 주기적인 회복 표시
		msg = "+%a%p %s %sc",
		leechMsg = "+%ea%p %s %sc (-%a)",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationUp",
		scrollType = "up",
		align = "center",
		iconalign = "right",
		filter = 0,
	},
	buff = {
		msg = "+(%s) %sc",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		color = "02ff02",				-- 버프 색상
		align = "center",
		iconalign = "right",
	},
	bufffade = {
		msg = "-(%s) %sc",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		color = "ffff02",				-- 버프 사라짐 색상
		align = "center",
		iconalign = "right",
	},
	debuff = {
		msg = "+[%s] %sc",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = 'THICKOUTLINE',
		scrollArea = "NotificationDown",
		scrollType = "down",
		color = "ff0202",				-- 디버프 색상
		align = "center",
		iconalign = "right",
	},
	debufffade = {
		msg = "-[%s] %sc",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = 'THICKOUTLINE',
		scrollArea = "NotificationDown",
		scrollType = "down",
		color = "ffff02",				-- 디버프 사라짐 색상
		align = "center",
		iconalign = "right",
	},
	interrupt = {
		msg = "%es 차단",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = 'THICKOUTLINE',
		scrollArea = "NotificationUp",
		scrollType = "sticky",
		align = "center",
		iconalign = "right",
	},
	extraattack = {
		msg = "+%a 추가타격",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationUp",
		scrollType = "up",
		align = "center",
	},
	reputation = {
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		align = "center",
	},
	enchant = {
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		align = "center",
		iconalign = "right",
	},
	schoolColor = {
		[SCHOOL_MASK_PHYSICAL] = "ffff7f",		-- 물리데미지 색상
		[SCHOOL_MASK_FIRE] = "ff7f00",			-- 화염데미지 색상
		[SCHOOL_MASK_FROST] = "7fffff",			-- 냉기데미지 색상
		[SCHOOL_MASK_ARCANE] = "ff7fff",			-- 비전데미지 색상
		[SCHOOL_MASK_NATURE] = "4cff4c",			-- 자연데미지 색상
		[SCHOOL_MASK_SHADOW] = "7f7fff",			-- 암흑데미지 색상
		[SCHOOL_MASK_HOLY] = "ffe57f",			-- 신성데미지 색상
		none = "ffff00",			-- 속성컬러옵션을 선택하지않으면 보여줄 색상
	},
	schoolColor2 = {
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FIRE] = "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FROST] = "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_ARCANE] = "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_NATURE] = "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_SHADOW]	= "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_HOLY]	= "ffffff",
	},
	schoolColor3 = {
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST]		= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_ARCANE]		= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_NATURE]		= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_SHADOW]		= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_HOLY]		= "ffffff",

		[SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE]	= "ffffff",
		[SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE]	= "ffffff",
		[SCHOOL_MASK_FROST + SCHOOL_MASK_SHADOW]	= "ffffff",
		[SCHOOL_MASK_FROST + SCHOOL_MASK_HOLY]		= "ffffff",

		[SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE]	= "ffffff",
		[SCHOOL_MASK_ARCANE + SCHOOL_MASK_SHADOW]	= "ffffff",
		[SCHOOL_MASK_ARCANE + SCHOOL_MASK_HOLY]		= "ffffff",

		[SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW]	= "ffffff",
		[SCHOOL_MASK_NATURE + SCHOOL_MASK_HOLY]		= "ffffff",

		[SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]		= "ffffff",
	},
	schoolColor4 = {
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_NATURE]																						= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW]											= "ffffff",
		[SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]						= "ffffff",
		[SCHOOL_MASK_PHYSICAL + SCHOOL_MASK_FIRE + SCHOOL_MASK_FROST + SCHOOL_MASK_ARCANE + SCHOOL_MASK_NATURE + SCHOOL_MASK_SHADOW + SCHOOL_MASK_HOLY]	= "ffffff",
	},
	powerColor = {
		mana = "517fff",			-- 마나 색상
		rage = "ff0202",			-- 분노 색상
		energy = "ffff02",			-- 기력 색상
		runic_power = "00d1ff",			-- 룬마력 색상
		runes = "7f7f7f",			-- 룬 색상
	},
	combo = {
		msg = "%a 콤보",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationUp",
		scrollType = "crit",
		align = "center",
		color = "20ff20",
	},

	registedSoundFiles = {
		["Sound\\Creature\\OgreKing\\OgreKingPreAggro.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack01.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack02.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack03.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack04.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack05.wav"] = true,
		["Sound\\Creature\\Illidan\\BLACK_Illidan_Attack06.wav"] = true,
		["Sound\\Event Sounds\\Event_wardrum_ogre.wav"] = true,
		["Sound\\Event Sounds\\OgreEventCheerUnique.wav"] = true,
		["Sound\\Spells\\Blade_of_Azzinoth_Impact.wav"] = true,
		["Sound\\Character\\BloodElf\\BloodElfMaleRoar01.wav"] = true,
	},
	exceptAuras = {},
	customEvents = {
		{
			msg = "체력낮음(%2)",
			outline = "THICKOUTLINE",
			align = "center",
			font = "Fonts\\K_Damage.TTF",
			size = 24,
			scrollArea = "NotificationDown",
			color = "ff0001",
			scrollType = "crit",
			condition = "selfHealthLoss",
			value = 30,
		},
		{
			msg = "마나낮음(%2)",
			outline = "THICKOUTLINE",
			align = "center",
			scrollType = "crit",
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "0050ff",
			sound = "",
			size = 24,
			class = {
				["HUNTER"] = true,
				["WARLOCK"] = true,
				["SHAMAN"] = true,
				["MAGE"] = true,
				["PALADIN"] = true,
				["PRIEST"] = true,
			},
			condition = "selfManaLoss",
			value = 30,
		},
		{
			msg = "<제압 발사>",
			outline = "",
			align = "center",
			font = "Fonts\\K_Damage.TTF",
			size = 28,
			scrollArea = "NotificationDown",
			color = "79ffba",
			sound = "Interface\\AddOns\\bcf\\sound\\Roar.wav",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "outgoingDodge",
			subConditions = {
				["Cooldown"] = 5,
				["SpellReady"] = "제압",
			},
		},
		{
			msg = "<마무리 일격>",
			outline = "",
			align = "center",
			size = 28,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "ff3013",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "targetHealthLoss",
			value = 20,
			subConditions = {
				["MinPower"] = 15,
			},
		},
		{
			msg = "<승리의 돌진>",
			outline = "",
			align = "center",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "ffa315",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "killblow",
			subConditions = {
				["HonorKillLevel"] = true,
			},
		},
		{
			msg = "((도발저항))",
			outline = "",
			script = "SendChatMessage(\">>> \"..UnitName(\"target\")..\" <<< 도발 저항!!\", \"YELL\");",
			align = "center",
			font = "Fonts\\K_Damage.TTF",
			size = 20,
			scrollArea = "NotificationDown",
			color = "ff0007",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "outgoingResist",
			value = "도발",
			subConditions = {
				["TargetIsEnemy"] = true,
			},
		},
		{
			msg = "((도발면역))",
			outline = "",
			script = "SendChatMessage(\">>> \"..UnitName(\"target\")..\" <<< 도발 면역!!\", \"YELL\");",
			align = "center",
			font = "Fonts\\K_Damage.TTF",
			size = 20,
			scrollArea = "NotificationDown",
			color = "ff0007",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "outgoingImmune",
			value = "도발",
			subConditions = {
				["TargetIsEnemy"] = true,
			},
		},
		{
			msg = "((포효저항))",
			outline = "",
			script = "SendChatMessage(\">>> \"..UnitName(\"target\")..\" <<< 도발 저항!!\", \"YELL\");",
			align = "center",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "ff0007",
			scrollType = "crit",
			class = {
				["DRUID"] = true,
			},
			condition = "outgoingResist",
			value = "포효",
			subConditions = {
				["TargetIsEnemy"] = true,
			},
		},
		{
			msg = "((포효면역))",
			outline = "",
			script = "SendChatMessage(\">>> \"..UnitName(\"target\")..\" <<< 도발 면역!!\", \"YELL\");",
			align = "center",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "ff0007",
			scrollType = "crit",
			class = {
				["DRUID"] = true,
			},
			condition = "outgoingImmune",
			value = "포효",
			subConditions = {
				["TargetIsEnemy"] = true,
			},
		},
		{
			msg = "<살쾡이의 이빨 발사>",
			outline = "",
			align = "center",
			size = 28,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			value = "",
			color = "ff1a0e",
			sound = "Interface\\AddOns\\bcf\\sound\\Roar.wav",
			scrollType = "crit",
			class = {
				["HUNTER"] = true,
			},
			condition = "incomingDodge",
			subConditions = {
				["Cooldown"] = 5,
				["SpellReady"] = "살쾡이의 이빨",
			},
		},
		{
			msg = "싸아아아아~", --_-;;
			outline = "",
			align = "center",
			size = 28,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationUp",
			color = "ffffff",
			sound = "Interface\\AddOns\\bcf\\sound\\stimpack.wav",
			scrollType = "up",
			class = {
				["HUNTER"] = true,
			},
			condition = "selfBuff",
			value = "신속 사격",
		},
		{
			msg = "<반격>",
			outline = "",
			script = "",
			align = "center",
			size = 28,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationUp",
			color = "49ff6e",
			sound = "Sound\\Creature\\Illidan\\BLACK_Illidan_Attack06.wav",
			scrollType = "up",
			class = {
				["ROGUE"] = true,
			},
			condition = "incomingParry",
			subConditions = {
				["SpellReady"] = "반격",
			},
		},
		{
			msg = "(%v) 처치!",
			outline = "THICKOUTLINE",
			align = "center",
			size = 24,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationUp",
			color = "f863ff",
			scrollType = "crit",
			condition = "killblow",
		},
	},
}


-------------------------------------------------------------------------------
-- local functions
-------------------------------------------------------------------------------

local function print(msg)
	ChatFrame1:AddMessage(msg, 1.0, 1.0, 0.2)
end

local function copyTable(src)
	local dest = new()
	for k, v in pairs(src) do
		if type(v)=='table' then
			dest[k] = copyTable(v)
		else
			dest[k] = v
		end
	end
	return dest
end
local function tcopy(to, from) -- "to" must be a table (possibly empty)
	for k,v in pairs(from) do
		if(type(v)=="table") then
			if not to then to = new() end
			to[k] = new()
			tcopy(to[k], v)
		else
			to[k] = v
		end
	end
end

local function tnilcopy(to, from)
	for k, v in pairs(from) do
		if k~="customEvents" then
			if type(v)=='boolean' then
--				if not to or to[k]==nil then
--					if not to then to = {} end
--					print(tostring(to[k]).."] = "..tostring(v))
--					to[k] = v
--				end
			else
				if to==nil or to[k]==nil then
					if not to then to = new() end
					if type(v) == "table" then
						to[k] = new()
						tcopy(to[k], v)
					else
--						print("t["..k.."] = "..v)
						to[k] = v
					end
				else
					if type(v) == "table" then
						tnilcopy(to[k], v)
					end
				end
			end
		end
	end
end

local function tmerge(to, from, ...)
	for k, v in pairs(from) do
		if(type(v)=="table") then
			if not to then to = {} end
			to[k] = {}
			tmerge(to[k], v)
		else
			to[k] = v
		end
	end
end

local function hexToDec(hex)
	local r = hex:sub(1, 2)
	local g = hex:sub(3, 4)
	local b = hex:sub(5, 6)
	r = tonumber(r, 16) / 255
	g = tonumber(g, 16) / 255
	b = tonumber(b, 16) / 255
	return r, g, b
end

local function FloatToText(r,g,b)
	if ( type (r) == "table" and r.r and r.g and r.b ) then
		r, g, b = r.r, r.g, r.b
	end
	if ( r > 1 ) then r = 1.0 end
	if ( g > 1 ) then g = 1.0 end
	if ( b > 1 ) then b = 1.0 end
	local newR, newG, newB = math.floor(r * 255), math.floor(g * 255), math.floor(b * 255)
	local fmt = "%.2x"

	return string.format(fmt, newR )..string.format(fmt, newG )..string.format(fmt, newB )
end

local function ClassCheck(class)
	if not class or class=="ALL" then return true end
	if type(class)=='table' then
		return class[myClass]
	else
		return class==myClass
	end
end

local function CheckSpellReady(spellName) -- from MSBT
	local start, duration, enabled = GetSpellCooldown(spellName)
	if enabled == 0  then
		return false
	elseif start and duration and start > 0 and duration > 1.5 then
		return false
	else
		return true
	end
end

local function GetSpellRequireCost(spellName)
	local name, _, icon, cost, isFunnel, powerType = GetSpellInfo(spellName)

	if name and name == spellName then
		return cost, powerType
	end

	return 0, 0
end

local function GetPowerTypeColor(powerType)
	if ( not powerType ) then
		return ""
	elseif ( powerType == SPELL_POWER_MANA ) then
		return STRING_POWER_MANA, db.showPowerColor and db.powerColor.mana
	elseif ( powerType == SPELL_POWER_RAGE ) then
		return STRING_POWER_RAGE, db.showPowerColor and db.powerColor.rage
	elseif ( powerType == SPELL_POWER_ENERGY ) then
		return STRING_POWER_ENERGY, db.showPowerColor and db.powerColor.energy
	elseif ( powerType == SPELL_POWER_FOCUS ) then
		return STRING_POWER_FOCUS
	elseif ( powerType == SPELL_POWER_HAPPINESS ) then
		return STRING_POWER_HAPPINESS
	elseif ( powerType == SPELL_POWER_RUNES ) then
		return STRING_POWER_RUNES
	elseif ( powerType == SPELL_POWER_RUNIC_POWER ) then
		return STRING_POWER_RUNIC_POWER, db.showPowerColor and db.powerColor.runic_power
	end
end

local function GetSchoolColor(school)
	if not school or not SchoolStringTable[school] then
		return nil, "ffffff"
	elseif SchoolStringTable[school] then
		return SchoolStringTable[school], db.showSchoolColor and db.schoolColor[school] or db.schoolColor2[school] or db.schoolColor3[school] or db.schoolColor4[school] or db.schoolColor.none or "ffff00"
	else
		return nil, "ffffff"
	end
end

local function GetTrail(info)
	local text = ""
	if info.blocked and info.blocked>0 then text = ("%s"..STRING_RESULT_BLOCKED):format(text, info.blocked) end
	if info.absorbed and info.absorbed>0 then text = ("%s"..STRING_RESULT_ABSORBED):format(text, info.absorbed) end
	if info.resisted and info.resisted>0 then text = ("%s"..STRING_RESULT_RESISTED):format(text, info.resisted) end
	if info.crushing then text = ("%s"..STRING_RESULT_CRUSHING):format(text) end
	if info.glancing and info.glancing>0 then text = ("%s"..STRING_RESULT_GLANCING):format(text) end
	if info.isSplit then text = ("%s"..STRING_RESULT_SPLIT):format(text) end
	if info.isDS then text = ("%s"..STRING_RESULT_DAMAGESHIELD):format(text) end
	return text:trim():len()>0 and " |cff888888"..text:trim().."|r" or ""
end

-- %a => amount
-- %s => spell
-- %p => powerType
-- %m => missType
-- %sc => source
-- %t => dest
-- %c => school
-- %i => spell icon
local function MessageFormat(msg, info)
	local coloredText = false
	local spellname = info.spell.name
	local extraspellname = info.extraSpell and info.extraSpell.name
	local amount = 0
	if info.amount and msg:find("%%a") then
		if info.overkill and info.overkill>0 and db.showOverKill then
			amount = info.amount - info.overkill
		else
			amount = info.amount
		end
		if info.powerType then
			local power, color = GetPowerTypeColor(info.powerType)
			if color then
				amount = ("|cff%s%s|r"):format(color, amount)
				coloredText = true
			end
			msg = msg:gsub("%%a", amount)
			if power and msg:find("%%p") then
				msg = msg:gsub("%%p", power)
			end
		elseif info.spell.school or info.school then
			local schoolName, schoolColor = GetSchoolColor(info.spell.school or info.school)
			msg = msg:gsub("%%a", ("|cff%s%d|r"):format(schoolColor, amount))
			coloredText = true
			if schoolName and msg:find("%%c") then
				msg = msg:gsub("%%c", ("|cff%s%s|r"):format(schoolColor, schoolName))
			end
		else
			msg = msg:gsub("%%a", amount)
		end
	end
	msg = msg:gsub("%%a", "")

	if info.extraAmount and msg:find("%%ea") then
		if info.powerType then
			local power, color = GetPowerTypeColor(info.powerType)
			if color then
				info.extraAmount = ("|cff%s%s|r"):format(color, info.extraAmount)
				coloredText = true
			end
			msg = msg:gsub("%%ea", info.extraAmount)
			if power and msg:find("%%p") then
				msg = msg:gsub("%%p", power)
			end
		elseif info.spell.school or info.school then
			local schoolName, schoolColor = GetSchoolColor(info.spell.school or info.school)
			msg = msg:gsub("%%ea", ("|cff%s%d|r"):format(schoolColor, info.extraAmount))
			coloredText = true
		else
			msg = msg:gsub("%%ea", info.extraAmount)
		end
	end
	msg = msg:gsub("%%ea", "")

	if info.source.name and msg:find("%%sc") and db.showDestName then
		if info.source.unit then
			local class = select(2, UnitClass(info.source.unit))
			local color = RAID_CLASS_COLORS[class]
			if color then
				msg = msg:gsub("%%sc", ("(|cff%s%s|r)"):format(FloatToText(color), info.source.name))
			else
				msg = msg:gsub("%%sc", "(|cff888888"..info.source.name.."|r)")
			end
		else
			msg = msg:gsub("%%sc", "("..info.source.name..")")
		end
	end
	msg = msg:gsub("%%sc", "")

	if info.dest.name and msg:find("%%t") and db.showDestName then
		if info.dest.unit then
			local class = select(2, UnitClass(info.dest.unit))
			local classcolor = RAID_CLASS_COLORS[class]
			if classcolor then
				msg = msg:gsub("%%t", ("(|cff%s%s|r)"):format(FloatToText(classcolor), info.dest.name))
			else
				msg = msg:gsub("%%t", "(|cff888888"..info.dest.name.."|r)")
			end
		else
			msg = msg:gsub("%%t", "("..info.dest.name..")")
		end
	end
	msg = msg:gsub("%%t", "")

	if spellname and msg:find("%%s") and db.showSpellName then
		if not coloredText then
			if info.powerType then
				local power, color = GetPowerTypeColor(info.powerType)
				if color then
					spellname = ("|cff%s%s|r"):format(color, spellname)
					coloredText = true
				end
			elseif info.school then
				local schoolName, schoolColor = GetSchoolColor(info.school)
				spellname = ("|cff%s%s|r"):format(schoolColor, spellname)
				coloredText = true
			end
		end
		msg = msg:gsub("%%s", spellname)
	end
	if extraspellname and msg:find("%%es") and db.showSpellName then
		if not coloredText then
			if info.powerType then
				local power, color = GetPowerTypeColor(info.powerType)
				if color then
					extraspellname = ("|cff%s%s|r"):format(color, extraspellname)
					coloredText = true
				end
			elseif info.school then
				local schoolName, schoolColor = GetSchoolColor(info.school)
				extraspellname = ("|cff%s%s|r"):format(schoolColor, extraspellname)
				coloredText = true
			end
		end
		msg = msg:gsub("%%es", extraspellname)
	end
	if info.missType and msg:find("%%m") then
		msg = msg:gsub("%%m", info.missType)
	end
	if info.spell.id and msg:find("%%i") then
		local icon = bcf:GetIcon(info.spell.id)
		if icon then
			msg = msg:gsub("%%i", ("|T%s:0|t"):format(icon))
		end
	end
	msg = msg:gsub("%%i", "")

	msg = msg:gsub("%%s", "")
	msg = msg:gsub("%%es", "")
	msg = msg:gsub("%%m", "")
	msg = msg:gsub("%%p", "")
	msg = msg:gsub("%%c", "")
	msg = msg:gsub("  ", " ")
	msg = msg:trim()..GetTrail(info)

	if info.overkill and info.overkill>0 and db.showOverKill then
		msg = ("%s |cff888888(%d)|r"):format(msg, info.overkill)
	end
	if info.source.isRT1 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"..msg
	elseif info.source.isRT2 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"..msg
	elseif info.source.isRT3 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"..msg
	elseif info.source.isRT4 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"..msg
	elseif info.source.isRT5 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"..msg
	elseif info.source.isRT6 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"..msg
	elseif info.source.isRT7 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"..msg
	elseif info.source.isRT8 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"..msg
	end

	if info.dest.isRT1 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"
	elseif info.dest.isRT2 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"
	elseif info.dest.isRT3 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"
	elseif info.dest.isRT4 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"
	elseif info.dest.isRT5 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"
	elseif info.dest.isRT6 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"
	elseif info.dest.isRT7 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"
	elseif info.dest.isRT8 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"
	end

	if info.count and info.count > 1 then
		msg = msg .. "x" .. info.count
	end

	return msg
end

local function MessageFormatHeal(msg, info)
	local coloredText = false
	local spellname = info.spell.name
	if info.amount and msg:find("%%a") then
		if info.overhealAmount and info.overhealAmount>0 and db.showOverHeal then
			msg = msg:gsub("%%a", ("|cff02ff02%d|r"):format(info.amount-info.overhealAmount))
		else
			msg = msg:gsub("%%a", ("|cff02ff02%d|r"):format(info.amount))
		end
		coloredText = true
	end
	if info.source.name and msg:find("%%sc") and db.showDestName then
		if info.source.unit then
			local class = select(2, UnitClass(info.source.unit))
			local color = RAID_CLASS_COLORS[class]
			if color then
				msg = msg:gsub("%%sc", ("(|cff%s%s|r)"):format(FloatToText(color), info.source.name))
				coloredText = true
			else
				msg = msg:gsub("%%sc", "("..info.source.name..")")
			end
		else
			msg = msg:gsub("%%sc", "("..info.source.name..")")
		end
	end
	msg = msg:gsub("%%sc", "")

	if info.dest.name and msg:find("%%t") and db.showDestName then
		if info.dest.unit then
			local class = select(2, UnitClass(info.dest.unit))
			local classcolor = RAID_CLASS_COLORS[class]
			if classcolor then
				msg = msg:gsub("%%t", ("(|cff%s%s|r)"):format(FloatToText(classcolor), info.dest.name))
			else
				msg = msg:gsub("%%t", "(|cff888888"..info.dest.name.."|r)")
			end
		else
			msg = msg:gsub("%%t", "("..info.dest.name..")")
		end
	end
	msg = msg:gsub("%%t", "")

	if spellname and msg:find("%%s") and db.showSpellName then
		if not coloredText then
			if info.powerType then
				local power, color = GetPowerTypeColor(info.powerType)
				if color then
					spellname = ("|cff%s%s|r"):format(color, spellname)
					coloredText = true
				end
			elseif info.school then
				local schoolName, schoolColor = GetSchoolColor(info.school)
				spellname = ("|cff%s%s|r"):format(schoolColor, spellname)
				coloredText = true
			end
		end
		msg = msg:gsub("%%s", spellname)
	end
	if info.spell.id and msg:find("%%i") then
		local icon = bcf:GetIcon(info.spell.id)
		if icon then
			msg = msg:gsub("%%i", ("|T%s:0|t"):format(icon))
		end
	end
	msg = msg:gsub("%%i", "")

	msg = msg:gsub("%%a", "")
	msg = msg:gsub("%%s", "")
	msg = msg:gsub("%%t", "")
	msg = msg:gsub("  ", " ")

	if info.overhealAmount and info.overhealAmount>0 and db.showOverHeal then
		msg = ("%s |cff888888(%d)|r"):format(msg, info.overhealAmount)
	end

	if info.source.isRT1 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"..msg
	elseif info.source.isRT2 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"..msg
	elseif info.source.isRT3 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"..msg
	elseif info.source.isRT4 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"..msg
	elseif info.source.isRT5 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"..msg
	elseif info.source.isRT6 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"..msg
	elseif info.source.isRT7 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"..msg
	elseif info.source.isRT8 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"..msg
	end

	if info.dest.isRT1 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"
	elseif info.dest.isRT2 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"
	elseif info.dest.isRT3 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"
	elseif info.dest.isRT4 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"
	elseif info.dest.isRT5 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"
	elseif info.dest.isRT6 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"
	elseif info.dest.isRT7 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"
	elseif info.dest.isRT8 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"
	end

	if info.count and info.count > 1 then
		msg = msg .. "x" .. info.count
	end

	return msg:trim()
end

local function MessageFormatAura(msg, info, color)
	local coloredText = false
	local spellname = info.spell.name

	if info.source.name and msg:find("%%sc") and db.showDestName then
		if info.source.unit then
			local class = select(2, UnitClass(info.source.unit))
			local classcolor = RAID_CLASS_COLORS[class]
			if classcolor then
				msg = msg:gsub("%%sc", ("(|cff%s%s|r)"):format(FloatToText(classcolor), info.source.name))
			else
				msg = msg:gsub("%%sc", "(|cff888888"..info.source.name.."|r)")
			end
		else
			msg = msg:gsub("%%sc", "("..info.source.name..")")
		end
	end
	msg = msg:gsub("%%sc", "")

	if info.dest.name and msg:find("%%t") and db.showDestName then
		if info.dest.unit then
			local class = select(2, UnitClass(info.dest.unit))
			local classcolor = RAID_CLASS_COLORS[class]
			if classcolor then
				msg = msg:gsub("%%t", ("(|cff%s%s|r)"):format(FloatToText(classcolor), info.dest.name))
			else
				msg = msg:gsub("%%t", "(|cff888888"..info.dest.name.."|r)")
			end
		else
			msg = msg:gsub("%%t", "("..info.dest.name..")")
		end
	end

	if spellname and msg:find("%%s") then
		spellname = ("|cff%s%s|r"):format(color, spellname)
		msg = msg:gsub("%%s", ("|cff%s%s|r"):format(color, spellname))
	end

	if info.amount and tonumber(info.amount) > 1 then
		msg = ("%s x%d"):format(msg, info.amount)
	end

	msg = msg:gsub("%%a", "")
	msg = msg:gsub("%%s", "")
	msg = msg:gsub("%%m", "")
	msg = msg:gsub("%%p", "")
	msg = msg:gsub("%%t", "")
	msg = msg:gsub("  ", " ")
	msg = msg:trim()

	if info.source.isRT1 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"..msg
	elseif info.source.isRT2 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"..msg
	elseif info.source.isRT3 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"..msg
	elseif info.source.isRT4 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"..msg
	elseif info.source.isRT5 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"..msg
	elseif info.source.isRT6 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"..msg
	elseif info.source.isRT7 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"..msg
	elseif info.source.isRT8 then
		msg = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"..msg
	end

	if info.dest.isRT1 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1.blp:0|t"
	elseif info.dest.isRT2 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2.blp:0|t"
	elseif info.dest.isRT3 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3.blp:0|t"
	elseif info.dest.isRT4 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4.blp:0|t"
	elseif info.dest.isRT5 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5.blp:0|t"
	elseif info.dest.isRT6 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6.blp:0|t"
	elseif info.dest.isRT7 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7.blp:0|t"
	elseif info.dest.isRT8 then
		msg = msg.."|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8.blp:0|t"
	end

	return msg
end


bcf.excepts = {
	buff = {
		["*"] = {
			["붕대 감기"] = true,
		},
		["MAGE"] = {
--			["신비한 지능"] = true,
--			["냉기 갑옷"] = true,
--			["타오르는 갑옷"] = true,
--			["얼음 갑옷"] = true,
--			["점멸"] = true,
--			["마법 증폭"] = true,
--			["마법 감쇠"] = true,
--			["환기"] = true,
--			["화염계 수호"] = true,
--			["냉기계 수호"] = true,
--			["얼음 보호막"] = true,
--			["얼음 방패"] = true,
--			["마법사 갑옷"] = true,
--			["마나 보호막"] = true,
--			["저체온증"] = true,
		},
		["WARRIOR"] = {
		},
		["HUNTER"] = {
		},
		["WARLOCK"] = {
		},
		["PREIST"] = {
		},
		["PALADIN"] = {
		},
		["SHAMAN"] = {
		},
		["ROGUE"] = {
		},
		["DRUID"] = {
		},
	},
	debuff = {
		["*"] = {
		},
		["MAGE"] = {
			["저체온증"] = true,
		},
		["WARRIOR"] = {
		},
		["HUNTER"] = {
		},
		["WARLOCK"] = {
		},
		["PREIST"] = {
		},
		["PALADIN"] = {
		},
		["SHAMAN"] = {
		},
		["ROGUE"] = {
		},
		["DRUID"] = {
		},
	},
	fade = {
		["*"] = {
		},
		["MAGE"] = {
			["저체온증"] = true,
			["점멸"] = true,
		},
		["WARRIOR"] = {
			["전투 태세"] = true,
			["방어 태세"] = true,
			["광폭 태세"] = true,
		},
		["HUNTER"] = {
		},
		["WARLOCK"] = {
		},
		["PREIST"] = {
		},
		["PALADIN"] = {
		},
		["SHAMAN"] = {
		},
		["ROGUE"] = {
		},
		["DRUID"] = {
		},
	},
}


function bcf:OnLoad()

	if not bScroll then
		print("LibScroller-1.0이 실행되지 않아 bcf를 사용할 수 없습니다.")
		return
	end

	if not cp then
		print("LibCombatParser-1.0이 실행되지 않아 bcf를 사용할 수 없습니다.")
		return
	end
	cp:AddListener(self, "parser")

	self.customFrame = CreateFrame("Frame")
	self.customFrame:SetScript("OnEvent", function (this, event, ...)
		if db.enable and db.useCustomEvent and self[event] and type(self[event])=='function' then
			self[event](self, ...)
		end
	end)

	self.stackFrame = CreateFrame("Frame")
	self.stackFrame:SetScript("OnUpdate", self.stackOnUpdate)

	self.eventFrame = CreateFrame("Frame")
	self.eventFrame:RegisterEvent("VARIABLES_LOADED")
	--self.eventFrame:RegisterEvent("PLAYER_AURAS_CHANGED")
	self.eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
	self.eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
	self.eventFrame:RegisterEvent("COMBAT_TEXT_UPDATE")
	self.eventFrame:RegisterEvent("UNIT_COMBO_POINTS")
	self.eventFrame:SetScript("OnEvent", function (this, event, ...)
		if self[event] and type(self[event])=='function' then
			self[event](self, ...)
		end
	end)

	self.defaultDB = defaultDB

	SlashCmdList["BCF"] = function(msg)
		self:ShowMenu()
	end
	SLASH_BCF1 = "/bcf"

	SlashCmdList["BCFSCROLLAREA"] = function(msg)
		bcf:ToggleScrollAreas()
	end
	SLASH_BCFSCROLLAREA1 = "/bcfarea"
end

function bcf:stackOnUpdate(elapsed)
	LAST_STACK_UPDATE = LAST_STACK_UPDATE + elapsed
	if LAST_STACK_UPDATE > STACK_DELAY_TIME then
		LAST_STACK_UPDATE = 0
		for id, info in pairs(stacks) do
			if bcf[info.type] then
				bcf[info.type](bcf, info)
			end
			stacks[id] = deepDel(stacks[id])
		end
	end	
end

-------------------------------------------------------------------------------
-- Functions
-------------------------------------------------------------------------------

function bcf:ToggleScrollAreas()
	self.bScroll:ToggleScrollAreas()
end

function bcf:GetIcon(spellId)
	if db.showIcon and spellId then
		local _, _, icon = GetSpellInfo(spellId)
		return icon
	end
	return nil
end

function bcf:ShowMenu()
	local loaded, message = LoadAddOn("bcf_options");
	if (loaded) then
		PlaySound("igMainMenuOpen");
		ShowUIPanel(BCFOptions);
	else
		PlaySound("TellMessage");
		print("'bcf_option' Addon Error: "..message);
	end
end

function bcf:UpdateScrollLevel()
	if self.bScroll then
		self.bScroll:SetLevel(db.scrollLevel)
	end
end

function bcf:SyncScrollAreas(bScrollToDb)
	if bScrollToDb then
		local sas = self.bScroll:GetScrollAreas()
		for i=1, #db.scrollAreas do
			db.scrollAreas[i] = nil
		end
		for i=1, #sas do
			db.scrollAreas[i] = {
				name = sas[i].name,
				width = sas[i].width,
				height = sas[i].height,
				x = sas[i].x,
				y = sas[i].y,
				scrollTime = sas[i].scrollTime,
			}
			self.bScroll:UpdateScrollArea(sas[i].name, nil, nil, nil, nil, nil, function (x, y, width, height)
				if x then
					db.scrollAreas[i].x = x
				end
				if y then
					db.scrollAreas[i].y = y
				end
				if width then
					db.scrollAreas[i].width = width
				end
				if height then
					db.scrollAreas[i].height = height
				end
			end)
		end
	else
		local v, addonname
		for i=1, #db.scrollAreas do
			v = db.scrollAreas[i]
			if not self.bScroll:IsDefaultScrollArea(v.name) then
				addonname = addonName_ko
			end
			self.bScroll:UpdateScrollArea(v.name, v.width, v.height, v.x, v.y, v.scrollTime, function (x, y, width, height)
				if x then
					db.scrollAreas[i].x = x
				end
				if y then
					db.scrollAreas[i].y = y
				end
				if width then
					db.scrollAreas[i].width = width
				end
				if height then
					db.scrollAreas[i].height = height
				end
			end,
			addonname)
			addonname = nil
		end
	end
end


-------------------------------------------------------------------------------
-- Event Handlers
-------------------------------------------------------------------------------

function bcf:VARIABLES_LOADED()
	if not bcfDB then
		--print("새로운 db")

		db = {}
		tcopy(db, defaultDB)
		bcfDB = db
		self.db = bcfDB
	else
		--print("db loading")

		-- new cfg check
		tnilcopy(bcfDB, defaultDB)

		db = bcfDB
		self.db = db
	end

	self.bScroll = bScroll
	self.animationTypes = self.bScroll:GetAnimationTypes()

	self:UpdateCustomEventDB()
	self.bScroll:SetLevel(db.scrollLevel)
	self:SyncScrollAreas()
end

function bcf:PLAYER_REGEN_ENABLED()
	if db.showEnterCombat then
		local text = ("|cff%s%s|r"):format(db.enterCombat.color, db.enterCombat.leaveMsg)
		self.bScroll:Display(
			'message',		text,
			'type',			db.enterCombat.scrollType,
			'textsize',		db.enterCombat.size,
			'font',			db.enterCombat.font,
			'outline',		db.enterCombat.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.enterCombat.scrollArea),
			'align',		db.enterCombat.align
		)
	end
end

function bcf:PLAYER_REGEN_DISABLED()
	if db.showEnterCombat then
		local text = ("|cff%s%s|r"):format(db.enterCombat.color, db.enterCombat.enterMsg)
		self.bScroll:Display(
			'message',		text,
			'type',			db.enterCombat.scrollType,
			'textsize',		db.enterCombat.size,
			'font',			db.enterCombat.font,
			'outline',		db.enterCombat.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.enterCombat.scrollArea),
			'align',		db.enterCombat.align
		)
	end
end

function bcf:UNIT_HEALTH(...)
	local arg1 = select(1,...)
	if arg1 == "player" then
		self:FireHealthEvent("selfHealthLoss", "player")
		self:FireHealthEvent("selfHealthGain", "player")
	elseif arg1 == "pet" then
		self:FireHealthEvent("petHealthLoss", "pet")
		self:FireHealthEvent("petHealthGain", "pet")
	elseif arg1 == "target" then
		self:FireHealthEvent("targetHealthLoss", "target")
		self:FireHealthEvent("targetHealthGain", "target")
	end
end
function bcf:UNIT_MANA(...)
	local arg1 = select(1,...)
	if arg1 == "player" then
		self:FireManaEvent("selfManaLoss", "player")
		self:FireManaEvent("selfManaGain", "player")
	end
end
bcf.UNIT_RAGE = bcf.UNIT_MANA
bcf.UNIT_ENERGY = bcf.UNIT_MANA

function bcf:CHAT_MSG_COMBAT_FACTION_CHANGE(...)
--	local a1, a2, a3 = select(1,...)
--	if a1 then
--		print("CHAT_MSG_COMBAT_FACTION_CHANGE: "..a1)
--	end
--	if a2 then
--		print("CHAT_MSG_COMBAT_FACTION_CHANGE: "..a2)
--	end
--	if a3 then
--		print("CHAT_MSG_COMBAT_FACTION_CHANGE: "..a3)
--	end
end

function bcf:COMBAT_TEXT_UPDATE(...)
	local messageType, data = select(1,...)
	if messageType == "SPELL_ACTIVE" then
		self:FireCustomEvent("SpellActive", data)
	elseif messageType == "HONOR_GAINED" and db.showHonorGain then
		if ( tonumber(data) > 0 ) then
			data = "+"..data;
		end
	end
end

function bcf:UNIT_COMBO_POINTS(...)
	local arg1 = ...
	if db.showCombo and arg1 == "player" then
		local comboPoints = GetComboPoints("player", "target")
		local tt = db.combo.scrollType
		local size = db.combo.size
		if ( comboPoints > 0 ) then
			if ( comboPoints == MAX_COMBO_POINTS ) then
				tt = "crit"
				size = size * 1.1
			end
		else
			return
		end
		local msg = db.combo.msg
		if msg:find("%%a") then
			msg = msg:gsub("%%a", comboPoints)
		end
		msg = msg:gsub("%%a", "")
		local text = ("|cff%s%s|r"):format(db.combo.color, msg:trim())
		self.bScroll:Display(
			'message',		text,
			'type',			tt,
			'textsize',		size,
			'font',			db.combo.font,
			'outline',		db.combo.outline,
			'area',			self.bScroll:GetScrollArea(db.combo.scrollArea),
			'align',		db.combo.align
		)
	end
end

function bcf:parser(info)
	if not db or not db.enable or not info then return end

	if db.stack then
		self:stack(info)
	elseif self[info.type] then
		self[info.type](self, info)
	end

	if db.useCustomEvent then
		self:ParserCustomEvent(info)
	end
end

function bcf:stack(info)
	local id

	if info.type ~= 'hit'
	and info.type ~= 'heal'
	and info.type ~= 'miss'
	and info.type ~= 'energize'
	and info.type ~= 'drain'
	and info.type ~= 'leech'
	and info.type ~= 'buff'
	and info.type ~= 'debuff'
	then
		if self[info.type] then
			self[info.type](self, info) -- 조건에 해당하지 않는 type은 그냥 메세지 출력
		end
		return
	end

--	if info.spell.id and info.source.name and info.dest.name then
--		id = info.type .. "_" .. info.spell.id .. "_" .. info.source.name .. "_" .. info.dest.name
--	elseif info.spell.id and info.source.name and not info.dest.name then
--		id = info.type .. "_" .. info.spell.id .. "_" .. info.source.name
--	elseif info.spell.id and not info.source.name and info.dest.name then
--		id = info.type .. "_" .. info.spell.id .. "_" .. info.dest.name
--	elseif info.spell.id and not info.source.name and not info.dest.name then
--		id = info.type .. "_" .. info.spell.id
--	elseif not info.spell.id and info.source.name and info.dest.name then
--		id = info.type .. "_" .. info.source.name .. "_" .. info.dest.name
--	elseif not info.spell.id and info.source.name and not info.dest.name then
--		id = info.type .. "_" .. info.source.name
--	elseif not info.spell.id and not info.source.name and info.dest.name then
--		id = info.type .. "_" .. info.dest.name
--	else
--		id = info.type
--	end

	id = info.type
	if info.spell.id then

		-- 죽기꺼
		if info.spell.id == 66974 then -- 보조무기로 때리는 죽음의 기사의 절멸id
			info.spell.id = 51425
		end
		if info.spell.id == 66979 then -- 보조무기로 때리는 죽음의 기사의 피의 일격id
			info.spell.id = 49930
		end
		if info.spell.id == 66217 then -- 보조무기로 때리는 죽음의 기사의 룬의 일격id
			info.spell.id = 56815
		end
		if info.spell.id == 66953 then -- 보조무기로 때리는 죽음의 기사의 죽음의 일격id
			info.spell.id = 49924
		end
		if info.spell.id == 66992 then -- 보조무기로 때리는 죽음의 기사의 역병의 일격id
			info.spell.id = 49921
		end
		if info.spell.id == 66962 then -- 보조무기로 때리는 죽음의 기사의 냉기의 일격id
			info.spell.id = 55268
		end
		--[[
		if info.spell.id == 70890 then -- 스컬지의 일격의 암흑속성과 물리속성 데미지를 합해서 보여준다.
			info.spell.id = 55271
			info.isCrit = 1
		end
		]]

		-- 도적꺼
		if info.spell.id == 34418 then -- 보조무기로 때리는 도적의 절단id
			info.spell.id = 34419
		end

		-- 전사꺼
		if info.spell.id == 44949 then -- 보조무기로 때리는 전사의 소용돌이id
			info.spell.id = 1680
		end


		--[[
		if info.spell.name and info.spell.name == "스컬지의 일격" then
			print("스컬지의 일격(" .. info.spell.id..")")
			-- 70890
			-- 55271 ori
		end
		if info.spell.name and info.spell.name == "소용돌이" then
			print("소용돌이(" .. info.spell.id..")")
			-- 44949
			-- 1680 ori
		end
		if info.spell.name and info.spell.name == "절단" then
			print("절단(" .. info.spell.id..")")
			-- 34419
			-- 34418 ori
		end
		if info.spell.name and info.spell.name == "절멸" then
			print("절멸(" .. info.spell.id..")")
			-- 66974
			-- 51425 ori
		end
		if info.spell.name and info.spell.name == "피의 일격" then
			print("피의 일격(" .. info.spell.id..")")
			-- 49930 ori
			-- 66979
		end
		if info.spell.name and info.spell.name == "룬의 일격" then
			print("룬의 일격(" .. info.spell.id..")")
			-- 66217
			-- 56815 ori
		end
		if info.spell.name and info.spell.name == "죽음의 일격" then
			print("죽음의 일격(" .. info.spell.id..")")
			-- 66953
			-- 49924 ori
		end
		if info.spell.name and info.spell.name == "역병의 일격" then
			print("역병의 일격(" .. info.spell.id..")")
			-- 66992
			-- 49921 ori
		end
		if info.spell.name and info.spell.name == "냉기의 일격" then
			print("냉기의 일격(" .. info.spell.id..")")
			-- 55268 ori
			-- 66962
		end
		]]
		id = id .. "_" .. info.spell.id
	end

	if info.source.me or info.source.myGuardian or info.source.myObject then
		id = id .. "_OG"
	elseif info.dest.me or info.dest.myGuardian or info.dest.myObject then
		id = id .. "_IC"
	elseif info.source.myPet then
		id = id .. "_OGP"
	elseif info.dest.myPet then
		id = id .. "_ICP"
	elseif info.source.isFocus then
		id = id .. "_OGF"
	elseif info.dest.isFocus then
		id = id .. "_ICF"
	end

	if not stacks[id] then
		--stacks[id] = new()
		stacks[id] = copyTable(info)
		stacks[id].count = 1
		stacks[id].critsAmount = info.isCrit and info.amount or 0
		stacks[id].crits = info.isCrit and 1 or 0
	else
		local temp = copyTable(info)

		temp.count = (stacks[id].count or 0) + 1

		temp.amount = (stacks[id].amount or 0) + (info.amount or 0)
		temp.overkill = (stacks[id].overkill or 0) + (info.overkill or 0)
		temp.overhealAmount = (stacks[id].overhealAmount or 0) + (info.overhealAmount or 0)
		temp.critsAmount = (stacks[id].critsAmount or 0 ) + (info.isCrit and info.amount or 0)
		temp.crits = (stacks[id].crits or 0) + (info.isCrit and 1 or 0)

		temp.isCrit = stacks[id].isCrit and info.isCrit -- 모두 크리일경우 크리로.. and를 or로 바꾸면 하나라도 크리이면 크리로..(?_?)

		temp.extraAmount = (stacks[id].extraAmount or 0) + (info.extraAmount or 0)
		
		temp.resisted = (stacks[id].resisted or 0) + (info.resisted or 0)
		temp.blocked = (stacks[id].blocked or 0) + (info.blocked or 0)
		temp.absorbed = (stacks[id].absorbed or 0) + (info.absorbed or 0)
		temp.glancing = (stacks[id].glancing or 0) + (info.glancing or 0)
		temp.crushing = stacks[id].crushing or info.crushing or nil

		stacks[id] = copyTable(temp)
	end

end

-------------------------------------------------------------------------------
-- Parser Event Handlers
-------------------------------------------------------------------------------

function bcf:hit(info)
	local text, icon, alpha, size, cfg
	if info.isEnvironmental then
		info.spell.name = TABLE_ENVIRONMENT_TYPES[info.spell.name] or nil
	end

	if (info.dest.me or info.dest.myGuardian or info.dest.myObject) and db.showIncomingHit then
		if info.isCrit then
			cfg = db.incomingHit.crit
		else
			cfg = db.incomingHit
		end
	elseif info.dest.myPet and db.showIncomingPetHit then
		if info.isCrit then
			cfg = db.incomingPetHit.crit
		else
			cfg = db.incomingPetHit
		end
		alpha = 50
	elseif (info.source.me or info.source.myGuardian or info.source.myObject) and db.showOutgoingHit then
		if info.isCrit then
			cfg = db.outgoingHit.crit
		else
			cfg = db.outgoingHit
		end
		--[[if info.dest and info.dest.isTarget then
		else
			alpha = 50
		end]]
	elseif info.source.myPet and db.showOutgoingPetHit then
		if info.isCrit then
			cfg = db.outgoingPetHit.crit
		else
			cfg = db.outgoingPetHit
		end
		alpha = 50
	end

	if cfg then
		if cfg.filter > 0 and info.amount < cfg.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(cfg.msg, info)
		size = cfg.size
		if info.isDOT then
			if not cfg.showDot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			cfg.scrollType,
			'textsize',		size,
			'font',			cfg.font,
			'outline',		cfg.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(cfg.scrollArea),
			'align',		cfg.align,
			'iconalign',	cfg.iconalign,
			'maxAlpha',		alpha
		)
	end


	if db.showIncomingFocusHit and info.dest.isFocus then
		if info.isCrit then
			cfg = db.incomingFocusHit.crit
		else
			cfg = db.incomingFocusHit
		end
		if cfg.filter > 0 and info.amount < cfg.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(cfg.msg, info)
		size = cfg.size
		if info.isDOT then
			if not cfg.showDot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			cfg.scrollType,
			'textsize',		size,
			'font',			cfg.font,
			'outline',		cfg.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(cfg.scrollArea),
			'align',		cfg.align,
			'iconalign',	cfg.iconalign
		)
	end

	if db.showOutgoingFocusHit and info.source.isFocus then
		if info.isCrit then
			cfg = db.outgoingFocusHit.crit
		else
			cfg = db.outgoingFocusHit
		end
		if cfg.filter > 0 and info.amount < cfg.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(cfg.msg, info)
		size = cfg.size
		if info.isDOT then
			if not cfg.showDot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			cfg.scrollType,
			'textsize',		size,
			'font',			cfg.font,
			'outline',		cfg.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(cfg.scrollArea),
			'align',		cfg.align,
			'iconalign',	cfg.iconalign
		)
	end
end

function bcf:miss(info)
	local text, icon, alpha, cfg

	if (info.source.me or info.source.myGuardian or info.source.myObject) and db.showOutgoingHit and db.outgoingHit.showMiss then -- 내가 때린게 miss인경우 (나쁜거)
		cfg = db.outgoingHit
	elseif info.source.myPet and db.showOutgoingPetHit and db.outgoingPetHit.showMiss then -- 펫이 때린게 miss인경우 (나쁜거)
		cfg = db.outgoingPetHit
		alpha = 50
	elseif (info.dest.me or info.dest.myGuardian or info.dest.myObject) and db.showIncomingHit and db.incomingHit.showMiss then -- 맞은게 miss (좋은거)
		cfg = db.incomingHit
	elseif info.dest.myPet and db.showIncomingPetHit and db.incomingPetHit.showMiss then -- 펫이 맞은게 miss (좋은거)
		cfg = db.incomingPetHit
		alpha = 50
	end

	if cfg then
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(cfg.missMsg, info)
		self.bScroll:Display(
			'message',		text,
			'type',			cfg.scrollType,
			'textsize',		cfg.size,
			'font',			cfg.font,
			'outline',		cfg.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(cfg.scrollArea),
			'align',		cfg.align,
			'iconalign',	cfg.iconalign,
			'maxAlpha',		alpha
		)
	end

	if db.showOutgoingFocusHit and db.outgoingFocusHit.showMiss and info.source.isFocus then -- 주시대상이 때린게 miss인경우 (나쁜거)
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.outgoingFocusHit.missMsg, info)
		self.bScroll:Display(
			'message',		text,
			'type',			db.outgoingFocusHit.scrollType,
			'textsize',		db.outgoingFocusHit.size,
			'font',			db.outgoingFocusHit.font,
			'outline',		db.outgoingFocusHit.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.outgoingFocusHit.scrollArea),
			'align',		db.outgoingFocusHit.align,
			'iconalign',	db.outgoingFocusHit.iconalign
		)
	end

	if db.showIncomingFocusHit and db.incomingFocusHit.showMiss and info.dest.isFocus then -- 주시대상이 맞은게 miss (좋은거)
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.incomingFocusHit.missMsg, info)
		self.bScroll:Display(
			'message',		text,
			'type',			db.incomingFocusHit.scrollType,
			'textsize',		db.incomingFocusHit.size,
			'font',			db.incomingFocusHit.font,
			'outline',		db.incomingFocusHit.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.incomingFocusHit.scrollArea),
			'align',		db.incomingFocusHit.align,
			'iconalign',	db.incomingFocusHit.iconalign
		)
	end
end

function bcf:heal(info)
	local text, icon, size, cfg
	if info.dest.me and db.showIncomingHeal then
		if info.isCrit then
			cfg = db.incomingHeal.crit
		else
			cfg = db.incomingHeal
		end
	elseif info.source.me and db.showOutgoingHeal then
		if info.isCrit then
			cfg = db.outgoingHeal.crit
		else
			cfg = db.outgoingHeal
		end
	end

	if cfg then
		if cfg.filter > 0 and info.amount < cfg.filter then return end
		text = MessageFormatHeal(cfg.msg, info)
		icon = self:GetIcon(info.spell.id)
		size = cfg.size
		if info.isDOT then
			if not cfg.showHot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			cfg.scrollType,
			'textsize',		size,
			'font',			cfg.font,
			'outline',		cfg.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(cfg.scrollArea),
			'align',		cfg.align,
			'iconalign',	cfg.iconalign
		)
	end
end

function bcf:energize(info)
	local text, icon, size
	if info.dest.me and db.showEnergize then
		if db.energize.filter > 0 and info.amount < db.energize.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.energize.msg, info)
		size = db.energize.size
		if info.isDOT then
			if not db.energize.showHot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			db.energize.scrollType,
			'textsize',		size,
			'font',			db.energize.font,
			'outline',		db.energize.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.energize.scrollArea),
			'align',		db.energize.align,
			'iconalign',	db.energize.iconalign
		)
	end
end

function bcf:drain(info)
	local text, icon, size
	if info.dest.me and db.showIncomingHit then
		if db.incomingHit.filter > 0 and info.amount < db.incomingHit.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.incomingHit.drainMsg, info)
		size = db.incomingHit.size
		if info.isDOT then
			if not db.incomingHit.showDot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			db.incomingHit.scrollType,
			'textsize',		size,
			'font',			db.incomingHit.font,
			'outline',		db.incomingHit.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.incomingHit.scrollArea),
			'align',		db.incomingHit.align,
			'iconalign',	db.incomingHit.iconalign
		)
	elseif info.source.me and db.showOutgoingHit then
		if db.outgoingHit.filter > 0 and info.amount < db.outgoingHit.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.outgoingHit.drainMsg, info)
		size = db.outgoingHit.size
		if info.isDOT then
			if not db.outgoingHit.showDot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			db.outgoingHit.scrollType,
			'textsize',		size,
			'font',			db.outgoingHit.font,
			'outline',		db.outgoingHit.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.outgoingHit.scrollArea),
			'align',		db.outgoingHit.align,
			'iconalign',	db.outgoingHit.iconalign
		)
	end
end

function bcf:leech(info)
	local text, icon, size
	if info.source.me and db.showEnergize then
		if db.energize.filter > 0 and info.amount < db.energize.filter then return end
		icon = self:GetIcon(info.spell.id)
		text = MessageFormat(db.energize.leechMsg, info)
		size = db.energize.size
		if info.isDOT then
			if not db.energize.showHot then return end
			size = size * 0.9
		end
		self.bScroll:Display(
			'message',		text,
			'type',			db.energize.scrollType,
			'textsize',		size,
			'font',			db.energize.font,
			'outline',		db.energize.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.energize.scrollArea),
			'align',		db.energize.align,
			'iconalign',	db.energize.iconalign
		)
	end
end

function bcf:interrupt(info)
	local text, icon
	if info.source.me and db.showInterrupt then
		text = MessageFormat(db.interrupt.msg, info)
		icon = self:GetIcon(info.extraSpell.id)
		self.bScroll:Display(
			'message',		text,
			'type',			db.interrupt.scrollType,
			'textsize',		db.interrupt.size,
			'font',			db.interrupt.font,
			'outline',		db.interrupt.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.interrupt.scrollArea),
			'align',		db.interrupt.align,
			'iconalign',	db.interrupt.iconalign
		)
	end
end

function bcf:extraattack(info)
	local text, icon
	if info.source.me and db.showExtraAttack then
		text = MessageFormat(db.extraattack.msg, info)
		icon = self:GetIcon(info.spell.id)
		self.bScroll:Display(
			'message',		text,
			'type',			db.extraattack.scrollType,
			'textsize',		db.extraattack.size,
			'font',			db.extraattack.font,
			'outline',		db.extraattack.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.extraattack.scrollArea),
			'align',		db.extraattack.align,
			'iconalign',	db.extraattack.iconalign
		)
	end
end

function bcf:reputation(info)
	local text
	if type(info.amount)=='number' then
		if info.amount > 0 then
			text = ("%s +|cff009919%d|r"):format(info.faction, info.amount)
		else
			text = ("%s -|cff991900%d|r"):format(info.faction, info.amount*-1)
		end
	elseif type(info.amount)=='string' then
		text = ("%s |cff009919%s|r"):format(info.faction, info.amount)
	else
		return
	end
	-- show or not?
	if ( db.autoChangeReputation ) then
		local watchedName = GetWatchedFactionInfo()
		if ( not watchedName or (watchedName and watchedName ~= info.faction) ) then
			local numFactions = GetNumFactions()
			for i=1, numFactions, 1 do
				local factionName = GetFactionInfo(i)
				if ( factionName == info.faction ) then
					SetWatchedFactionIndex(i)
					break
				end
			end
		end
	end

	self.bScroll:Display(
		'message',		text,
		'type',			db.reputation.scrollType,
		'textsize',		db.reputation.size,
		'font',			db.reputation.font,
		'outline',		db.reputation.outline,
		'area',			self.bScroll:GetScrollArea(db.reputation.scrollArea),
		'align',		db.reputation.align
	)
end

function bcf:enchant(info)
	local text, icon
	if info.source.me and db.showEnchant then
		icon = self:GetIcon(info.spell.id)
		if info.isFade then
			text = ("%s |cffff0202%s|r 사라짐"):format(info.item.name, info.spell.name)
		else
			text = ("%s |cff02ff02%s|r 강화"):format(info.item.name, info.spell.name)
		end
		self.bScroll:Display(
			'message',		text,
			'type',			db.enchant.scrollType,
			'textsize',		db.enchant.size,
			'font',			db.enchant.font,
			'outline',		db.enchant.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.enchant.scrollArea),
			'align',		db.enchant.align,
			'iconalign',	db.enchant.iconalign
		)
	end
end

function bcf:buff(info)
	local text, icon

	-- 재버프는 다시 보여주지 않는다.
	if info.isRefresh then
		return
	end
	if info.dest.me and not info.isFade and db.showBuff then
		if info.spell.name then
			--[[if (bcf.excepts.buff[myClass] and bcf.excepts.buff[myClass][info.spell.name]) or bcf.excepts.buff["*"][info.spell.name] then
				return
			end]]
			if db.exceptAuras and db.exceptAuras[info.spell.name] then
				return
			end
		end
		if db.showIcon then
			if not activeBuffsIconCache[info.spell.name] then
				local i = 1
				local name, rank, texture = UnitBuff("player", i)
				while texture do
					if name == info.spell.name then
						activeBuffsIconCache[info.spell.name] = texture
						break
					end
					i = i + 1
					name, rank, texture = UnitBuff("player", i)
				end
			end
			icon = activeBuffsIconCache[info.spell.name]
		end
		text = MessageFormatAura(db.buff.msg, info, db.buff.color)
		self.bScroll:Display(
			'message',		text,
			'type',			db.buff.scrollType,
			'textsize',		db.buff.size,
			'font',			db.buff.font,
			'outline',		db.buff.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.buff.scrollArea),
			'align',		db.buff.align,
			'iconalign',	db.buff.iconalign
		)
	elseif info.dest.me and info.isFade and db.showBuffFade then
		if info.spell.name then
			--[[if (bcf.excepts.fade[myClass] and bcf.excepts.fade[myClass][info.spell.name]) or bcf.excepts.fade["*"][info.spell.name] then
				return
			end]]
			if db.exceptAuras and db.exceptAuras[info.spell.name] then
				return
			end
		end
		icon = activeBuffsIconCache[info.spell.name] or self:GetIcon(info.spell.id)
		text = MessageFormatAura(db.bufffade.msg, info, db.bufffade.color)
		self.bScroll:Display(
			'message',		text,
			'type',			db.bufffade.scrollType,
			'textsize',		db.bufffade.size,
			'font',			db.bufffade.font,
			'outline',		db.bufffade.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.bufffade.scrollArea),
			'align',		db.bufffade.align,
			'iconalign',	db.bufffade.iconalign
		)
	end
end

function bcf:debuff(info)
	local text, icon

	-- 재버프는 다시 보여주지 않는다.
	if info.isRefresh then
		return
	end
	if info.dest.me and not info.isFade and db.showDebuff then
		if info.spell.name then
			--[[if (bcf.excepts.debuff[myClass] and bcf.excepts.debuff[myClass][info.spell.name]) or bcf.excepts.debuff["*"][info.spell.name] then
				return
			end]]
			if db.exceptAuras and db.exceptAuras[info.spell.name] then
				return
			end
		end
		if db.showIcon then
			if not activeDebuffsIconCache[info.spell.name] then
				local i = 1
				local name, rank, texture = UnitDebuff("player", i)
				while texture do
					if name == info.spell.name then
						activeDebuffsIconCache[info.spell.name] = texture
						break
					end
					i = i + 1
					name, rank, texture = UnitDebuff("player", i)
				end
			end
			icon = activeDebuffsIconCache[info.spell.name]
		end
		text = MessageFormatAura(db.debuff.msg, info, db.debuff.color)
		self.bScroll:Display(
			'message',		text,
			'type',			db.debuff.scrollType,
			'textsize',		db.debuff.size,
			'font',			db.debuff.font,
			'outline',		db.debuff.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.debuff.scrollArea),
			'align',		db.debuff.align,
			'iconalign',	db.debuff.iconalign
		)
	elseif info.dest.me and info.isFade and db.showDebuffFade then
		if info.spell.name then
			if (bcf.excepts.fade[myClass] and bcf.excepts.fade[myClass][info.spell.name]) or bcf.excepts.fade["*"][info.spell.name] then
				return
			end
		end
		icon = activeDebuffsIconCache[info.spell.name] or self:GetIcon(info.spell.id)
		text = MessageFormatAura(db.debufffade.msg, info, db.debufffade.color)
		self.bScroll:Display(
			'message',		text,
			'type',			db.debufffade.scrollType,
			'textsize',		db.debufffade.size,
			'font',			db.debufffade.font,
			'outline',		db.debufffade.outline,
			'icon',			icon,
			'iconborder',	db.showIconBorder,
			'area',			self.bScroll:GetScrollArea(db.debufffade.scrollArea),
			'align',		db.debufffade.align,
			'iconalign',	db.debufffade.iconalign
		)
	end
end


-------------------------------------------------------------------------------
-- Event customEvents
-------------------------------------------------------------------------------

function bcf:ParserCustomEvent(info)

	-- crit
	if info.type == 'hit' and info.source.me and info.isCrit then
		self:FireCustomEvent("outgoingCrit", info.spell.name, info.source.name, info.dest.name, info.amount)
	elseif info.type == 'hit' and info.dest.me and info.isCrit then
		self:FireCustomEvent("incomingCrit", info.spell.name, info.source.name, info.dest.name, info.amount)
	elseif info.type == 'hit' and info.source.myPet and info.isCrit then
		self:FireCustomEvent("outgoingPetCrit", info.spell.name, info.source.name, info.dest.name, info.amount)
	elseif info.type == 'hit' and info.dest.myPet and info.isCrit then
		self:FireCustomEvent("incomingPetCrit", info.spell.name, info.source.name, info.dest.name, info.amount)

	-- player miss
	elseif info.type == 'miss' and (info.source.me or info.source.myGuardian or info.source.myObject) and info.missType == BLOCK then
		self:FireCustomEvent("outgoingBlock", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.dest.me or info.dest.myGuardian or info.dest.myObject) and info.missType == BLOCK then
		self:FireCustomEvent("incomingBlock", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.source.me or info.source.myGuardian or info.source.myObject) and info.missType == DODGE then
		self:FireCustomEvent("outgoingDodge", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.dest.me or info.dest.myGuardian or info.dest.myObject) and info.missType == DODGE then
		self:FireCustomEvent("incomingDodge", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.source.me or info.source.myGuardian or info.source.myObject) and info.missType == PARRY then
		self:FireCustomEvent("outgoingParry", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.dest.me or info.dest.myGuardian or info.dest.myObject) and info.missType == PARRY then
		self:FireCustomEvent("incomingParry", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.source.me or info.source.myGuardian or info.source.myObject) and info.missType == RESIST then
		self:FireCustomEvent("outgoingResist", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.dest.me or info.dest.myGuardian or info.dest.myObject) and info.missType == RESIST then
		self:FireCustomEvent("incomingResist", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.source.me or info.source.myGuardian or info.source.myObject) and info.missType == IMMUNE then
		self:FireCustomEvent("outgoingImmune", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and (info.dest.me or info.dest.myGuardian or info.dest.myObject) and info.missType == IMMUNE then
		self:FireCustomEvent("incomingImmune", info.spell.name, info.source.name, info.dest.name)

	-- pet miss
	elseif info.type == 'miss' and info.source.myPet and info.missType == BLOCK then
		self:FireCustomEvent("outgoingPetBlock", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.dest.myPet and info.missType == BLOCK then
		self:FireCustomEvent("incomingPetBlock", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.source.myPet and info.missType == DODGE then
		self:FireCustomEvent("outgoingPetDodge", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.dest.myPet and info.missType == DODGE then
		self:FireCustomEvent("incomingPetDodge", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.source.myPet and info.missType == PARRY then
		self:FireCustomEvent("outgoingPetParry", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.dest.myPet and info.missType == PARRY then
		self:FireCustomEvent("incomingPetParry", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.source.myPet and info.missType == RESIST then
		self:FireCustomEvent("outgoingPetResist", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.dest.myPet and info.missType == RESIST then
		self:FireCustomEvent("incomingPetResist", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.source.myPet and info.missType == IMMUNE then
		self:FireCustomEvent("outgoingPetImmune", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'miss' and info.dest.myPet and info.missType == IMMUNE then
		self:FireCustomEvent("incomingPetImmune", info.spell.name, info.source.name, info.dest.name)

	-- cast
	elseif info.type == 'cast' and (info.source.me or info.dest.isTarget) and info.castType == 'start' then
		self:FireCustomEvent("outgoingCastStart", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'cast' and info.source.me and info.castType == 'success' then
		self:FireCustomEvent("outgoingCastSuccess", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'cast' and info.source.me and info.castType == 'fail' then
		self:FireCustomEvent("outgoingCastFail", info.spell.name, info.source.name, info.dest.name, info.reason)
	elseif info.type == 'cast' and (info.dest.me or info.source.isTarget) and info.castType == 'start' then
		self:FireCustomEvent("incomingCastStart", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'cast' and info.dest.me and info.castType == 'success' then
		self:FireCustomEvent("incomingCastSuccess", info.spell.name, info.source.name, info.dest.name)
	elseif info.type == 'cast' and info.dest.me and info.castType == 'fail' then
		self:FireCustomEvent("incomingCastFail", info.spell.name, info.source.name, info.dest.name, info.reason)


	-- death
	elseif info.type == 'death' and info.source.me then
		self:FireCustomEvent("killblow", info.dest.name)


	-- buff/debuff
	elseif info.type == 'buff' and not info.isRefresh then
		if info.isFade then
			if info.dest.me then
				self:FireCustomEvent("selfBuffFade", info.spell.name)
			elseif info.dest.myPet then
				self:FireCustomEvent("petBuffFade", info.spell.name)
			end
			if info.dest.isTarget then
				self:FireCustomEvent("targetBuffFade", info.spell.name)
			end
		else
			if info.dest.me then
				self:FireCustomEvent("selfBuff", info.spell.name)
			elseif info.dest.myPet then
				self:FireCustomEvent("petBuff", info.spell.name)
			end
			if info.dest.isTarget then
				self:FireCustomEvent("targetBuff", info.spell.name)
			end
		end
	elseif info.type == 'debuff' and not info.isRefresh then
		if info.isFade then
			if info.dest.me then
				self:FireCustomEvent("selfDebuffFade", info.spell.name)
			elseif info.dest.myPet then
				self:FireCustomEvent("petDebuffFade", info.spell.name)
			end
			if info.dest.isTarget then
				self:FireCustomEvent("targetDebuffFade", info.spell.name)
			end
		else
			if info.dest.me then
				self:FireCustomEvent("selfDebuff", info.spell.name)
			end
			if info.dest.myPet then
				self:FireCustomEvent("petDebuff", info.spell.name)
			end
			if info.dest.isTarget then
				self:FireCustomEvent("targetDebuff", info.spell.name)
			end
		end
	end
end

function bcf:FireCustomEvent(conditionType, value, ...)
	if customEvents[conditionType] and #customEvents[conditionType] > 0 then
		--print(conditionType .. " / " .. (value or "-nil-"))
		for i=1, #customEvents[conditionType] do
			if ClassCheck(customEvents[conditionType][i].class) and (not customEvents[conditionType][i].value or customEvents[conditionType][i].value == value) then
				if customEvents[conditionType][i].subConditions then --and #customEvents[conditionType][i].subConditions>0 then
					--print("여기오냐?")
					if self:CheckSubCondition(customEvents[conditionType][i].subConditions, customEvents[conditionType][i]) then
						self:DisplayCustomEvent(customEvents[conditionType][i], value, ...)
					end
				else
					self:DisplayCustomEvent(customEvents[conditionType][i], value, ...)
				end
			end
		end
	end
end

function bcf:FireHealthEvent(conditionType, unit)
	if customEvents[conditionType] and #customEvents[conditionType] > 0 then
		local condi = false
		local curPercent = (UnitHealth(unit) / UnitHealthMax(unit)) * 100
		for i=1, #customEvents[conditionType] do
			if conditionType:find("Loss$") then
				if curPercent < customEvents[conditionType][i].value then
					condi = true
				end
			elseif conditionType:find("Gain$") then
				if curPercent >= customEvents[conditionType][i].value then
					condi = true
				end
			end
			if condi and ClassCheck(customEvents[conditionType][i].class) then
				if not hmChecker[customEvents[conditionType][i]] then
					if customEvents[conditionType][i].subConditions then
						if self:CheckSubCondition(customEvents[conditionType][i].subConditions, customEvents[conditionType][i]) then
							self:DisplayCustomEvent(customEvents[conditionType][i], math.floor(curPercent), UnitName(unit), UnitHealth(unit))
							hmChecker[customEvents[conditionType][i]] = true
						end
					else
						self:DisplayCustomEvent(customEvents[conditionType][i], math.floor(curPercent), UnitName(unit), UnitHealth(unit))
						hmChecker[customEvents[conditionType][i]] = true
					end
				end
			else
				hmChecker[customEvents[conditionType][i]] = nil
			end
			condi = false
		end
	end
end
function bcf:FireManaEvent(conditionType, unit)
	if customEvents[conditionType] and #customEvents[conditionType] > 0 then
		local condi = false
		local curPercent = (UnitMana(unit) / UnitManaMax(unit)) * 100
		for i=1, #customEvents[conditionType] do
			if conditionType:find("Loss$") then
				if curPercent < customEvents[conditionType][i].value then
					condi = true
				end
			elseif conditionType:find("Gain$") then
				if curPercent >= customEvents[conditionType][i].value then
					condi = true
				end
			end
			if condi and ClassCheck(customEvents[conditionType][i].class) then
				if not hmChecker[customEvents[conditionType][i]] then
					if customEvents[conditionType][i].subConditions then
						if self:CheckSubCondition(customEvents[conditionType][i].subConditions, customEvents[conditionType][i]) then
							self:DisplayCustomEvent(customEvents[conditionType][i], math.floor(curPercent), UnitName(unit), UnitMana(unit))
							hmChecker[customEvents[conditionType][i]] = true
						end
					else
						self:DisplayCustomEvent(customEvents[conditionType][i], math.floor(curPercent), UnitName(unit), UnitMana(unit))
						hmChecker[customEvents[conditionType][i]] = true
					end
				end
			else
				hmChecker[customEvents[conditionType][i]] = nil
			end
			condi = false
		end
	end
end

function bcf:CheckSubCondition(subConditions, event)
	for subConditionType, values in pairs(subConditions) do
		if not self:FireSubCondition(subConditionType, values, event) then
			return false
		end
	end
	return true
end
function bcf:FireSubCondition(subConditionType, values, event)
	--print(subConditionType)
	local returnValue = true
	if subConditionType == "SpellReady" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not CheckSpellReady(k) then
					return false
				end
			end
			return true
		else
			return CheckSpellReady(values)
		end
	elseif subConditionType == "SpellUsable" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not IsUsableSpell(k) then
					return false
				end
			end
			return true
		else
			return IsUsableSpell(values)
		end
	elseif subConditionType == "MinPower" then
		local mana, powerType = values, 0 -- 값이 숫자라면 바로 비교대상으로 지정한다.
		if type(values)=="string" then -- 값이 문자라면 "스펠명"으로 간주하고 해당 스펠의 cost를 찾는다.
			mana, powerType = GetSpellRequireCost(values)
		end
		if powerType == -2 and UnitHealth("player") >= tonumber(mana) then -- powerType -2 => health
			return true
		elseif UnitMana("player") >= tonumber(mana) then
			return true
		else
			return false
		end
	elseif subConditionType == "hasBuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not UnitBuff("player", k) then
					return false
				end
			end
			return true
		else
			return UnitBuff("player", values) and true or false
		end
	elseif subConditionType == "hasNoBuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if UnitBuff("player", k) then
					return false
				end
			end
			return true
		else
			return not UnitBuff("player", values)
		end
	elseif subConditionType == "BuffCount" then
		if type(values)=='table' then
			--print("테이블 맞음")
			local buffname, _, _, buffcount = UnitBuff("player", values[1])
			--print("buffcount = " .. buffcount)
			--print("values[2] = " .. values[2])
			if buffname and buffcount == tonumber(values[2]) then
				return true
			end
		end
	elseif subConditionType == "hasDebuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not UnitDebuff("player", k) then
					return false
				end
			end
			return true
		else
			return UnitDebuff("player", values) and true or false
		end
	elseif subConditionType == "hasNoDebuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if UnitDebuff("player", k) then
					return false
				end
			end
			return true
		else
			return not UnitDebuff("player", values)
		end
	elseif subConditionType == "DebuffCount" then
		if type(values)=='table' then
			local buffname, _, _, buffcount = UnitDebuff("player", values[1])
			if buffname and buffcount == tonumber(values[2]) then
				return true
			end
		end
	elseif subConditionType == "Cooldown" and tonumber(values) > 0 then
		values = tonumber(values)
		local lastFire = cooldowns[event] or (-1*values)
		if GetTime()-lastFire >= values then
			cooldowns[event] = GetTime()
			return true
		end
	elseif subConditionType == "TargetIsPlayer" then
		return UnitExists("target") and UnitIsPlayer("target")
	elseif subConditionType == "TargetIsEnemy" then
		return UnitExists("target") and UnitIsEnemy("target", "player")
	elseif subConditionType == "TargetIsFriend" then
		return UnitExists("target") and UnitIsFriend("target", "player")
	elseif subConditionType == "WarriorStance" then
		if type(values)=='table' then
			return values[GetShapeshiftForm()]
		else
			return GetShapeshiftForm() == tonumber(values)
		end
	elseif subConditionType == "DruidForm" then
		if type(values)=='table' then
			return values[GetShapeshiftForm()]
		else
			return GetShapeshiftForm() == tonumber(values)
		end
	elseif subConditionType == "DKPresence" then
		if type(values)=='table' then
			return values[GetShapeshiftForm()]
		else
			return GetShapeshiftForm() == tonumber(values)
		end
	elseif subConditionType == "HonorKillLevel" then
		if UnitExists("target") then
			if (UnitLevel("target") < 0) or (UnitLevel("target") >= UnitLevel("player")-GetQuestGreenRange()) then
				return true
			end
		end
	end
end

function bcf:DisplayCustomEvent(event, value, ...)
	if event.display then
		local r, g, b = hexToDec(event.display.color or "ffffff")
		local msg = event.display.msg
		if value and msg:find("%%v") then
			msg = msg:gsub("%%v", tostring(value))
		end
		msg = msg:gsub("%%v", "")
		for i=1, select("#",...) do
			if msg:find("%%"..i) then
				msg = msg:gsub("%%"..i, tostring(select(i,...)))
			end
			msg = msg:gsub("%%"..i, "")
		end
		msg = msg:gsub("  "," ")
		self.bScroll:Display(
			'message',		msg:trim(),
			'area',			self.bScroll:GetScrollArea(event.display.scrollArea),
			'type',			event.display.scrollType,
			'textsize',		event.display.size,
			'outline',		event.display.outline,
			'align',		event.display.align,
			'font',			event.display.font,
			'r',			r,
			'g',			g,
			'b',			b
		)
		if event.display.sound then
			PlaySoundFile(event.display.sound)
		end
		if event.display.func then
			if type(event.display.func)=='function' then
				event.display.func()
			elseif type(event.display.func)=='string' then
				local func = getglobal(event.display.func)
				if func and type(func)=='function' then
					func()
				end
			end
		end
		if event.display.script then
			RunScript(event.display.script)
		end
	end
end

function bcf:UpdateCustomEventDB(silence)
	for k, v in pairs(customEvents) do
		customEvents[k] = nil
	end
	self.customFrame:UnregisterAllEvents()
	for k, v in pairs(customRegistedEvents) do
		customRegistedEvents[k] = nil
	end

--	self.customFrame:UnregisterAllEvents()
--	doAll(deepDel, customEvents, customRegistedEvents)

--	customEvents, customRegistedEvents = new(), new()

	if db.customEvents and #db.customEvents > 0 then
		for i=1, #db.customEvents do
			local t = db.customEvents[i]
			if t.condition then
				if not customEvents[t.condition] then
					customEvents[t.condition] = new()
				end
				local g = {}
				g.class = t.class
				if t.subConditions then
					g.subConditions = copyTable(t.subConditions)
				end

				if t.condition == "selfBuff"
				or t.condition == "petBuff"
				or t.condition == "targetBuff"
				or t.condition == "selfDebuff"
				or t.condition == "petDebuff"
				or t.condition == "targetDebuff"
				or t.condition == "selfFade"
				or t.condition == "petFade"
				or t.condition == "targetFade"
				then
					g.value = t.value
				else
					if t.value and t.value~="" then
						g.value = t.value
					end
				end

				g.display = {
					msg = t.msg,
					size = t.size,
					font = t.font,
					outline = t.outline,
					scrollArea = t.scrollArea,
					scrollType = t.scrollType,
					align = t.align,
					sound = t.sound,
					func = t.func,
					color = t.color,
					script = t.script,
				}
				if t.sound and t.sound~="" then
					if not db.registedSoundFiles then
						db.registedSoundFiles = {}
					end
					db.registedSoundFiles[t.sound] = true
				end
				if t.condition == 'selfHealthLoss'
				or t.condition == 'selfHealthGain'
				or t.condition == 'petHealthLoss'
				or t.condition == 'petHealthGain'
				or t.condition == 'targetHealthLoss'
				or t.condition == 'targetHealthGain'
				then
					customRegistedEvents["UNIT_HEALTH"] = true
				end
				if t.condition == 'selfManaLoss' or t.condition == 'selfManaGain' then
					customRegistedEvents["UNIT_MANA"] = true
					customRegistedEvents["UNIT_RAGE"] = true
					customRegistedEvents["UNIT_ENERGY"] = true
					customRegistedEvents["UNIT_RUNIC_POWER"] = true
				end

				if not t.class or (type(t.class)=='string' and t.class==myClass) or (type(t.class)=='table' and t.class[myClass]) then
					table.insert(customEvents[t.condition], g)
				end

				--del(g) -- memory clear
			end
		end
	end
	for k,_ in pairs(customRegistedEvents) do
		self.customFrame:RegisterEvent(k)
	end
end


bcf:OnLoad()
