
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 spellIDCache = {}
local spellCostCache = {}
local activeBuffsIconCache = {}
local activeDebuffsIconCache = {}
local currentAuras = {buffs={},debuffs={}}
local cooldowns = {}
local hmChecker = {}
local customEvents = {}
local customRegistedEvents = {}

local tooltip = CreateFrame("GameTooltip")
do
	bcf.tooltip = tooltip
	if not tooltip.left then
		tooltip.left = {}
		tooltip.right = {}
	end
	for i = #tooltip.left+1, 30 do
		local left, right = tooltip:CreateFontString(), tooltip:CreateFontString()
		tooltip.left[i] = left
		tooltip.right[i] = right
		left:SetFontObject(GameFontNormal)
		right:SetFontObject(GameFontNormal)
		tooltip:AddFontStrings(left, right)
	end
	tooltip:SetOwner(UIParent, "ANCHOR_NONE")
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_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


-- The many school types
local STRING_SCHOOL_PHYSICAL	= SPELL_SCHOOL0_NAME
local STRING_SCHOOL_FIRE		= SPELL_SCHOOL2_NAME
local STRING_SCHOOL_FROST		= SPELL_SCHOOL4_NAME
local STRING_SCHOOL_ARCANE		= SPELL_SCHOOL6_NAME
local STRING_SCHOOL_NATURE		= SPELL_SCHOOL3_NAME
local STRING_SCHOOL_SHADOW		= SPELL_SCHOOL5_NAME
local STRING_SCHOOL_HOLY		= SPELL_SCHOOL1_NAME
local STRING_SCHOOL_UNKNOWN		= "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

-- Result Types
local STRING_RESULT_RESISTED	= TEXT_MODE_A_STRING_RESULT_RESISTED --"저항"
local STRING_RESULT_BLOCKED		= TEXT_MODE_A_STRING_RESULT_BLOCKED --"방어"
local STRING_RESULT_ABSORBED	= TEXT_MODE_A_STRING_RESULT_ABSORBED --"흡수"
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,						-- 애드온 사용 여부

	showIncomingHit = true,				-- 받는 데미지 표시
	showIncomingPetHit = false,			-- 펫이 받는 데미지 표시
	showIncomingFocusHit = false,		-- 주시자가 받는 데미지 표시
	
	showOutgoingHit = false,				-- 주는 데미지 표시
	showOutgoingPetHit = false,			-- 펫이 주는 데미지 표시
	showOutgoingFocusHit = false,		-- 주시자가 주는 데미지 표시
	
	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 = {
--		{
--			name = "incoming",
--			width = 40,
--			height = 260,
--			x = -240,
--			y = -30,
--			scrollTime = 3,
--		},
--		{
--			name = "outgoing",
--			width = 40,
--			height = 260,
--			x = 240,
--			y = -30,
--			scrollTime = 3,
--		},
--		{
--			name = "NotificationUp",
--			width = 40,
--			height = 160,
--			x = 0,
--			y = 120,
--			scrollTime = 3,
--		},
--		{
--			name = "NotificationDown",
--			width = 40,
--			height = 160,
--			x = 0,
--			y = -120,
--			scrollTime = 3,
--		},
--		{
--			name = "Sticky",
--			width = 40,
--			height = 90,
--			x = 0,
--			y = -270,
--			scrollTime = 3,
--		},
--		{
--			name = "Crit",
--			width = 90,
--			height = 90,
--			x = 0,
--			y = 0,
--			scrollTime = 3,
--		},
	},
	enterCombat = {
		enterMsg = "+전투 시작",
		leaveMsg = "-전투 종료",
		size = 20,
		font = "Fonts\\K_Damage.TTF",
		outline = '',
		scrollArea = "NotificationDown",
		scrollType = "down",
		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,
		},
	},
	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,
		},
	},
	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,
		},
	},
	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,
		},
	},
	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,
		},
	},
	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,
		},
	},
	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 = "NotificationUp",
			scrollType = "crit",
			align = "center",
			iconalign = "right",
			filter = 0,
		},
	},
	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,
		},
	},
	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 = 'THICKOUTLINE',
		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 = '',
		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 = {
		physical = "ff0000",		-- 물리데미지 색상
		fire = "ff7f00",			-- 화염데미지 색상
		frost = "7fffff",		-- 냉기데미지 색상
		arcane = "ffff00",		-- 비전데미지 색상
		nature = "4cff4c",		-- 자연데미지 색상
		shadow = "ff7fff",		-- 암흑데미지 색상
		holy = "ffe57f",			-- 신성데미지 색상
		none = "ff0000",			-- 속성컬러옵션을 선택하지않으면 보여줄 색상
	},
	powerColor = {
		mana = "517fff",			-- 마나 색상
		rage = "ff0202",			-- 분노 색상
		energy = "ffff02",		-- 기력 색상
	},
	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,
	},

	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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			align = "center",
			size = 20,
			font = "Fonts\\K_Damage.TTF",
			scrollArea = "NotificationDown",
			color = "4c24ff",
			scrollType = "crit",
			class = {
				["WARRIOR"] = true,
			},
			condition = "outgoingCrit",
			subConditions = {
				["MinPower"] = 20,
				["SpellReady"] = "광란",
				["Cooldown"] = 5,
				["hasNoBuff"] = "광란",
			},
		},
		{
			msg = "<승리의 돌진>",
			outline = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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 = "THICKOUTLINE",
			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",
		},
	},
	icons = {},
}


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

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

local function copyTable(src)
	local dest = {}
	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 = {} end
			to[k] = {}
			tcopy(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 then return true end
	if type(class)=='table' then
		return class[myClass]
	else
		return class==myClass
	end
end

local function GetSpellID(spellName)
	local i = 1
	local sName = GetSpellName(i, BOOKTYPE_SPELL)
	while sName do
		if spellName == sName then
			return i
		end
		i = i + 1
		sName = GetSpellName(i, BOOKTYPE_SPELL)
	end
	return 0
end

local function CheckSpellReady(spellName) -- from MSBT
	if (not spellName or spellName == "") then return true end

	if (not spellIDCache[spellName]) then spellIDCache[spellName] = GetSpellID(spellName) end
	local spellID = spellIDCache[spellName]

	if (spellID == 0) then return false end

	-- Fail check if the spell is cooling down (but ignore the global cooldown).
	local start, duration = GetSpellCooldown(spellID, BOOKTYPE_SPELL)
	if (start > 0 and duration > 1.5) then return false end
	return true
end

local function GetSpellRequireCost(spellName)
	if (not spellIDCache[spellName]) then spellIDCache[spellName] = GetSpellID(spellName) end
	local spellID = spellIDCache[spellName]

	bcf.tooltip:ClearLines()
	bcf.tooltip:SetSpell(spellID, BOOKTYPE_SPELL)
	if not bcf.tooltip:IsOwned(UIParent) then
		bcf.tooltip:SetOwner(UIParent, "ANCHOR_NONE")
	end
	local line = bcf.tooltip.left[2]:GetText()
	if line then
		line = tonumber(line:match("(%d+)"))
	end
	return line or 0
end

local function updateCurrentAuras()
	for k, v in pairs(currentAuras.buffs) do
		currentAuras.buffs[k] = nil
	end
	for k, v in pairs(currentAuras.debuffs) do
		currentAuras.debuffs[k] = nil
	end

	local i = 1
	local buffName = UnitBuff("player", i)
	while buffName do
		currentAuras.buffs[buffName] = true
		i = i + 1
		buffName = UnitBuff("player", i)
	end

	i = 1
	buffName = UnitDebuff("player", i)
	while buffName do
		currentAuras.debuffs[buffName] = true
		i = i + 1
		buffName = UnitDebuff("player", i)
	end
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
	end
end

local function GetSchoolColor(school)
	if not school then
		return nil, "ffffff"
	elseif school == SCHOOL_MASK_PHYSICAL then
		return STRING_SCHOOL_PHYSICAL, db.showSchoolColor and db.schoolColor.physical or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_FIRE then
		return STRING_SCHOOL_FIRE, db.showSchoolColor and db.schoolColor.fire or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_FROST then
		return STRING_SCHOOL_FROST, db.showSchoolColor and db.schoolColor.frost or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_NATURE then
		return STRING_SCHOOL_NATURE, db.showSchoolColor and db.schoolColor.nature or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_ARCANE then
		return STRING_SCHOOL_ARCANE, db.showSchoolColor and db.schoolColor.arcane or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_SHADOW then
		return STRING_SCHOOL_SHADOW, db.showSchoolColor and db.schoolColor.shadow or db.schoolColor.none or "ffff00"
	elseif school == SCHOOL_MASK_HOLY then
		return STRING_SCHOOL_HOLY, db.showSchoolColor and db.schoolColor.holy or db.schoolColor.none or "ffff00"
	else
		return nil, "ffffff"
	end
end

local function GetTrail(info)
	local text = ""
	if info.blocked then text = ("%s %d%s"):format(text, info.blocked, STRING_RESULT_BLOCKED) end
	if info.absorbed then text = ("%s %d%s"):format(text, info.absorbed, STRING_RESULT_ABSORBED) end
	if info.resisted then text = ("%s %d%s"):format(text, info.resisted, STRING_RESULT_RESISTED) end
	if info.crushing then text = ("%s %s"):format(text, STRING_RESULT_CRUSHING) end
	if info.glancing then text = ("%s %s"):format(text, STRING_RESULT_GLANCING) end
	if info.isSplit then text = ("%s %s"):format(text, STRING_RESULT_SPLIT) end
	if info.isDS then text = ("%s %s"):format(text, STRING_RESULT_DAMAGESHIELD) 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
	if info.amount and msg:find("%%a") then
		if info.powerType then
			local power, color = GetPowerTypeColor(info.powerType)
			if color then
				info.amount = ("|cff%s%s|r"):format(color, info.amount)
				coloredText = true
			end
			msg = msg:gsub("%%a", info.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, info.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", info.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.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

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 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 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

	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.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("PLAYER_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


-------------------------------------------------------------------------------
-- 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")
		db = bcfDB
		self.db = db
	end

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

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

function bcf:PLAYER_AURAS_CHANGED()
	updateCurrentAuras()
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:PLAYER_COMBO_POINTS(...)
	if db.showCombo then
		local comboPoints = GetComboPoints()
		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.enable or not info then return end

	if self[info.type] then
		self[info.type](self, info)
	end
	if db.useCustomEvent then
		self:ParserCustomEvent(info)
	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.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
		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
		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.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
		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 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 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' 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' 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
		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
					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)
	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 = values
		if type(values)=="string" then
			if not spellCostCache[values] then
				spellCostCache[values] = GetSpellRequireCost(values)
			end
			mana = spellCostCache[values]
		end
		if UnitMana("player") >= tonumber(mana) then
			return true
		end
	elseif subConditionType == "hasBuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not currentAuras.buffs[k] then
					return false
				end
			end
			return true
		else
			return currentAuras.buffs[values] and true or false
		end
	elseif subConditionType == "hasNoBuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if currentAuras.buffs[k] then
					return false
				end
			end
			return true
		else
			return not currentAuras.buffs[values]
		end
	elseif subConditionType == "hasDebuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if not currentAuras.debuffs[k] then
					return false
				end
			end
			return true
		else
			return currentAuras.debuffs[values] and true or false
		end
	elseif subConditionType == "hasNoDebuff" then
		if type(values)=='table' then
			for k, v in pairs(values) do
				if currentAuras.debuffs[k] then
					return false
				end
			end
			return true
		else
			return not currentAuras.debuffs[values]
		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 == "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
	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] = {}
				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
				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
			end
		end
	end
	for k,_ in pairs(customRegistedEvents) do
		self.customFrame:RegisterEvent(k)
	end
end


bcf:OnLoad()
