-- Locally bound frame cache
local frameCache = CT_RA_Cache;

-- Variables
CHAT_MSG_CTRAID = "CTRaid";
CT_RA_Squelch = 0;
CT_RA_Comm_MessageQueue = { };
CT_RA_Level = 0;
CT_RA_Stats = {
	{
		{ }
	}
};
CT_RA_PTargets = { };
CT_RA_BuffsToCure = { };
CT_RA_BuffsToRecast = { };
CT_RA_RaidParticipant = nil; -- Used to see what player participated in the raid on this account

CT_RA_Auras = { 
	["buffs"] = { },
	["debuffs"] = { }
};
CT_RA_LastSend = nil;
CT_RA_ClassPositions = {
	[CT_RA_WARRIOR] = 1,
	[CT_RA_DRUID] = 2,
	[CT_RA_MAGE] = 3,
	[CT_RA_WARLOCK] = 4,
	[CT_RA_ROGUE] = 5,
	[CT_RA_HUNTER] = 6,
	[CT_RA_PRIEST] = 7,
	[CT_RA_PALADIN] = 8,
	[CT_RA_SHAMAN] = 8
};
CT_RA_ClassIndices = { "WARRIOR", "DRUID", "MAGE", "WARLOCK", "ROGUE", "HUNTER", "PRIEST", "PALADIN", "SHAMAN" };

CT_RA_Emergency_RaidHealth = { };
CT_RA_Emergency_Units = { };

CT_RA_UnitIDFrameMap = { };
CT_RA_LastSent = { };
CT_RA_BuffTimeLeft = { };
CT_RA_ResFrame_Options = { };
CT_RA_MainTanks = { };
CT_RA_CurrPlayerName = "";

CT_RA_NumRaidMembers = 0;

ChatTypeGroup["CTRAID"] = {
	"CHAT_MSG_CTRAID"
};
ChatTypeInfo["CTRAID"] = { sticky = 0 };
tinsert(OtherMenuChatTypeGroups, "CTRAID");
CHAT_CTRAID_GET = "";
CT_RA_ChatInfo = {
	["Default"] = {
		["r"] = 1.0,
		["g"] = 0.5,
		["b"] = 0,
		["show"] = {
			"ChatFrame1"
		}
	}
};

CT_oldFCF_Tab_OnClick = FCF_Tab_OnClick;
function CT_newFCF_Tab_OnClick(button)
	CT_oldFCF_Tab_OnClick(button);
	if ( button == "RightButton" ) then
		local frameName = "ChatFrame" .. this:GetID();
		local frame = getglobal(frameName);
		local info = CT_RA_ChatInfo["Default"];
		if ( CT_RA_ChatInfo[UnitName("player")] ) then
			info = CT_RA_ChatInfo[UnitName("player")];
		end
		for k, v in pairs(info["show"]) do
			if ( v == frameName ) then
				local y = 1;
				while ( frame.messageTypeList[y] ) do
					y = y + 1;
				end
				frame.messageTypeList[y] = "CTRAID";
			end
		end
	end
end
FCF_Tab_OnClick = CT_newFCF_Tab_OnClick;

CT_oldFCF_SetChatTypeColor = FCF_SetChatTypeColor;
function CT_newFCF_SetChatTypeColor()
	CT_oldFCF_SetChatTypeColor();
	if ( UIDROPDOWNMENU_MENU_VALUE == "CTRAID" ) then
		local r,g,b = ColorPickerFrame:GetColorRGB();
		local chatInfo = CT_RA_ChatInfo[UnitName("player")];
		local chatTypeInfo = ChatTypeInfo["CTRAID"];
		if ( not chatInfo ) then
			CT_RA_ChatInfo[UnitName("player")] = CT_RA_ChatInfo["Default"];
		end
		chatInfo.r = r;
		chatInfo.g = g;
		chatInfo.b = b;
		chatTypeInfo.r = r;
		chatTypeInfo.g = g;
		chatTypeInfo.b = b;
	end
end
FCF_SetChatTypeColor = CT_newFCF_SetChatTypeColor;

CT_oldFCF_CancelFontColorSettings = FCF_CancelFontColorSettings;
function CT_newFCF_CancelFontColorSettings(prev)
	CT_oldFCF_CancelFontColorSettings(prev);
	if ( prev.r and UIDROPDOWNMENU_MENU_VALUE == "CTRAID" ) then
		local chatInfo = CT_RA_ChatInfo[UnitName("player")];
		local chatTypeInfo = ChatTypeInfo["CTRAID"];
		if ( not chatInfo ) then
			CT_RA_ChatInfo[UnitName("player")] = CT_RA_ChatInfo["Default"];
		end
		chatInfo.r = prev.r;
		chatInfo.g = prev.g;
		chatInfo.b = prev.b;
		chatTypeInfo.r = prev.r;
		chatTypeInfo.g = prev.g;
		chatTypeInfo.b = prev.b;
	end
end
FCF_CancelFontColorSettings = CT_newFCF_CancelFontColorSettings;
CT_oldFCFMessageTypeDropDown_OnClick = FCFMessageTypeDropDown_OnClick;
function CT_newFCFMessageTypeDropDown_OnClick()
	CT_oldFCFMessageTypeDropDown_OnClick();
	if ( not CT_RA_ChatInfo[UnitName("player")] ) then
		CT_RA_ChatInfo[UnitName("player")] = CT_RA_ChatInfo["Default"];
	end
	if ( this.value == "CTRAID" ) then
		local chatInfo = CT_RA_ChatInfo[UnitName("player")];
		local chatTypeInfo = ChatTypeInfo["CTRAID"];
		local currChatFrame = FCF_GetCurrentChatFrame():GetName();
		if ( UIDropDownMenuButton_GetChecked() ) then
			for k, v in pairs(chatInfo["show"]) do
				if ( v == currChatFrame ) then
					chatInfo["show"][k] = nil;
					break;
				end
			end
		else
			tinsert(chatInfo["show"], currChatFrame);
		end
	end
end
FCFMessageTypeDropDown_OnClick = CT_newFCFMessageTypeDropDown_OnClick;

function CT_RA_ShowHideWindows()
	if ( UnitAffectingCombat("player") ) then
		return;
	end
	
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( tempOptions["HiddenGroups"] ) then
		tempOptions["ShowGroups"] = tempOptions["HiddenGroups"];
		tempOptions["HiddenGroups"] = nil;

		local num = 0;
		for k, v in pairs(tempOptions["ShowGroups"]) do
			num = num + 1;
			getglobal("CT_RAOptionsGroupCB" .. k):SetChecked(1);
		end
		if ( num > 0 ) then
			CT_RACheckAllGroups:SetChecked(1);
		else
			CT_RACheckAllGroups:SetChecked(nil);
		end
	else
		tempOptions["HiddenGroups"] = tempOptions["ShowGroups"];
		tempOptions["ShowGroups"] = { };
		for i = 1, 8, 1 do
			getglobal("CT_RAOptionsGroupCB" .. i):SetChecked(nil);
		end
		CT_RACheckAllGroups:SetChecked(nil);
	end
	CT_RA_UpdateRaidGroup(0);
	CT_RA_UpdateRaidFrames();
end

function CT_RA_SetGroup()
	local tempOptions = CT_RAMenu_Options["temp"];
	tempOptions["ShowGroups"][this.id] = this:GetChecked();
	local num = 0;
	for k, v in pairs(tempOptions["ShowGroups"]) do
		num = num + 1;
	end
	if ( num > 0 ) then
		CT_RACheckAllGroups:SetChecked(1);
	else
		CT_RACheckAllGroups:SetChecked(nil);
	end
	CT_RA_UpdateRaidGroup(0);
	CT_RA_UpdateRaidFrames();
end

function CT_RA_CheckAllGroups()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not tempOptions["ShowGroups"] ) then tempOptions["ShowGroups"] = { }; end
	for i = 1, 8, 1 do
		tempOptions["ShowGroups"][i] = this:GetChecked();
		getglobal("CT_RAOptionsGroupCB" .. i):SetChecked(this:GetChecked());
	end
	CT_RA_UpdateRaidGroup(0);
	CT_RA_UpdateRaidFrames();
end

function CT_RA_ParseEvent(event)
	local nick, sMsg, msg;
	if ( event == "CHAT_MSG_ADDON" ) then
		nick, sMsg = arg4, arg2;
	else
		nick, sMsg = arg2, arg1;
	end
	local numRaidMembers = GetNumRaidMembers();
	local name, rank, subgroup, level, class, fileName, zone, online, isDead, raidid, frame;
	for i = 1, numRaidMembers, 1 do
		if ( UnitName("raid" .. i) == nick ) then
			raidid = i;
			name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i);
			frame = CT_RA_UnitIDFrameMap["raid"..i];
			break;
		end
	end

	local playerName = UnitName("player");
	local unitStats = CT_RA_Stats[nick];
	
	if ( name and not unitStats ) then
		CT_RA_Stats[nick] = {
			["Buffs"] = { },
			["Debuffs"] = { },
			["Position"] = { }
		};
		unitStats = CT_RA_Stats[nick];
	end
	
	if ( ( event == "CHAT_MSG_RAID" or event == "CHAT_MSG_RAID_LEADER" ) and type(sMsg) == "string" ) then
		if ( raidid ) then
			-- We have a valid unit
			msg = gsub(sMsg, "%%", "%%%%");
				
			if ( unitStats and raidid ) then
				if ( arg6 and not unitStats[arg6]  and ( arg6 == "AFK" or arg6 == "DND" ) ) then
					unitStats[arg6] = { 1, 0 };
					CT_RA_UpdateUnitDead(frame);
				elseif ( arg2 == name and ( not arg6 or arg6 == "" ) and ( unitStats["DND"] or unitStats["AFK"] ) ) then
					unitStats["DND"] = nil;
					unitStats["AFK"] = nil;
					CT_RA_UpdateUnitDead(frame);
				end
			end
			if ( rank and rank < 1 and CT_RA_Squelch and CT_RA_Squelch > 0 ) then
				if ( CT_RA_Level >= 1 and CT_RA_IsSendingWithVersion(1.468) ) then
					SendChatMessage("<공격대 도우미> 공격대에 정숙 모드가 작동했습니다. 정숙해주세요. " .. floor(CT_RA_Squelch) .. "초 남았습니다.", "WHISPER", nil, name);
				end
				return;
			end
			
			if ( rank and rank >= 1 and string.find(sMsg, "<공격대 도우미> (.+)%님의 요청에 의해 공격대를 해산합니다.") ) then
				LeaveParty();
				return;
			end
			if ( rank >= 1 ) then
				if ( name ~= playerName and sMsg == "<공격대 도우미> 정숙 모드가 해제 되었습니다." ) then
					if ( CT_RA_Squelch > 0 ) then
						CT_RA_Squelch = 0;
						CT_RA_Print("<공격대 도우미> 정숙 모드가 " .. name .. "님에 의해 해제되었습니다.", 1, 0.5, 0);
					end
				elseif ( name ~= playerName and sMsg == "<공격대 도우미> 정숙 모드입니다. 이야기 할 수 없습니다." ) then
					if ( CT_RA_Squelch == 0 ) then
						CT_RA_Squelch = 5*60;
						CT_RA_Print("<공격대 도우미> 정숙 모드가 " .. name .. "님에 의해 작동을 시작합니다.", 1, 0.5, 0);
					end
				end
				return;
			end
		end
	elseif ( event == "CHAT_MSG_WHISPER" and type(sMsg) == "string" ) then
		local tempOptions = CT_RAMenu_Options["temp"];
		if ( tempOptions["KeyWord"] and strlower(sMsg) == strlower(tempOptions["KeyWord"]) ) then
			local temp = arg2;
			if ( numRaidMembers == 40 or ( GetNumPartyMembers() == 4 and numRaidMembers == 0 ) ) then
				CT_RA_Print("<공격대 도우미> '|c00FFFFFF" .. temp .. "|r'님께서 초대를 요청했습니다, 공격대가 현재 모두 찬 상태입니다.", 1, 0.5, 0);
				SendChatMessage("<공격대 도우미> 공격대가 모두 찼습니다.", "WHISPER", nil, temp);
			else
				CT_RA_Print("<공격대 도우미> '|c00FFFFFF" .. temp .. "|r'님을 키워드 초대 기능을 통해 초대했습니다.", 1, 0.5, 0);
				InviteUnit(temp);
				CT_RA_UpdateFrame.lastInvite = 1;
				CT_RA_UpdateFrame.inviteName = temp;
			end
		else
			local _, _, secRem = string.find(sMsg, "<공격대 도우미> 공격대에 정숙 모드가 작동했습니다%. 정숙해주세요%. (%d+)초 남았습니다%.");
			if ( secRem and CT_RA_Squelch == 0 ) then
				if ( rank >= 1 ) then
					CT_RA_Squelch = tonumber(secRem);
					CT_RA_Print("<공격대 도우미> 정숙 모드가 " .. secRem .. "초 간 " .. name .. "님에 의해 작동중입니다.", 1, 0.5, 0);
				end
			end
		end
	elseif ( event == "CHAT_MSG_WHISPER_INFORM" ) then
		if ( arg1 == "<공격대 도우미> 당신은 이미 파티에 속해 있습니다." ) then
			CT_RA_Print("<공격대 도우미> '|c00FFFFFF" .. arg2 .. "|r'님께 그 또는 그녀가 이미 파티에 있음을 통보합니다.", 1, 0.5, 0);
		end
	elseif ( event == "CHAT_MSG_COMBAT_FRIENDLY_DEATH" ) then
		if ( not CT_RAMenu_Options["temp"]["HideTankNotifications"] ) then
			local _, _, name = string.find(sMsg, CT_RA_TANKHASDIEDREGEXP);
			if ( name ) then
				for k, v in pairs(CT_RA_MainTanks) do
					if ( v == name ) then
						CT_RA_WarningFrame:AddMessage("탱커 " .. name .. "님이 사망했습니다!", 1, 0, 0, 1, UIERRORS_HOLD_TIME);
						PlaySoundFile("Sound\\interface\\igQuestFailed.wav");
						break;
					end
				end
			end
		end
	elseif ( strsub(event, 1, 15) == "CHAT_MSG_SYSTEM" and type(sMsg) == "string" ) then
		local useless, useless, plr = string.find(sMsg, CT_RA_HASLEFTRAIDREGEXP);
		if ( CT_RA_RaidParticipant and plr and plr ~= CT_RA_RaidParticipant ) then
			CT_RA_CurrPositions[plr] = nil;
			CT_RA_Stats[plr] = nil;
			for k, v in pairs(CT_RA_MainTanks) do
				if ( v == plr ) then
					CT_RA_MainTanks[k] = nil;
					CT_RATarget.MainTanks[k] = nil;
					break;
				end
			end
		elseif ( string.find(sMsg, CT_RA_HASJOINEDRAIDREGEXP) ) then
			if ( CT_RA_Level >= 2 ) then
				local useless, useless, plr = string.find(sMsg, CT_RA_HASJOINEDRAIDREGEXP);
				if ( plr and CT_RATab_AutoPromotions[plr] ) then
					PromoteToAssistant(plr);
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. plr .. "|r님을 자동 승급했습니다.", 1, 0.5, 0);
				end
			end
		elseif ( string.find(sMsg, CT_RA_AFKMESSAGE) or sMsg == MARKED_AFK ) then
			local _, _, msg = string.find(sMsg, CT_RA_AFKMESSAGE);
			if ( msg and msg ~= DEFAULT_AFK_MESSAGE ) then
				if ( strlen(msg) > 20 ) then
					msg = strsub(msg, 1, 20) .. "...";
				end
				CT_RA_AddMessage("AFK " .. msg);
			else
				CT_RA_AddMessage("AFK");
			end
		elseif ( string.find(sMsg, CT_RA_DNDMESSAGE) ) then
			local _, _, msg = string.find(sMsg, CT_RA_DNDMESSAGE);
			if ( msg and msg ~= DEFAULT_DND_MESSAGE ) then
				if ( strlen(msg) > 20 ) then
					msg = strsub(msg, 1, 20) .. "...";
				end
				CT_RA_AddMessage("DND " .. msg);
			else
				CT_RA_AddMessage("DND");
			end
		elseif ( sMsg == CLEARED_AFK ) then
			CT_RA_AddMessage("UNAFK");
		elseif ( sMsg == CLEARED_DND ) then
			CT_RA_AddMessage("UNDND");
		end

	elseif ( event == "CHAT_MSG_ADDON" and arg1 == "CTRA" and arg3 == "RAID" ) then
		if ( raidid ) then
			-- Unit is in raid
			local eventtype = strsub(event, 10);
			local info = ChatTypeInfo[eventtype];
			event = "CHAT_MSG_CTRAID";
			if ( arg6 and not unitStats[arg6] and ( arg6 == "AFK" or arg6 == "DND" ) ) then
				unitStats[arg6] = { 1, 0 };
				CT_RA_UpdateUnitDead(frame);
			elseif ( ( not arg6 or arg6 == "" ) and ( unitStats["DND"] or unitStats["AFK"] ) ) then
				unitStats["DND"] = nil;
				unitStats["AFK"] = nil;
				CT_RA_UpdateUnitDead(frame);
			end
			if ( not sMsg ) then
				return;
			end
			local msg = string.gsub(sMsg, "%$", "s");
			msg = string.gsub(msg, "§", "S");
			if ( strsub(msg, strlen(msg)-10) == " ...딸꾹!") then
				msg = strsub(msg, 1, strlen(msg)-11);
			end
			local tempUpdate, message;
			if ( string.find(msg, "#") ) then
				local arr = CT_RA_Split(msg, "#");
				for k, v in pairs(arr) do
					tempUpdate, message = CT_RA_ParseMessage(name, v);
					if ( message ) then
						CT_RA_Print(message, 1, 0.5, 0);
					end
					if ( tempUpdate ) then
						for k, v in pairs(tempUpdate) do
							tinsert(update, v);
						end
					end
				end
			else
				tempUpdate, message = CT_RA_ParseMessage(name, msg);
				if ( message ) then
					CT_RA_Print(message, 1, 0.5, 0);
				end
				if ( tempUpdate ) then
					for k, v in pairs(tempUpdate) do
						tinsert(update, v);
					end
				end
			end
			if ( type(update) == "table" ) then
				for k, v in pairs(update) do
					if ( type(v) == "number" ) then
						CT_RA_UpdateUnitStatus(CT_RA_UnitIDFrameMap["raid"..v]);
					else
						for i = 1, GetNumRaidMembers(), 1 do
							local uName = UnitName("raid" .. i);
							if ( uName and uName == v ) then
								CT_RA_UpdateUnitStatus(CT_RA_UnitIDFrameMap["raid"..i]);
								break;
							end
						end
					end
				end
			end
		end
	elseif ( event == "CHAT_MSG_PARTY" ) then
		if ( raidid ) then
			if ( arg6 and not unitStats[arg6] and ( arg6 == "AFK" or arg6 == "DND" ) ) then
				unitStats[arg6] = { 1, 0 };
				CT_RA_UpdateUnitDead(frame);
			elseif ( ( not arg6 or arg6 == "" ) and ( unitStats["DND"] or unitStats["AFK"] ) ) then
				unitStats["DND"] = nil;
				unitStats["AFK"] = nil;
				CT_RA_UpdateUnitDead(frame);
			end
		end
	end
end
	
CT_RA_oldChatFrame_OnEvent = ChatFrame_OnEvent;
function CT_RA_newChatFrame_OnEvent(event)
	if ( strsub(event, 1, 13) == "CHAT_MSG_RAID" or strsub(event, 1, 20) == "CHAT_MSG_RAID_LEADER" ) then
		local tempOptions = CT_RAMenu_Options["temp"];
		local name, rank;
		for i = 1, GetNumRaidMembers(), 1 do
			name, rank = GetRaidRosterInfo(i);
			if ( name == arg2 ) then
				if ( rank and rank < 1 and CT_RA_Squelch > 0 ) then
					return;
				end
				break;
			end
		end
		if ( not rank ) then
			rank = 0;
		end
		if ( rank >= 1 and ( arg1 == "<공격대 도우미> 정숙 모드입니다. 이야기 할 수 없습니다." or arg1 == "<공격대 도우미> 정숙 모드가 해제 되었습니다." ) ) then
			return;
		end
		local useless, useless, chan = string.find(gsub(arg1, "%%", "%%%%"), "^<공격대 도우미> 공격대 도우미가 전송하는 자동 메세지입니다. 공격대 사용 채널이 다음의 이름으로 변경되었습니다: (.+)$");
		if ( chan ) then
			return;
		end
		if ( rank == 2 and ( not tempOptions["leaderColor"] or tempOptions["leaderColor"].enabled ) ) then
			CT_RA_oldAddMessage = this.AddMessage;
			this.AddMessage = CT_RA_newAddMessage;
			CT_RA_oldChatFrame_OnEvent(event);
			this.AddMessage = CT_RA_oldAddMessage;
			return;
		end
	elseif ( event == "CHAT_MSG_WHISPER" ) then
		local tempOptions = CT_RAMenu_Options["temp"];
		if ( ( tempOptions["KeyWord"] and strlower(arg1) == strlower(tempOptions["KeyWord"]) ) or arg1 == "<공격대 도우미> 공격대에 정숙 모드가 작동했습니다. 정숙해주세요." ) then
			return;
		end
	end
	CT_RA_oldChatFrame_OnEvent(event);
end
ChatFrame_OnEvent = CT_RA_newChatFrame_OnEvent;

function CT_RA_newAddMessage(obj, msg, r, g, b)
	local tempOptions = CT_RAMenu_Options["temp"];
	local newR, newG, newB = 1, 1, 0;
	if ( tempOptions["leaderColor"] ) then
		newR, newG, newB = tempOptions["leaderColor"].r, tempOptions["leaderColor"].g, tempOptions["leaderColor"].b;
	end
	return CT_RA_oldAddMessage(obj, string.gsub(msg, "(|Hplayer:.-|h%[)(.+)(%])", "%1|c00" .. CT_RA_RGBToHex(newR, newG, newB) .. "%2|r%3"), r, g, b);
end

function CT_RA_ParseMessage(nick, msg)
	local tempOptions = CT_RAMenu_Options["temp"];
	local useless, val1, val2, val3, val4, frame, raidid, rank, update;
	local numRaidMembers = GetNumRaidMembers();
	local playerName = UnitName("player");
	
	for i = 1, numRaidMembers, 1 do
		if ( UnitName("raid" .. i) == nick ) then
			raidid = i;
			useless, rank = GetRaidRosterInfo(i);
			frame = CT_RA_UnitIDFrameMap["raid"..i];
			break;
		end
	end

	if ( not raidid ) then
		return;
	end
	
	local unitStats = CT_RA_Stats[nick];
	if ( not unitStats ) then
		if ( not update ) then
			update = { };
		end
		CT_RA_Stats[nick] = {
			["Buffs"] = { },
			["Debuffs"] = { },
			["Position"] = { }
		};
		unitStats = CT_RA_Stats[nick];
		tinsert(update, raidid);
	end
	unitStats["Reporting"] = 1;
	
	-- Check buff renewal
	useless, useless, val1, val2, val3 = string.find(msg, "^RN ([^%s]+) ([^%s]+) ([^%s]+)$"); -- timeleft(1), id(2), num(3)
	if ( tonumber(val1) and tonumber(val2) and tonumber(val3) ) then
		-- Buffs
		local buff;
		for k, v in pairs(tempOptions["BuffArray"]) do
			if ( tonumber(val2) == v["index"] ) then
				buff = v;
				break;
			end
		end
		if ( not buff and tonumber(val2) == -1 ) then
			buff = { ["show"] = 1, ["name"] = CT_RA_FEIGNDEATH[CT_RA_GetLocale()] };
		elseif ( not buff ) then
			return update;
		end
		local name = buff["name"];
		if ( type(name) == "table" ) then
			if ( tonumber(val3) ) then
				name = name[tonumber(val3)];
			else
				return update;
			end
		end
		local text = CT_RA_BuffTextures[name];
		if ( not name or not text ) then
			return update;
		end
		unitStats["Buffs"][name] = { text[1], tonumber(val1) };
		return update;
	end

	-- Check status requests
	if ( msg == "SR" ) then
		if ( unitStats ) then
			unitStats["Buffs"] = { ["n"] = 0 };
			unitStats["Debuffs"] = { ["n"] = 0 };
		end
		CT_RA_ScanPartyAuras("raid" .. raidid);
		CT_RA_UpdateFrame.scheduleUpdate = 8;
		CT_RA_UpdateFrame.scheduleMTUpdate = 8;
		return update;
	end

	if ( strsub(msg, 1, 2) == "S " ) then
		if ( frame ) then
			for str in string.gmatch(msg, " B [^%s]+ [^%s]+ [^#]+ #") do
				useless, useless, val1, val3, val2 = string.find(str, "B ([^%s]+) ([^%s]+) (.+) #");
				if ( val1 and val2 and val3 ) then
					unitStats["Buffs"][val2] = { val1, tonumber(val3) };
					CT_RA_UpdateUnitBuffs(unitStats["Buffs"], frame, nick);
				end
			end
		end
		return update;
	end

	if ( strsub(msg, 1, 3) == "MS " ) then
		if ( rank >= 1 ) then
			if ( tempOptions["PlayRSSound"] ) then
				PlaySoundFile("Sound\\Doodad\\BellTollNightElf.wav");
			end
			CT_RAMessageFrame:AddMessage(nick .. ": " .. strsub(msg, 3), tempOptions["DefaultAlertColor"].r, tempOptions["DefaultAlertColor"].g, tempOptions["DefaultAlertColor"].b, 1.0, UIERRORS_HOLD_TIME);
		end
		return update;
	end

	useless, useless, val1 = string.find(msg, "^V ([%d%.]+)$");
	if ( tonumber(val1) ) then
		unitStats["Version"] = tonumber(val1);
		return update;
	end

	
	if ( strsub(msg, 1, 4) == "SET " ) then
		local useless, useless, num, name = string.find(msg, "^SET (%d+) (.+)$");
		if ( name ) then
			if ( rank >= 1 ) then
				num = tonumber(num);
				for k, v in pairs(CT_RA_MainTanks) do
					if ( v == name ) then
						CT_RA_MainTanks[k] = nil;
						CT_RATarget.MainTanks[k] = nil;
					end
				end
				local mtID = 0;
				for i = 1, numRaidMembers, 1 do
					if ( UnitName("raid" .. i) == name ) then
						mtID = i;
						break;
					end
				end
				CT_RA_MainTanks[num] = name;
				CT_RATarget.MainTanks[num] = { mtID, name };
				CT_RATarget_UpdateInfoBox();
				CT_RATarget_UpdateStats();
				CT_RAOptions_UpdateMTs();
				CT_RA_UpdateRaidFrameData();
				CT_RA_UpdateMTs(true);
			end
		end
		return update;
	end

	if ( strsub(msg, 1, 2) == "R " ) then
		local useless, useless, name = string.find(msg, "^R (.+)$");
		if ( name ) then
			for k, v in pairs(CT_RA_MainTanks) do
				if ( v == name ) then
					for i = 1, GetNumRaidMembers(), 1 do
						local user, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i);
						if ( rank >= 1 and user == nick ) then
							CT_RA_MainTanks[k] = nil;
							CT_RATarget.MainTanks[k] = nil;
							CT_RA_UpdateRaidFrameData();
							CT_RA_UpdateMTs(true);
							CT_RAOptions_UpdateMTs();
							return update;
						end
					end
				end
			end
		end
		return update;
	end

	if ( msg == "DB" ) then
		if ( rank >= 1 ) then
			CT_RA_Print("<공격대 도우미> '|c00FFFFFF" .. nick .. "|r'님의 요청에 의해 공격대를 해산합니다.", 1, 0.5, 0);
			LeaveParty();
		end
		return update;
	end

	if ( msg == "RESSED" ) then
		unitStats["Ressed"] = 1;
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	if ( msg == "NORESSED" ) then
		unitStats["Ressed"] = nil;
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	if ( msg == "CANRES" ) then
		unitStats["Ressed"] = 2;
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	if ( strsub(msg, 1, 3) == "RES" ) then
		if ( msg == "RESNO" ) then
			CT_RA_Ressers[nick] = nil;
		else
			local _, _, player = string.find(msg, "^RES (.+)$");
			if ( player ) then
				CT_RA_Ressers[nick] = player;
			end
		end
		CT_RA_UpdateResFrame();
		return update;
	end
	-- Check ready

	if ( msg == "CHECKREADY" ) then
		if ( rank >= 1 ) then
			CT_RA_CheckReady_Person = nick;
			if ( nick ~= playerName ) then
				PlaySoundFile("Sound\\interface\\levelup2.wav");
				CT_RA_ReadyFrame:Show();
			end
		end
		return update;
	elseif ( ( msg == "READY" or msg == "NOTREADY" ) and CT_RA_CheckReady_Person == playerName ) then
		if ( msg == "READY" ) then
			unitStats["notready"] = nil;
		else
			unitStats["notready"] = 2;
		end
		local all_ready = true;
		local nobody_ready = true;
		for k, v in pairs(CT_RA_Stats) do
			if ( v["notready"] ) then
				all_ready = false;
				if ( v["notready"] == 1 ) then
					nobody_ready = false;
				end
			end
		end
		if ( all_ready ) then
			CT_RA_Print("<공격대 도우미> 모든 공격대원이 접속해 있습니다.", 1, 1, 0);
		elseif ( not all_ready and nobody_ready ) then
			CT_RA_UpdateFrame.readyTimer = 0.1;
		end
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	-- Check Rly
	if ( msg == "CHECKRLY" ) then
		if ( rank >= 1 ) then
			CT_RA_CheckRly_Person = nick;
			if ( nick ~= UnitName("player") ) then
				PlaySoundFile("Sound\\interface\\levelup2.wav");
				CT_RA_RlyFrame:Show();
			end
		end
		return update;
	elseif ( ( msg == "YARLY" or msg == "NORLY" ) and CT_RA_CheckRly_Person == playerName ) then
		if ( msg == "YARLY" ) then
			unitStats["rly"] = nil;
		else
			unitStats["rly"] = 1;
		end
		local all_ready = true;
		local nobody_ready = true;
		for k, v in pairs(CT_RA_Stats) do
			if ( v["rly"] ) then
				all_ready = false;
				if ( v["rly"] == 1 ) then
					nobody_ready = false;
				end
			end
		end
		if ( all_ready ) then
			CT_RA_Print("<공격대 도우미> 준비 완료.", 1, 1, 0);
		elseif ( not all_ready and nobody_ready ) then
			CT_RA_UpdateFrame.rlyTimer = 0.1;
		end
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	-- Check AFK

	if ( msg == "AFK" ) then
		unitStats["AFK"] = { 1, 0 };
		CT_RA_UpdateUnitDead(frame);
		return update;
	elseif ( msg == "UNAFK" ) then
		unitStats["AFK"] = nil;
		CT_RA_UpdateUnitDead(frame);
		return update;
	elseif ( msg == "DND" ) then
		unitStats["DND"] = { 1, 0 };
		CT_RA_UpdateUnitDead(frame);
		return update;
	elseif ( msg == "UNDND" ) then
		unitStats["DND"] = nil;
		CT_RA_UpdateUnitDead(frame);
		return update;
	elseif ( strsub(msg, 1, 3) == "AFK" ) then
		-- With reason
		unitStats["AFK"] = { strsub(msg, 5), 0 };
		CT_RA_UpdateUnitDead(frame);
		return update;
	elseif ( strsub(msg, 1, 3) == "DND" ) then
		-- With reason
		unitStats["DND"] = { strsub(msg, 5), 0 };
		CT_RA_UpdateUnitDead(frame);
		return update;
	end

	-- Check duration
	if ( msg == "DURC" ) then
		if ( rank == 0 ) then
			return;
		end
		local currDur, maxDur, brokenItems = CT_RADurability_GetDurability();
		CT_RA_AddMessage("DUR " .. currDur .. " " .. maxDur .. " " .. brokenItems .. " " .. nick);
		return update;
	elseif ( string.find(msg, "^DUR ") ) then
		local _, _, currDur, maxDur, brokenItems, callPerson = string.find(msg, "^DUR (%d+) (%d+) (%d+) ([^%s]+)$");
		if ( currDur and maxDur and brokenItems and callPerson == playerName ) then
			currDur, maxDur = tonumber(currDur), tonumber(maxDur);
			local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(raidid);
			CT_RADurability_Add(nick, "|c00FFFFFF" .. floor((currDur/maxDur)*100+0.5) .. "%|r (|c00FFFFFF" .. brokenItems .. "개 부서짐|r)", fileName, floor((currDur/maxDur)*100+0.5));
		end
		return update;
	end
	
	-- Check resists (Thanks Sudo!)
	if ( msg == "RSTC" ) then
		if ( rank == 0 ) then
			return update;
		end
		if ( tempOptions["DisableQuery"] ) then
			CT_RA_AddMessage("RST -1 " .. nick);
		else
			local resistStr = "";
			for i = 2, 6, 1 do
				local _, res, _, _ = UnitResistance("player", i);
				resistStr = resistStr .. " " .. res;
			end
			CT_RA_AddMessage("RST" .. resistStr ..  " " .. nick);
		end
		return update;
	elseif ( string.find(msg, "^RST ") ) then
		local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(raidid);
		local _, _, plrName = string.find(msg, "^RST %-1 ([^%s]+)$");
		if ( plrName and plrName == playerName ) then
			CT_RADurability_Add(nick, "|c00FFFFFF수집 불가|r", fileName, -1, -1, -1, -1, -1);
		else
			local _, _, FR, NR, FRR, SR, AR, callPerson = string.find(msg, "^RST (%d+) (%d+) (%d+) (%d+) (%d+) ([^%s]+)$");
			if ( FR and callPerson == playerName ) then
				CT_RADurability_Add(nick, "", fileName, tonumber(FR), tonumber(NR), tonumber(FRR), tonumber(SR), tonumber(AR) );
			end
		end
		return update;
	end

	-- Check reagents
	if ( msg == "REAC" ) then
		if ( rank == 0 ) then
			return update;
		end
		local numItems = CT_RAReagents_GetReagents();
		if ( numItems and numItems >= 0 ) then
			CT_RA_AddMessage("REA " .. numItems .. " " .. nick);
		end
		return update;
	elseif ( string.find(msg, "^REA ") ) then
		local _, _, numItems, callPerson = string.find(msg, "^REA ([^%s]+) ([^%s]+)$");
		if ( numItems and callPerson and callPerson == playerName ) then
			local classes = {
				[CT_RA_PRIEST] = CT_REG_PRIEST,
				[CT_RA_MAGE] = CT_REG_MAGE,
				[CT_RA_DRUID] = CT_REG_DRUID,
				[CT_RA_WARLOCK] = CT_REG_WARLOCK,
				[CT_RA_PALADIN] = CT_REG_PALADIN,
				[CT_RA_SHAMAN] = CT_REG_SHAMAN
			};
			local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(raidid);
			--if ( numItems ~= "1" ) then
				CT_RADurability_Add(nick, classes[UnitClass("raid"..raidid)] .. "|c00FFFFFF" .. numItems .. "|r " .. "개", fileName, numItems);
			--else
			--	CT_RADurability_Add(nick, "|c00FFFFFF" .. numItems .. "|r " .. classes[UnitClass("raid"..raidid)], fileName, numItems );
			--end
		end
		return update;
	end

	-- Check items
	if ( string.find(msg, "^ITMC ") ) then
		local _, _, itemName = string.find(msg, "^ITMC (.+)$");
		if ( itemName ) then
			if ( rank == 0 ) then
				return;
			end
			if ( tempOptions["DisableQuery"] ) then
				CT_RA_AddMessage("ITM " .. -1 .. " " .. itemName .. " " .. nick);
			else
				local numItems = CT_RAItem_GetItems(itemName);
				if ( numItems and numItems > 0 ) then
					CT_RA_AddMessage("ITM " .. numItems .. " " .. itemName .. " " .. nick);
				end
			end
		end
		return update;
	elseif ( string.find(msg, "^ITM ") ) then
		local _, _, numItems, itemName, callPerson = string.find(msg, "^ITM ([-%d]+) (.+) ([^%s]+)$");
		if ( numItems and itemName and callPerson and callPerson == UnitName("player") ) then
			local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(raidid);
			if ( numItems == "-1" ) then
				CT_RADurability_Add(nick, "|c00FFFFFF체크 안함|r", fileName, "0", class);
			elseif ( numItems ~= "1" ) then
				CT_RADurability_Add(nick, itemName .. "|c00FFFFFF" .. numItems .. "|r 개" , fileName, numItems);
			--else
			--	CT_RADurability_Add(nick, "|c00FFFFFF" .. numItems .. "|r " .. itemName, fileName, numItems);
			end
		end
		return update;
	end
	
	-- Check cooldowns
	if ( string.find(msg, "^CD %d+ %d+$") ) then
		local _, _, num, cooldown = string.find(msg, "^CD (%d+) (%d+)$");
		if ( num == "1" ) then
			unitStats["Rebirth"] = tonumber(cooldown)*60;
		elseif ( num == "2" ) then
			unitStats["Reincarnation"] = tonumber(cooldown)*60;
		elseif ( num == "3" ) then
			unitStats["Soulstone"] = tonumber(cooldown)*60;
		end
		return update;
	end

	-- Assist requests
	if ( string.find(msg, "^ASSISTME (.+)$") ) then
		if ( rank >= 1 ) then
			local _, _, name = string.find(msg, "^ASSISTME (.+)$");
			if ( name and name == playerName ) then
				CT_RATarget.assistPerson = { nick, 20 };
				ShowUIPanel(CT_RA_AssistFrame);
			end
		end
		return update;
	elseif ( string.find(msg, "^STOPASSIST (.+)$") ) then
		if ( rank >= 1 ) then
			local _, _, name = string.find(msg, "^STOPASSIST (.+)$");
			if ( name and name == playerName ) then
				HideUIPanel(CT_RA_AssistFrame);
			end
		end
		return update;
	end
	
	-- Vote
	local _, _, question = string.find(msg, "^VOTE (.+)$");
	if ( question ) then
		if ( rank >= 1 ) then
			CT_RA_VotePerson = { nick, 0, 0, question };
			if ( nick ~= playerName ) then
				PlaySoundFile("Sound\\interface\\levelup2.wav");
				CT_RA_VoteFrame.question = question;
				CT_RA_VoteFrame:Show();
			end
		end
		return update;
	elseif ( ( msg == "VOTEYES" or msg == "VOTENO" ) and CT_RA_VotePerson and CT_RA_VotePerson[1] == playerName ) then
		if ( msg == "VOTEYES" ) then
			CT_RA_VotePerson[2] = CT_RA_VotePerson[2] + 1;
		elseif ( msg == "VOTENO" ) then
			CT_RA_VotePerson[3] = CT_RA_VotePerson[3] + 1;
		end
		return update;
	end
	
	return update;
end

function CT_RA_UpdateBindings()
	local bindKey;
	for i = 1, 5, 1 do
		bindKey = GetBindingKey("CT_ASSISTMT"..i);
		if ( bindKey ) then
			SetOverrideBindingClick(CT_RAFrame, false, bindKey, "CT_RAMTGroupUnitButton"..i);
		end
		bindKey = GetBindingKey("CT_TARGETMT"..i);
		if ( bindKey ) then
			SetOverrideBindingClick(CT_RAFrame, false, bindKey, "CT_RAMTTGroupUnitButton"..i);
		end
		bindKey = GetBindingKey("CT_ASSISTPT"..i);
		if ( bindKey ) then
			SetOverrideBindingClick(CT_RAFrame, false, bindKey, "CT_RAPTTGroupUnitButton"..i);
		end
		bindKey = GetBindingKey("CT_TARGETPT"..i);
		if ( bindKey ) then
			SetOverrideBindingClick(CT_RAFrame, false, bindKey, "CT_RAPTGroupUnitButton"..i);
		end
	end
end

-- Send messages
function CT_RA_AddMessage(msg)
	tinsert(CT_RA_Comm_MessageQueue, msg);
end

function CT_RA_SendMessage(msg)
	if ( GetNumRaidMembers() == 0 ) then return; end -- Mod should be disabled if not in raid
	SendAddonMessage("CTRA", msg, "RAID");
end

function CT_RA_OnEvent(event)
	if ( event == "PLAYER_LEAVING_WORLD" ) then
		CT_RAFrame.disableEvents = true;
		return;
	elseif ( CT_RAFrame.disableEvents and event ~= "PLAYER_ENTERING_WORLD" ) then
		return;
	elseif ( event == "PLAYER_ENTERING_WORLD" or event == "RAID_ROSTER_UPDATE" ) then
		CT_RAFrame.disableEvents = nil;
		local numRaidMembers = GetNumRaidMembers();
		local playerName = UnitName("player");
		local tempOptions = CT_RAMenu_Options["temp"];
		if ( event == "RAID_ROSTER_UPDATE" ) then
			if ( numRaidMembers == 0 ) then
				CT_RA_MainTanks = { };
				CT_RA_PTargets = { };
				CT_RATarget.MainTanks = { };
				CT_RA_Stats = { };
				CT_RA_ButtonIndexes = { };
				CT_RA_Emergency_UpdateHealth();
				CT_RA_UpdateMTs();
				CT_RA_UpdatePTs();
				CT_RAMetersFrame:Hide();
				CT_RA_UpdateRaidFrameOptions();
			elseif ( CT_RA_NumRaidMembers == 0 and numRaidMembers > 0 ) then
				CT_RA_UpdateFrame.SS = 10;
				if ( CT_RA_UpdateFrame.time ) then
					CT_RA_UpdateFrame.time = nil;
				end
				if ( not CT_RA_HasJoinedRaid ) then
					CT_RA_Print("<공격대 도우미> 공격대에 들어오셨습니다. CT_공격대 도우미를 사용해 주셔서 감사합니다!", 1, 0.5, 0);
				end
				CT_RA_PartyMembers = { };
				CT_RA_HasJoinedRaid = 1;
				if ( CT_RA_Squelch > 0 ) then
					CT_RA_Print("<공격대 도우미> 정숙 모드가 자동으로 해제되었습니다. (공격대 참가).", 1, 0.5, 0);
					CT_RA_Squelch = 0;
				end
			end
			CT_RA_CheckGroups();
		else
			local emptyFunc = function() end
			for i = 1, 4, 1 do
				getglobal("PartyMemberFrame"..i):SetScript("OnShow", emptyFunc);
			end
			CT_RA_UpdateRaidFrameOptions();
		end

		if ( numRaidMembers > 0 ) then
			if ( tempOptions["StatusMeters"] and tempOptions["StatusMeters"]["Show"] ) then
				CT_RAMetersFrame:Show();
			else
				CT_RAMetersFrame:Hide();
			end
			if ( tempOptions["ShowMonitor"] ) then
				CT_RA_ResFrame:Show();
			else
				CT_RA_ResFrame:Hide();
			end
		else
			CT_RA_ResFrame:Hide();
			CT_RAMetersFrame:Hide();
		end

		CT_RAOptions_Update();
		if ( CT_RA_NumRaidMembers ~= numRaidMembers ) then
			for i = 1, numRaidMembers, 1 do
				local uId = "raid" .. i;
				local uName = UnitName(uId);
				if ( uName and CT_RA_Stats[uName] ) then
					CT_RA_Stats[uName]["Debuffs"].n = 0;
				end
				CT_RA_ScanPartyAuras(uId);
			end
			CT_RA_UpdateRaidGroup(0);
			if ( CT_RA_NumRaidMembers == 0 and CT_RA_Level >= 2 ) then
				local lootid = ( CT_RATab_DefaultLootMethod or -1 );
				if ( lootid == 1 ) then
					SetLootMethod("freeforall");
				elseif ( lootid == 2 ) then
					SetLootMethod("roundrobin");
				elseif ( lootid == 3 ) then
					SetLootMethod("master", playerName);
				elseif ( lootid == 4 ) then
					SetLootMethod("group");
				elseif ( lootid == 5 ) then
					SetLootMethod("needbeforegreed");
				end
				for i = 1, numRaidMembers, 1 do
					local name, rank = GetRaidRosterInfo(i);
					if ( name ~= playerName and rank < 1 and CT_RATab_AutoPromotions[name] ) then
						PromoteToAssistant(name);
						CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. name .. "|r님을 자동 승급했습니다.", 1, 0.5, 0);
					end
				end
			end
		else
			CT_RA_UpdateRaidGroup(3);
		end
		CT_RA_NumRaidMembers = numRaidMembers;
		CT_RA_UpdateRaidFrames();
		
		-- Check if someone left
		local update;
		for key, value in pairs(CT_RA_MainTanks) do
			if ( not CT_RA_Stats[value] ) then
				CT_RA_MainTanks[key] = nil;
				update = true;
			end
		end
		for key, value in pairs(CT_RA_PTargets) do
			if ( not CT_RA_Stats[value] ) then
				CT_RA_PTargets[key] = nil;
				update = true;
			end
		end
		if ( update ) then
			CT_RA_UpdateRaidFrameData();
		end
		
		if ( event == "PLAYER_ENTERING_WORLD" ) then
			if ( CT_RA_RaidParticipant ) then
				if ( CT_RA_RaidParticipant ~= playerName ) then
					CT_RA_Stats = { { } };
					CT_RA_MainTanks = { };
					CT_RA_PTargets = { };
					CT_RATarget.MainTanks = { };
					CT_RA_ButtonIndexes = { };
				end
			end
			CT_RA_RaidParticipant = playerName;
			-- Add chat frame stuff
			local info = CT_RA_ChatInfo[playerName];
			local chatTypeInfo = ChatTypeInfo["CTRAID"];
			if ( not info ) then
				info = CT_RA_ChatInfo["Default"];
			end
			chatTypeInfo.r = info.r;
			chatTypeInfo.g = info.g;
			chatTypeInfo.b = info.b;
		end
	elseif ( event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" ) then
		local _, _, id = string.find(arg1, "^raid(%d+)$");
		if ( id ) then
			local frame = CT_RA_UnitIDFrameMap["raid"..id];
			local name, hCurr, hMax = UnitName(arg1), UnitHealth(arg1), UnitHealthMax(arg1);
			local hpp = ( hCurr or 1 ) / ( hMax or 1 );
			local stats = CT_RA_Stats[name];
			if ( name and frame ) then
				if ( not stats ) then
					CT_RA_Stats[name] = {
						["Buffs"] = { },
						["Debuffs"] = { },
						["Position"] = { }
					};
					stats = CT_RA_Stats[name];
				end
				if ( UnitIsDead(arg1) or UnitIsGhost(arg1) ) then
					CT_RA_ScanPartyAuras(arg1);
					if ( not stats["Dead"] ) then
						stats["Dead"] = 1;
					end
					CT_RA_UpdateUnitDead(frame);
				elseif ( stats["Dead"] ) then
					if ( hCurr > 0 and not UnitIsGhost(arg1) ) then
						stats["Dead"] = nil;
					end
					CT_RA_UpdateUnitDead(frame);
				else
					stats["Dead"] = nil;
					if ( not frame.hpp or frame.hpp ~= floor(hpp*100) ) then
						CT_RA_UpdateUnitHealth(frame);
					end
				end
				if ( CT_RA_Emergency_Units[name] or ( not CT_RA_EmergencyFrame.maxPercent or hpp < CT_RA_EmergencyFrame.maxPercent ) ) then
					CT_RA_Emergency_UpdateHealth();
				end
			end
		elseif ( ( GetNumRaidMembers() == 0 and ( arg1 == "player" or string.find(arg1, "^party%d+$") ) ) ) then
			if ( CT_RA_Emergency_Units[UnitName(arg1)] or ( not CT_RA_EmergencyFrame.maxPercent or ( hpp and hpp < CT_RA_EmergencyFrame.maxPercent ) ) ) then
				CT_RA_Emergency_UpdateHealth();
			end
		end
		return;
	elseif ( event == "UNIT_AURA" and GetNumRaidMembers() > 0 ) then
		if ( string.find(arg1, "^raid%d+$") ) then
			CT_RA_ScanPartyAuras(arg1);
		end
	elseif ( event == "UNIT_MANA" or event == "UNIT_MAXMANA" or event == "UNIT_RAGE" or event == "UNIT_MAXRAGE" or event == "UNIT_ENERGY" or event == "UNIT_MAXENERGY" ) then
		local _, _, id = string.find(arg1, "^raid(%d+)$");
		if ( id ) then
			CT_RA_UpdateUnitMana(CT_RA_UnitIDFrameMap["raid"..id]);
		end
		return;
	elseif ( event == "UNIT_SPELLCAST_SUCCEEDED" ) then
		if ( arg2 == CT_RA_REBIRTH ) then
			CT_RA_AddMessage("CD 1 30");
		elseif ( arg2 == CT_RA_SOULSTONERESURRECTION ) then
			CT_RA_AddMessage("CD 3 30");
		end
	elseif ( event == "PLAYER_TARGET_CHANGED" ) then
		CT_RA_UpdateResFrame();
	elseif ( event == "UPDATE_BINDINGS" ) then
		CT_RA_UpdateBindings();
	end
end

CT_RA_oldUseSoulstone = UseSoulstone;
function CT_RA_newUseSoulstone()
	local text = HasSoulstone();
	if ( text and text == "윤회" ) then
		local cooldown;
		for i = 1, GetNumTalentTabs(), 1 do
			for y = 1, GetNumTalents(i), 1 do
				local name, _, _, _, currRank = GetTalentInfo(i, y);
				if ( name == "윤회 연마" ) then
					cooldown = 60 - (currRank*10);
					break;
				end
			end
			if ( cooldown ) then
				break;
			end
		end
		if ( not cooldown ) then
			cooldown = 60;
		end
		CT_RA_AddMessage("CD 2 " .. cooldown);
	end
	CT_RA_oldUseSoulstone();
end
UseSoulstone = CT_RA_newUseSoulstone;

-----------------------------------------------------
--                  Update Functions               --
-----------------------------------------------------

-- Update health
function CT_RA_UpdateUnitHealth(frame)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local id = "raid" .. frame.id;
	local maxHealth = UnitHealthMax(id);
	local percent = floor(UnitHealth(id) / maxHealth * 100);
	local name = UnitName(id);
	frame.hpp = percent;
	local updateDead = frame.status;
	if ( percent and percent > 0 ) then
		-- Commonly used values
		local defaultAlpha = tempOptions.DefaultAlpha;
		if ( defaultAlpha and defaultAlpha < 1 ) then
			frame:SetAlpha(math.max(math.min(defaultAlpha+(1-(percent/100))*(1-defaultAlpha), 1), defaultAlpha));
		else
			frame:SetAlpha(1);
		end
		local showHP = tempOptions["ShowHP"];
		local memberHeight = tempOptions["MemberHeight"];
		local framePercent = frame.Percent;
		local frameHPBar = frame.HPBar;
		local stats = CT_RA_Stats[name];
		
		if ( stats and stats["Ressed"] ) then
			stats["Ressed"] = nil;
			updateDead = 1;
		end
		if ( percent > 100 ) then
			percent = 100;
		end
		frameHPBar:SetValue(percent);
		if ( showHP and showHP == 1 and maxHealth and memberHeight == 40 ) then
			framePercent:SetText(floor(percent/100*maxHealth) .. "/" .. maxHealth);
		elseif ( showHP and showHP == 2 and memberHeight == 40 ) then
			framePercent:SetText(percent .. "%");
		elseif ( showHP and showHP == 3 and memberHeight == 40 ) then
			if ( maxHealth ) then
				local diff = floor(percent/100*maxHealth)-maxHealth;
				if ( diff == 0 ) then diff = ""; end
				framePercent:SetText(diff);
			else
				framePercent:SetText(percent-100 .. "%");
			end
		else
			framePercent:Hide();
		end
		local hppercent = percent/100;
		local r, g;
		if ( hppercent > 0.5 and hppercent <= 1) then
			g = 1;
			r = (1.0 - hppercent) * 2;
		elseif ( hppercent >= 0 and hppercent <= 0.5 ) then
			r = 1.0;
			g = hppercent * 2;
		else
			r = 0;
			g = 1;
		end
		frameHPBar:SetStatusBarColor(r, g, 0);
		frame.HPBG:SetVertexColor(r, g, 0, tempOptions["BGOpacity"]);
	end
	local isDead;
	if ( updateDead ) then
		CT_RA_UpdateUnitDead(frame, 1);
	end
end

-- Update status

function CT_RA_UpdateUnitStatus(frame)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local frameName = frame.name;
	local id = frame.id;
	
	if ( not id ) then
		return;
	end
	
	local width, height, scale = CT_RA_GetFrameData(id);
	local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(id);
	
	if ( tempOptions["HideBorder"] ) then
		if ( height == 28 ) then
			frame.BuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -5);
			frame.DebuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -5);
		else
			frame.BuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -3);
			frame.DebuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -3);
		end
		frame:SetBackdropBorderColor(1, 1, 1, 0);
		
		frame.Percent:SetPoint("TOP", frameName, "TOP", 2, -16);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
	else
		frame:SetBackdropBorderColor(1, 1, 1, 1);
		frame.BuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -5);
		frame.DebuffButton1:SetPoint("TOPRIGHT", frameName, "TOPRIGHT", -5, -5);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.HPBG:SetPoint("TOPLEFT",frameName, "TOPLEFT", 10, -22);
		frame.Percent:SetPoint("TOP", frameName, "TOP", 2, -18);
	end
	if ( height == 32 or height == 29 or height == 28 or height == 25 ) then
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
	else
		frame.HPBar:Show();
		frame.HPBG:Show();
	end
	
	local stats = CT_RA_Stats[name];
	frame.Name:SetText(name);
	CT_RA_UpdateUnitDead(frame);
	if ( stats ) then
		CT_RA_UpdateUnitBuffs(stats["Buffs"], frame, name);
	end
	if ( online ) then
		CT_RA_UpdateUnitHealth(frame, 1);
		CT_RA_UpdateUnitMana(frame);
		if ( stats ) then
			CT_RA_UpdateUnitBuffs(stats["Buffs"], frame, name);
		end
	end
end

function CT_RA_CanShowInfo(id)
	local tempOptions = CT_RAMenu_Options["temp"];
	local stats = CT_RA_Stats[UnitName(id)];
	local showHP, hasFD, isRessed, isNotReady, showAFK, isDead;
	local hp = tempOptions["ShowHP"];
	
	showHP = ( hp and hp <= 3 );
	hasFD = ( stats and stats["FD"] );
	isRessed = ( stats and stats["Ressed"] );
	isNotReady = ( stats and stats["notready"] );
	showAFK = ( tempOptions["ShowAFK"] and stats and stats["AFK"] );
	isDead = ( ( stats and stats["Dead"] ) or UnitIsDead(id) or UnitIsGhost(id) );
	if ( showHP and not hasFD and not isRessed and not isNotReady and not showAFK and not isDead ) then
		return true;
	else
		return nil;
	end
end
-- Update mana
function CT_RA_UpdateUnitMana(frame)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local id = "raid" .. frame.id;
	local percent;
	if ( UnitExists(id) ) then
		percent = floor(UnitMana(id) / UnitManaMax(id) * 100);
	end
	frame.MPBar:SetValue(percent);
end

-- Update buffs
function CT_RA_UpdateUnitBuffs(buffs, frame, nick)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local num = 1;
	if ( buffs ) then
		if ( not tempOptions["ShowDebuffs"] or tempOptions["ShowBuffsDebuffed"] ) then
			for key, val in ipairs(tempOptions["BuffArray"]) do
				local name;
				if ( type(val["name"]) == "table" ) then
					if ( buffs[val["name"][1]] ) then
						name = val["name"][1];
					elseif ( buffs[val["name"][2]] ) then
						name = val["name"][2];
					end
				elseif ( buffs[val["name"]] ) then
					name = val["name"];
				end
				if ( name ) then
					if ( num <= 4 and val["show"] ~= -1 ) then -- Change 4 to number of buffs
						local button = frame["BuffButton"..num];
						frameCache[button].Icon:SetTexture("Interface\\Icons\\" .. CT_RA_BuffTextures[name][1]);
						button.name = name;
						button.owner = nick;
						button.texture = CT_RA_BuffTextures[name][1];
						button:Show();
						num = num + 1;
					end
				end
			end
		end
	end
	for i = num, 4, 1 do -- Change 4 to number of buffs
		frame["BuffButton"..i]:Hide();
	end
	local stats = CT_RA_Stats[nick];
	if ( stats ) then
		CT_RA_UpdateUnitDebuffs(stats["Debuffs"], frame);
	end
end

function CT_RA_UpdateUnitDead(frame, didUpdateHealth)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local raidid = "raid" .. frame.id;
	local name, rank, subgroup, level, class, fileName, zone, online, dead = GetRaidRosterInfo(frame.id);
	local color = RAID_CLASS_COLORS[fileName];
	if ( color ) then
		frame.Name:SetTextColor(color.r, color.g, color.b);
	end
	local stats, isFD, isDead = CT_RA_Stats[name], false, false;
	if ( UnitIsGhost(raidid) or UnitIsDead(raidid) ) then
		isFD = CT_RA_CheckFD(name, raidid)
		if ( isFD == 0 ) then
			isDead = 1;
			-- Scan buffs&debuffs on death
			CT_RA_ScanPartyAuras(raidid);
		end
	end
	local height = tempOptions["MemberHeight"];
	if ( ( ( class == CT_RA_WARRIOR or class == CT_RA_ROGUE ) and tempOptions["HideRP"] ) or ( class ~= CT_RA_WARRIOR and class ~= CT_RA_ROGUE and tempOptions["HideMP"] ) ) then
		height = height - 4;
	end
	if ( not online ) then
		for i = 1, 4, 1 do
			if ( i <= 2 ) then
				frame["DebuffButton"..i]:Hide();
			end
			frame["BuffButton"..i]:Hide();
		end
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(37);
		else
			frame:SetHeight(40);
		end
		if ( name ) then
			if ( not stats ) then
				CT_RA_Stats[name] = {
					["Buffs"] = { },
					["Debuffs"] = { },
					["Position"] = { },
				};
				stats = CT_RA_Stats[name];
			end
			if ( not stats["Offline"] ) then
				stats["Offline"] = 1;
			end
		end
		frame.status = "offline";
		frame.Status:SetText("오프라인");
		frame.Status:Show();
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
		return;
	elseif ( stats and stats["notready"] ) then
		frame.Status:Show();
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(37);
		else
			frame:SetHeight(40);
		end
		
		if ( stats["notready"] == 1 ) then
			frame.status = "noreply";
			frame.Status:SetText("응답 없음");
			frame:SetBackdropColor(0.45, 0.45, 0.45, 1);
		else
			frame.status = "notready";
			frame.Status:SetText("준비 않됨");
			frame:SetBackdropColor(0.8, 0.45, 0.45, 1);
		end

		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	elseif ( isFD == 1 ) then
		frame.status = "feigndeath";
		frame.Status:Show();
		frame.Status:SetText("죽은척하기");
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] and ( ( ( class == CT_RA_WARRIOR or class == CT_RA_ROGUE ) and tempOptions["HideRP"] ) or ( class ~= CT_RA_WARRIOR and class ~= CT_RA_ROGUE and tempOptions["HideMP"] ) ) ) then
			frame:SetHeight(height+3);
		end
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	elseif ( isFD == 2 ) then
		frame.status = "spiritofredemption";
		frame.Status:Show();
		frame.Status:SetText("구원의 영혼");
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] and ( ( ( class == CT_RA_WARRIOR or class == CT_RA_ROGUE ) and tempOptions["HideRP"] ) or ( class ~= CT_RA_WARRIOR and class ~= CT_RA_ROGUE and tempOptions["HideMP"] ) ) ) then
			frame:SetHeight(height+3);
		end
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	elseif ( stats and stats["Ressed"] ) then
		frame.status = "resurrected";
		frame.Status:Show();
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(37);
		else
			frame:SetHeight(40);
		end
		if ( stats["Ressed"] == 1 ) then
			frame.Status:SetText("부활받음");
		elseif ( stats["Ressed"] == 2 ) then
			frame.Status:SetText("영혼석 있음");
		end
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	elseif ( isDead ) then
		frame.status = "dead";
		for i = 1, 4, 1 do
			if ( i <= 2 ) then
				frame["DebuffButton"..i]:Hide();
			end
			frame["BuffButton"..i]:Hide();
		end
		frame.Status:Show();
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(37);
		else
			frame:SetHeight(40);
		end
		frame.Status:SetText("죽음");
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	elseif ( stats and stats["AFK"] and tempOptions["ShowAFK"] ) then
		frame.status = "afk";
		frame.Status:Show();
		frame:SetBackdropColor(0.3, 0.3, 0.3, 1);
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(37);
		else
			frame:SetHeight(40);
		end

		frame.Status:SetText("자리비움");
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.Percent:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame:SetAlpha(1);
	else
		if ( frame.status and not didUpdateHealth ) then
			CT_RA_UpdateUnitHealth(frame);
		end
		local canShowInfo = CT_RA_CanShowInfo("raid"..frame.id);
		frame.status = nil;
		frame:SetBackdropColor(tempOptions["DefaultColor"].r, tempOptions["DefaultColor"].g, tempOptions["DefaultColor"].b, tempOptions["DefaultColor"].a);
		if ( tempOptions["MemberHeight"] == 40 ) then
			frame.HPBar:Show();
			frame.HPBG:Show();
			if ( canShowInfo ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
		end
		if ( tempOptions["HideBorder"] ) then
			frame:SetHeight(height-3);
		else
			frame:SetHeight(height);
		end
		if ( class == CT_RA_WARRIOR ) then
			frame.MPBar:SetStatusBarColor(1, 0, 0);
			frame.MPBG:SetVertexColor(1, 0, 0, tempOptions["BGOpacity"]);
		elseif ( class == CT_RA_ROGUE ) then
			frame.MPBar:SetStatusBarColor(1, 1, 0);
			frame.MPBG:SetVertexColor(1, 1, 0, tempOptions["BGOpacity"]);
		else
			frame.MPBar:SetStatusBarColor(0, 0, 1);
			frame.MPBG:SetVertexColor(0, 0, 1, tempOptions["BGOpacity"]);
		end
		frame.Status:Hide();
		if ( ( ( class == CT_RA_WARRIOR or class == CT_RA_ROGUE ) and not tempOptions["HideRP"] ) or ( class ~= CT_RA_WARRIOR and class ~= CT_RA_ROGUE and not tempOptions["HideMP"] ) ) then
			frame.MPBar:Show();
			frame.MPBG:Show();
			if ( canShowInfo ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
		else
			frame.MPBar:Hide();
			frame.MPBG:Hide();
		end
		
		if ( stats ) then
			local debuffs = stats.Debuffs;
			local numDebuffs = debuffs.n;
			if ( numDebuffs and numDebuffs > 0 ) then
				CT_RA_UpdateUnitDebuffs(debuffs, frame);
			end
		end
	end
	if ( stats ) then
		stats["Offline"] = nil;
	end
end

-- Update debuffs
function CT_RA_UpdateUnitDebuffs(debuffs, frame)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not frame or not tempOptions["ShowGroups"][frame.frameParent.id] ) then
		return;
	end
	local num = 1;
	if ( tempOptions["ShowBuffsDebuffed"] ) then
		num = 2;
	end
	local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(frame.id);
	local stats, setbg = CT_RA_Stats[name], 0;
	if ( name and stats and online and not UnitIsGhost("raid" .. frame.id) and ( not UnitIsDead("raid" .. frame.id) or stats["FD"] ) ) then
		if ( not frame.status ) then
			local defaultColors = tempOptions["DefaultColor"];
			frame:SetBackdropColor(defaultColors.r, defaultColors.g, defaultColors.b, defaultColors.a);
		end
		if ( debuffs ) then
			for key, val in ipairs(tempOptions["DebuffColors"]) do
				for k, v in pairs(debuffs) do
					if ( k ~= "n" ) then
						local en, de, fr, ko;
						if ( type(val["type"]) == "table" ) then
							en = val["type"]["en"];
							de = val["type"]["de"];
							fr = val["type"]["fr"];
							ko = val["type"]["ko"];
						else
							ko = val["type"];
						end
						if ( ( ( en and en == v[1] ) or ( de and de == v[1] ) or ( fr and fr == v[1] ) or ( ko and ko == v[1] ) ) and val["id"] ~= -1 ) then
							if ( tempOptions["ShowBuffsDebuffed"] and num >= 1 ) then
								local button = frame["DebuffButton"..num];
								frameCache[button].Icon:SetTexture("Interface\\Icons\\" ..v[3]);
								button.name = k;
								button.owner = name;
								button.texture = v[3];
								button:Show();
								num = num - 1;
							elseif ( not tempOptions["ShowBuffsDebuffed"] and tempOptions["ShowDebuffs"] and num <= 2 ) then
								local button = frame["DebuffButton"..num];
								frameCache[button].Icon:SetTexture("Interface\\Icons\\" ..v[3]);
								button.name = k;
								button.owner = name;
								button.texture = v[3];
								button:Show();
								num = num + 1;
							end
							if ( setbg == 0 and not frame.status ) then
								frame:SetBackdropColor(val.r, val.g, val.b, val.a);
								setbg = 1;
							end
						end
					end
				end
			end
		end
		if ( tempOptions["ShowBuffsDebuffed"] ) then
			if ( num < 1 ) then
				for i = 1, 4, 1 do
					frame["BuffButton"..i]:Hide();
				end
			end
			for i = num, 1, -1 do
				frame["DebuffButton"..i]:Hide();
			end
		else
			for i = num, 2, 1 do
				frame["DebuffButton"..i]:Hide();
			end
		end
	end
end

-- Get info

function CT_RA_SortClassArray(arr)

	local classValues = {
		[CT_RA_WARRIOR] = 8,
		[CT_RA_PALADIN] = 7,
		[CT_RA_DRUID] = 6,
		[CT_RA_MAGE] = 5,
		[CT_RA_WARLOCK] = 4,
		[CT_RA_ROGUE] = 3,
		[CT_RA_HUNTER] = 2,
		[CT_RA_PRIEST] = 1,
		[CT_RA_SHAMAN] = 0
	};

	local limit, st, j, temp, swapped;
	limit = #arr;
	st = 0;
	while ( st < limit ) do
		swapped = false;
		st = st + 1;
		limit = limit - 1;
		local val, val1;

		for j = st, limit, 1 do
			if ( arr[j]["class"] ) then
				val = classValues[arr[j]["class"]];
			else
				val = 0;
			end
			if ( arr[j+1]["class"] ) then
				val1 = classValues[arr[j+1]["class"]];
			else
				val1 = 0;
			end
			if ( val < val1 ) then
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
				swapped = true;
			end
		end
		if ( not swapped ) then return arr; end

		swapped = false;
		for j=limit, st, -1 do
			if ( arr[j]["class"] ) then
				val = classValues[arr[j]["class"]];
			else
				val = 0;
			end
			if ( arr[j+1]["class"] ) then
				val1 = classValues[arr[j+1]["class"]];
			else
				val1 = 0;
			end

			if ( val < val1 ) then
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
				swapped = true;
			end
		end
		if ( not swapped ) then return arr; end
	end
	return arr;
end

function CT_RA_UpdateMT(raidid, mtid, frame, key, val)
	local tempOptions = CT_RAMenu_Options["temp"];
	local frameName = frame.name;
	local mtwidth, ptwidth, height = CT_RA_GetMTFrameData();
	
	frame:SetWidth(mtwidth); frame:SetHeight(height);
	if ( tempOptions["ShowMTTT"] and not UnitIsUnit(mtid, raidid.."target") and
	     not UnitIsPlayer(raidid) and UnitExists(raidid.."target") and not tempOptions["HideColorChange"] ) then
	     	frame:SetBackdropColor(1, 0, 0, 0.5);
	else
		local defaultColors = tempOptions.DefaultColor;
		frame:SetBackdropColor(defaultColors.r, defaultColors.g, defaultColors.b, defaultColors.a);
	end
	if ( tempOptions["HideBorder"] ) then
		frame:SetBackdropBorderColor(1, 1, 1, 0);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -16);
	else
		frame:SetBackdropBorderColor(1, 1, 1, 1);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -18);
	end
	if ( raidid and UnitExists(raidid) and strlen(UnitName(raidid) or "") > 0 ) then
		local health, healthmax, mana, manamax = UnitHealth(raidid), UnitHealthMax(raidid), UnitMana(raidid), UnitManaMax(raidid);
		frame.Name:SetHeight(15);
		frame.Status:Hide();
		frame.HPBar:Show();
		frame.HPBG:Show();
		frame.MPBar:Show();
		frame.MPBG:Show();
		frame.Name:Show();
		local manaType = UnitPowerType(raidid);
		if ( ( manaType == 0 and not tempOptions["HideMP"] ) or ( manaType > 0 and not tempOptions["HideRP"] and UnitIsPlayer(raidid) ) ) then
			local manaTbl = ManaBarColor[manaType];
			frame.MPBar:SetStatusBarColor(manaTbl.r, manaTbl.g, manaTbl.b);
			frame.MPBG:SetVertexColor(manaTbl.r, manaTbl.g, manaTbl.b, tempOptions["BGOpacity"]);
			frame.MPBar:SetMinMaxValues(0, manamax);
			frame.MPBar:SetValue(mana);
		else
			frame.MPBar:Hide();
			frame.MPBG:Hide();
		end
		if ( health and healthmax and not UnitIsDead(raidid) and not UnitIsGhost(raidid) ) then
			if ( tempOptions["ShowHP"] and tempOptions["ShowHP"] <= 4 ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
			
			frame.HPBar:SetMinMaxValues(0, healthmax);
			frame.HPBar:SetValue(health);
			
			frame.Percent:SetText(floor(health/healthmax*100+0.5) .. "%");
			local percent = health/healthmax;
			if ( percent >= 0 and percent <= 1 ) then
				local r, g;
				if ( percent > 0.5 ) then
					g = 1;
					r = (1.0 - percent) * 2;
				else
					r = 1;
					g = percent * 2;
				end
				frame.HPBar:SetStatusBarColor(r, g, 0);
				frame.HPBG:SetVertexColor(r, g, 0, tempOptions["BGOpacity"]);
			end
		elseif ( UnitIsDead(raidid) or UnitIsGhost(raidid) ) then
			frame.HPBar:Hide();
			frame.HPBG:Hide();
			frame.Percent:Hide();
			frame.MPBar:Hide();
			frame.MPBG:Hide();
			frame.Status:Show();
			frame.Status:SetText("죽음");
		else
			frame.HPBar:Hide();
			frame.HPBG:Hide();
		end
		frame.Name:SetText(UnitName(raidid));
		if ( UnitCanAttack("player", raidid) ) then
			frame.Name:SetTextColor(1, 0.5, 0);
		else
			frame.Name:SetTextColor(0.5, 1, 0);
		end
		frame.unitName = UnitName(raidid);
	else
		frame.Percent:Hide();
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame.Status:Hide();
		frame.Name:SetText(val .. "의 대상");
		frame.Name:SetHeight(30);
		frame.Name:SetTextColor(1, 0.82, 0);
	end
end

function CT_RA_UpdateMTs(forceUpdate)
	local tempOptions = CT_RAMenu_Options["temp"];
	local CT_RA_MainTanks = CT_RA_MainTanks;
	local num, val = 0;
	for i = 1, ( tempOptions["ShowNumMTs"] or 10 ), 1 do
		val = CT_RA_MainTanks[i];
		if ( val ) then
			num = num + 1;
			local frame = getglobal("CT_RAMTGroupUnitButton"..num);
			if ( frame ) then
				local frameParent = frame.frameParent;
				local raidid = frame.unit;
				if ( raidid ) then
					local mtid = raidid:match("^(%a+%d+)");
					local name, hppercent, mppercent = UnitName(raidid), UnitHealth(raidid)/UnitHealthMax(raidid), UnitMana(raidid)/UnitManaMax(raidid);
					if ( forceUpdate or name ~= ( frame.unitName or "" ) or hppercent ~= ( frame.hppercent or -1 ) or mppercent ~= ( frame.mppercent or -1 ) or not UnitIsConnected(raidid) ) then
						if ( not UnitIsConnected(raidid) ) then
							frame.unitName = nil; frame.hppercent = nil; frame.mppercent = nil;
						else
							frame.unitName = name; frame.hppercent = hppercent; frame.mppercent = mppercent;
						end
						CT_RA_UpdateMT(raidid, mtid, frame, key, val);
					end
				end
			end
		end
	end
	CT_RA_UpdateMTTTs(forceUpdate);
end

function CT_RA_UpdatePT(raidid, frame, key, val)
	local tempOptions = CT_RAMenu_Options["temp"];
	local frameName = frame.name;
	local mtwidth, ptwidth, height = CT_RA_GetMTFrameData();
	
	frame:SetWidth(mtwidth); frame:SetHeight(height);
	frame:SetBackdropColor(tempOptions["DefaultColor"]["r"], tempOptions["DefaultColor"]["g"], tempOptions["DefaultColor"]["b"], tempOptions["DefaultColor"]["a"]);
	
	if ( tempOptions["HideBorder"] ) then
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -16);
		frame:SetBackdropBorderColor(1, 1, 1, 0);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
	else
		frame:SetBackdropBorderColor(1, 1, 1, 1);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -18);
	end
	if ( raidid and UnitExists(raidid) and strlen(UnitName(raidid) or "") > 0 ) then
		local health, healthmax, mana, manamax = UnitHealth(raidid), UnitHealthMax(raidid), UnitMana(raidid), UnitManaMax(raidid);
		frame.Name:SetHeight(15);
		frame.Status:Hide();
		frame.HPBar:Show();
		frame.HPBG:Show();
		frame.MPBar:Show();
		frame.MPBG:Show();
		frame.Name:Show();
		local manaType = UnitPowerType(raidid);
		if ( ( manaType == 0 and not tempOptions["HideMP"] ) or ( manaType > 0 and not tempOptions["HideRP"] and UnitIsPlayer(raidid) ) ) then
			local manaTbl = ManaBarColor[manaType];
			getglobal(frame:GetName() .. "MPBar"):SetStatusBarColor(manaTbl.r, manaTbl.g, manaTbl.b);
			getglobal(frame:GetName() .. "MPBG"):SetVertexColor(manaTbl.r, manaTbl.g, manaTbl.b, tempOptions["BGOpacity"]);
			frame.MPBar:SetMinMaxValues(0, manamax);
			frame.MPBar:SetValue(mana);
		else
			frame.MPBar:Hide();
			frame.MPBG:Hide();
		end
		if ( health and healthmax and not UnitIsDead(raidid) and not UnitIsGhost(raidid) and UnitIsConnected(raidid) ) then
			if ( tempOptions["ShowHP"] and tempOptions["ShowHP"] <= 4 ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
			
			frame.HPBar:SetMinMaxValues(0, healthmax);
			frame.HPBar:SetValue(health);
			
			frame.Percent:SetText(floor(health/healthmax*100+0.5) .. "%");
			local percent = health/healthmax;
			if ( percent >= 0 and percent <= 1 ) then
				local r, g;
				if ( percent > 0.5 ) then
					g = 1;
					r = (1.0 - percent) * 2;
				else
					r = 1;
					g = percent * 2;
				end
				frame.HPBar:SetStatusBarColor(r, g, 0);
				frame.HPBG:SetVertexColor(r, g, 0, tempOptions["BGOpacity"]);
			end
		elseif ( not UnitIsConnected(raidid) ) then
			frame.HPBar:Hide();
			frame.HPBG:Hide();
			frame.Percent:Hide();
			frame.MPBar:Hide();
			frame.MPBG:Hide();
			frame.Status:Show();
			frame.Status:SetText("오프라인");
		elseif ( UnitIsDead(raidid) or UnitIsGhost(raidid) ) then
			frame.HPBar:Hide();
			frame.HPBG:Hide();
			frame.Percent:Hide();
			frame.MPBar:Hide();
			frame.MPBG:Hide();
			frame.Status:Show();
			local isFD = CT_RA_CheckFD(UnitName(raidid), raidid);
			if ( isFD == 1 ) then
				frame.Status:SetText("죽은척하기");
			elseif ( isFD == 2 ) then
				frame.Status:SetText("구원의 영혼");
			else
				frame.Status:SetText("죽음");
			end
		else
			frame.HPBar:Hide();
			frame.HPBG:Hide();
		end
		frame.Name:SetText(UnitName(raidid));
		frame.Name:SetTextColor(0.5, 1, 0);
		frame.unitName = UnitName(raidid);
	else
		frame.Percent:Hide();
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame.Status:Hide();
		frame.Name:SetText(val);
		frame.Name:SetHeight(30);
		frame.Name:SetTextColor(1, 0.82, 0);
	end
end

function CT_RA_UpdatePTs(forceUpdate)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( CT_RA_PTargets ) then
		for key, val in pairs(CT_RA_PTargets) do
			if ( key <= 10 ) then
				local frame = getglobal("CT_RAPTGroupUnitButton" .. key);
				if ( frame ) then
					local raidid = frame.unit;
					if ( raidid ) then
						local name, hppercent, mppercent = UnitName(raidid), UnitHealth(raidid)/UnitHealthMax(raidid), UnitMana(raidid)/UnitManaMax(raidid);
						if ( forceUpdate or name ~= ( frame.unitName or "" ) or hppercent ~= ( frame.hppercent or -1 ) or mppercent ~= ( frame.mppercent or -1 ) or not UnitIsConnected(raidid) ) then
							if ( not UnitIsConnected(raidid) ) then
								frame.unitName = nil; frame.hppercent = nil; frame.mppercent = nil;
							else
								frame.unitName = name; frame.hppercent = hppercent; frame.mppercent = mppercent;
							end
							CT_RA_UpdatePT(raidid, frame, key, val);
						end
					end
				end

			end
		end
	end
	CT_RA_UpdatePTTs(forceUpdate);
end

function CT_RA_UpdatePTT(raidid, frame, key, val)
	local tempOptions = CT_RAMenu_Options["temp"];
	local frameName = frame.name;
	local mtwidth, ptwidth, height = CT_RA_GetMTFrameData();
	
	frame:SetWidth(mtwidth); frame:SetHeight(height);
	frame:SetBackdropColor(tempOptions["DefaultColor"]["r"], tempOptions["DefaultColor"]["g"], tempOptions["DefaultColor"]["b"], tempOptions["DefaultColor"]["a"]);
	if ( tempOptions["HideBorder"] ) then
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -16);
		frame:SetBackdropBorderColor(1, 1, 1, 0);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
	else
		frame:SetBackdropBorderColor(1, 1, 1, 1);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -18);
	end
	if ( raidid and UnitExists(raidid) and strlen(UnitName(raidid) or "") > 0 ) then
		local health, healthmax, mana, manamax = UnitHealth(raidid), UnitHealthMax(raidid), UnitMana(raidid), UnitManaMax(raidid);
		frame.Name:SetHeight(15);
		frame.Status:Hide();
		frame.HPBar:Show();
		frame.HPBG:Show();
		frame.MPBar:Show();
		frame.MPBG:Show();
		frame.Name:Show();
		local manaType = UnitPowerType(raidid);
		if ( ( manaType == 0 and not tempOptions["HideMP"] ) or ( manaType > 0 and not tempOptions["HideRP"] and UnitIsPlayer(raidid) ) ) then
			local manaTbl = ManaBarColor[manaType];
			frame.MPBar:SetStatusBarColor(manaTbl.r, manaTbl.g, manaTbl.b);
			frame.MPBG:SetVertexColor(manaTbl.r, manaTbl.g, manaTbl.b, tempOptions["BGOpacity"]);
			frame.MPBar:SetMinMaxValues(0, manamax);
			frame.MPBar:SetValue(mana);
		else
			frame.MPBar:Hide();
			frame.MPBG:Hide();
		end
		if ( health and healthmax and not UnitIsDead(raidid) and not UnitIsGhost(raidid) ) then
			if ( tempOptions["ShowHP"] and tempOptions["ShowHP"] <= 4 ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
			
			frame.HPBar:SetMinMaxValues(0, healthmax);
			frame.HPBar:SetValue(health);
			
			frame.Percent:SetText(floor(health/healthmax*100+0.5) .. "%");
			local percent = health/healthmax;
			if ( percent >= 0 and percent <= 1 ) then
				local r, g;
				if ( percent > 0.5 ) then
					g = 1;
					r = (1.0 - percent) * 2;
				else
					r = 1;
					g = percent * 2;
				end
				frame.HPBar:SetStatusBarColor(r, g, 0);
				frame.HPBG:SetVertexColor(r, g, 0, tempOptions["BGOpacity"]);
			end
		elseif ( UnitIsDead(raidid) or UnitIsGhost(raidid) ) then
			frame.HPBar:Hide();
			frame.HPBG:Hide();
			frame.Percent:Hide();
			frame.MPBar:Hide();
			frame.MPBG:Hide();
			frame.Status:Show();
			frame.Status:SetText("죽음");
		else
			frame.HPBar:Hide();
			frame.HPBG:Hide();
		end
		frame.Name:SetText(UnitName(raidid));
		if ( UnitCanAttack("player", raidid) ) then
			frame.Name:SetTextColor(1, 0.5, 0);
		else
			frame.Name:SetTextColor(0.5, 1, 0);
		end
		frame.unitName = UnitName(raidid);
	else
		frame.Percent:Hide();
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame.Status:Hide();
		frame.Name:SetText("<대상 없음>");
		frame.Name:SetHeight(30);
		frame.Name:SetTextColor(1, 0.82, 0);
	end
end

function CT_RA_UpdatePTTs(forceUpdate)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( tempOptions["ShowPTT"] ) then
		for key, val in pairs(CT_RA_PTargets) do
			if ( key <= 10 ) then
				local frame = getglobal("CT_RAPTTGroupUnitButton"..key);
				if ( frame ) then
					local raidid = frame.unit;
					if ( raidid ) then
						local name, hppercent, mppercent = UnitName(raidid), UnitHealth(raidid)/UnitHealthMax(raidid), UnitMana(raidid)/UnitManaMax(raidid);
						if ( forceUpdate or name ~= ( frame.unitName or "" ) or hppercent ~= ( frame.hppercent or -1 ) or mppercent ~= ( frame.mppercent or -1 ) or not UnitIsConnected(raidid) ) then
							if ( not UnitIsConnected(raidid) ) then
								frame.unitName = nil; frame.hppercent = nil; frame.mppercent = nil;
							else
								frame.unitName = name; frame.hppercent = hppercent; frame.mppercent = mppercent;
							end
							CT_RA_UpdatePTT(raidid, frame, key, val);
						end
					end
				end
			end
		end
	end
end

function CT_RA_UpdateMTTT(raidid, mtid, frame, key, val)
	local tempOptions = CT_RAMenu_Options["temp"];
	local frameName = frame.name;
	local mtwidth, ptwidth, height = CT_RA_GetMTFrameData();
	
	frame:SetWidth(mtwidth); frame:SetHeight(height);
	if ( UnitExists(raidid.."target") and not UnitIsUnit(mtid, raidid) and
	     not UnitIsPlayer(raidid.."target") and not tempOptions["HideColorChange"] ) then
		frame:SetBackdropColor(1, 0, 0, 0.5);
	else
		local defaultColors = tempOptions.DefaultColor;
		frame:SetBackdropColor(defaultColors.r, defaultColors.g, defaultColors.b, defaultColors.a);
	end
	if ( tempOptions["HideBorder"] ) then
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -16);
		frame:SetBackdropBorderColor(1, 1, 1, 0);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -19);
	else
		frame:SetBackdropBorderColor(1, 1, 1, 1);
		frame.HPBar:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.HPBG:SetPoint("TOPLEFT", frameName, "TOPLEFT", 10, -22);
		frame.Percent:SetPoint("TOP", frameName, "TOPLEFT", 47, -18);
	end
	if ( raidid and UnitExists(raidid) and strlen(UnitName(raidid) or "") > 0 ) then
		local health, healthmax, mana, manamax = UnitHealth(raidid), UnitHealthMax(raidid), UnitMana(raidid), UnitManaMax(raidid);
		frame.Name:SetHeight(15);
		frame.Status:Hide();
		frame.HPBar:Show();
		frame.HPBG:Show();
		frame.MPBar:Show();
		frame.MPBG:Show();
		frame.Name:Show();
		local manaType = UnitPowerType(raidid);
		if ( ( manaType == 0 and not tempOptions["HideMP"] ) or ( manaType > 0 and not tempOptions["HideRP"] and UnitIsPlayer(raidid) ) ) then
			local manaTbl = ManaBarColor[manaType];
			frame.MPBar:SetStatusBarColor(manaTbl.r, manaTbl.g, manaTbl.b);
			frame.MPBG:SetVertexColor(manaTbl.r, manaTbl.g, manaTbl.b, tempOptions["BGOpacity"]);
			frame.MPBar:SetMinMaxValues(0, manamax);
			frame.MPBar:SetValue(mana);
		else
			frame.MPBar:Hide();
			frame.MPBG:Hide();
		end
		if ( health and healthmax and not UnitIsDead(raidid) and not UnitIsGhost(raidid) ) then
			if ( tempOptions["ShowHP"] and tempOptions["ShowHP"] <= 4 ) then
				frame.Percent:Show();
			else
				frame.Percent:Hide();
			end
			
			frame.HPBar:SetMinMaxValues(0, healthmax);
			frame.HPBar:SetValue(health);
			
			frame.Percent:SetText(floor(health/healthmax*100+0.5) .. "%");
			local percent = health/healthmax;
			if ( percent >= 0 and percent <= 1 ) then
				local r, g;
				if ( percent > 0.5 ) then
					g = 1;
					r = (1.0 - percent) * 2;
				else
					r = 1;
					g = percent * 2;
				end
				frame.HPBar:SetStatusBarColor(r, g, 0);
				frame.HPBG:SetVertexColor(r, g, 0, tempOptions["BGOpacity"]);
			end
		elseif ( UnitIsDead(raidid) or UnitIsGhost(raidid) ) then
			frame.HPBar:Hide();
			frame.HPBG:Hide();
			frame.Percent:Hide();
			frame.MPBar:Hide();
			frame.MPBG:Hide();
			frame.Status:Show();
			frame.Status:SetText("죽음");
		else
			frame.HPBar:Hide();
			frame.HPBG:Hide();
		end
		frame.Name:SetText(UnitName(raidid));
		if ( UnitCanAttack("player", raidid) ) then
			frame.Name:SetTextColor(1, 0.5, 0);
		else
			frame.Name:SetTextColor(0.5, 1, 0);
		end
		frame.unitName = UnitName(raidid);
	else
		frame.Percent:Hide();
		frame.HPBar:Hide();
		frame.HPBG:Hide();
		frame.MPBar:Hide();
		frame.MPBG:Hide();
		frame.Status:Hide();
		frame.Name:SetText("<대상 없음>");
		frame.Name:SetHeight(30);
		frame.Name:SetTextColor(1, 0.82, 0);
	end
end

function CT_RA_UpdateMTTTs(forceUpdate)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( not tempOptions["ShowMTTT"] ) then
		return;
	end
	local CT_RA_MainTanks = CT_RA_MainTanks;
	local num, val = 0;
	for i = 1, ( tempOptions["ShowNumMTs"] or 10 ), 1 do
		val = CT_RA_MainTanks[i];
		if ( val ) then
			num = num + 1;
			local frame = getglobal("CT_RAMTTGroupUnitButton"..num);
			if ( frame ) then
				local raidid = frame.unit;
				if ( raidid ) then
					local mtid = raidid:match("^(%a+%d+)");
					local name, hppercent, mppercent = UnitName(raidid), UnitHealth(raidid)/UnitHealthMax(raidid), UnitMana(raidid)/UnitManaMax(raidid);
					if ( forceUpdate or name ~= ( frame.unitName or "" ) or hppercent ~= ( frame.hppercent or -1 ) or mppercent ~= ( frame.mppercent or -1 ) or not UnitIsConnected(raidid) ) then
						if ( not UnitIsConnected(raidid) ) then
							frame.unitName = nil; frame.hppercent = nil; frame.mppercent = nil;
						else
							frame.unitName = name; frame.hppercent = hppercent; frame.mppercent = mppercent;
						end
						if ( name == UnitName("player") and not UnitIsPlayer(mtid .. "target") ) then
							local isMT;
							for k, v in pairs(CT_RA_MainTanks) do
								if ( v == UnitName("player") ) then
									isMT = 1;
									break;
								end
							end
							if ( not isMT and not CT_RA_UpdateFrame.hasAggroAlert and tempOptions["AggroNotifier"] ) then
								CT_RA_UpdateFrame.hasAggroAlert = 15;
								CT_RA_WarningFrame:AddMessage(UnitName(mtid .. "target") .. "에게 어그로를 먹었습니다!", 1, 0, 0, 1, UIERRORS_HOLD_TIME);
								if ( tempOptions["AggroNotifierSound"] ) then
									PlaySoundFile("Sound\\Spells\\PVPFlagTakenHorde.wav");
								end
							end
						end
						CT_RA_UpdateMTTT(raidid, mtid, frame, num, val);
					end
				end
			end
		end
	end
end

function CT_RA_UpdateGroupVisibility(num, noStatusUpdate)
	local tempOptions = CT_RAMenu_Options["temp"];
	local group = getglobal("CT_RAGroup" .. num);
	local drag = getglobal("CT_RAGroupDrag" .. num);
	if ( not tempOptions["ShowGroups"] or not tempOptions["ShowGroups"][num] or 
		not group.hasMembers ) then
			drag:Hide();
			frameCache[group].GroupName:Hide();
	elseif ( group.hasMembers ) then
		if ( tempOptions["LockGroups"] ) then
			drag:Hide();
		else
			drag:Show();
		end
		if ( tempOptions["HideNames"] ) then
			frameCache[group].GroupName:Hide();
		else
			frameCache[group].GroupName:Show();
		end
	end

	local frame, i = group:GetAttribute("child1"), 1;
	while ( frame ) do
		if ( not noStatusUpdate and tempOptions["ShowGroups"] and tempOptions["ShowGroups"][num] ) then
			CT_RA_UpdateUnitStatus(frame);
		end
		
		i = i + 1;
		frame = group:GetAttribute("child" .. i);
	end
end

function CT_RA_UpdateVisibility(noStatusUpdate)
	for i = 1, 8, 1 do
		CT_RA_UpdateGroupVisibility(i, noStatusUpdate);
	end
	if ( CT_RA_MainTanks ) then
		CT_RA_UpdateMTs();
	end
	if ( CT_RA_PTargets ) then
		CT_RA_UpdatePTs();
	end
end

local function prepareFrame(frame, dragFrame, template, initFunction, title, splitView, useModifier, isPT)
	local tempOptions = CT_RAMenu_Options["temp"];
	local numRaidMembers = CT_RA_NumRaidMembers;
	local initialConfigFunction = initFunction;
	
	local showReversed = tempOptions["ShowReversed"];
	local showHorizontal = splitView == nil and tempOptions["ShowHorizontal"];
	local hideBorder = tempOptions["HideBorder"];
	local removeSpace = hideBorder and tempOptions["HideSpace"];
	
	if ( not frame.init ) then
		frame:SetAttribute("template", template);
		frame.initialConfigFunction = initialConfigFunction;
		frame.GroupName = frameCache[frame].GroupName;
		frame.id = frame:GetID();
		frame.name = frame:GetName();
		frame.frameParent = frame:GetParent();
		frame.GroupName:ClearAllPoints();
		frame.GroupName:SetPoint("CENTER", dragFrame);
		frame.init = true;
		frame.useModifier = useModifier;
		if ( title ) then
			frame.GroupName:SetText(title);
		end
	end
	
	local splitOffset = ( hideBorder and 5 ) or 0;

	if ( showReversed ) then
		if ( showHorizontal ) then
			frame:SetAttribute("point", "RIGHT");
			CT_RA_LinkDrag(frame, dragFrame, "BOTTOMLEFT", "BOTTOMLEFT", 5, 14);
		elseif ( splitView == -1 ) then
			frame:SetAttribute("point", "BOTTOM");
			CT_RA_LinkDrag(frame, dragFrame, "BOTTOMRIGHT", "BOTTOM", splitOffset, 14);	
		elseif ( splitView == 1 ) then
			frame:SetAttribute("point", "BOTTOM");
			CT_RA_LinkDrag(frame, dragFrame, "BOTTOMLEFT", "BOTTOM", -splitOffset, 14);
		else
			frame:SetAttribute("point", "BOTTOM");
			CT_RA_LinkDrag(frame, dragFrame, "BOTTOM", "BOTTOM", 0, 14);
		end
	else
		frame:SetAttribute("sortDir", "ASC");
		if ( showHorizontal ) then
			frame:SetAttribute("point", "RIGHT");
			CT_RA_LinkDrag(frame, dragFrame, "TOPLEFT", "TOPLEFT", 5, -14);
		elseif ( splitView == -1 ) then
			frame:SetAttribute("point", "TOP");
			CT_RA_LinkDrag(frame, dragFrame, "TOPRIGHT", "TOP", splitOffset, -14);
		elseif ( splitView == 1 ) then
			frame:SetAttribute("point", "TOP");
			CT_RA_LinkDrag(frame, dragFrame, "TOPLEFT", "TOP", -splitOffset, -14);
		else
			frame:SetAttribute("point", "TOP");
			CT_RA_LinkDrag(frame, dragFrame, "TOP", "TOP", 0, -14);
		end
	end

	if ( removeSpace ) then
		if ( showHorizontal ) then
			frame:SetAttribute("xOffset", 10);
			frame:SetAttribute("yOffset", 0);
		else
			frame:SetAttribute("xOffset", 0);
			frame:SetAttribute("yOffset", ( showReversed and -10 ) or 10);
		end
	else
		frame:SetAttribute("xOffset", 0);
		frame:SetAttribute("yOffset", 0);
	end
	return frame;
end

function CT_RA_UpdateRaidFrames()
	local tempOptions = CT_RAMenu_Options["temp"];
	local numRaidMembers = CT_RA_NumRaidMembers;
	
	local showGroups = tempOptions["ShowGroups"];
	local lockGroups = tempOptions["LockGroups"];
	local hideNames = tempOptions["HideNames"];
	
	-- Normal groups
	for i = 1, 8, 1 do
		if ( numRaidMembers > 0 and showGroups and showGroups[i] ) then
			getglobal("CT_RAGroup"..i):Show();
		else
			getglobal("CT_RAGroup"..i):Hide();
		end
	end
	
	-- Main Tanks
	if ( numRaidMembers > 0 and next(CT_RA_MainTanks) ) then
		CT_RAMTGroup:Show();
		if ( hideNames ) then
			CT_RAMTGroup.GroupName:Hide();
		else
			CT_RAMTGroup.GroupName:Show();
		end
		if ( lockGroups ) then
			CT_RAMTGroupDrag:Hide();
		else
			CT_RAMTGroupDrag:Show();
		end
	else
		CT_RAMTGroup:Hide();
		CT_RAMTGroupDrag:Hide();
	end
	
	-- Main Tank Target's Target
	if ( numRaidMembers > 0 and tempOptions["ShowMTTT"] ) then
		if ( next(CT_RA_MainTanks) ) then CT_RAMTTGroup:Show(); else CT_RAMTTGroup:Hide(); end
	else
		CT_RAMTTGroup:Hide();
	end
	
	-- Player Targets
	if ( numRaidMembers > 0 and next(CT_RA_PTargets) ) then
		CT_RAPTGroup:Show();
		if ( hideNames ) then
			CT_RAPTGroup.GroupName:Hide();
		else
			CT_RAPTGroup.GroupName:Show();
		end
		if ( lockGroups ) then
			CT_RAPTGroupDrag:Hide();
		else
			CT_RAPTGroupDrag:Show();
		end
	else
		CT_RAPTGroup:Hide();
		CT_RAPTGroupDrag:Hide();
	end
	
	-- Player Target's Target
	if ( numRaidMembers > 0 and tempOptions["ShowPTT"] ) then
		if ( next(CT_RA_PTargets) ) then CT_RAPTTGroup:Show(); else CT_RAPTTGroup:Hide(); end
	else
		CT_RAPTTGroup:Hide();
	end
end

function CT_RA_UpdateRaidFrameData()
	local tempOptions = CT_RAMenu_Options["temp"];
	local numRaidMembers = CT_RA_NumRaidMembers;

	-- Main Tanks
	local showMTT = tempOptions["ShowMTTT"] or false;
	local frame = prepareFrame(CT_RAMTGroup, CT_RAMTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, "메인탱커 대상", showMTT and -1, "target");
	local list, num, obj = "", 0;
	if ( not tempOptions["HideMTs"] ) then
		for i = 1, 10, 1 do
			obj = CT_RA_MainTanks[i];
			if ( obj ) then
				list = list .. obj .. ",";
				num = num + 1;
				if ( num == tempOptions["ShowNumMTs"] ) then
					break;
				end
			end
		end
		list = strsub(list, 0, -2);
	end
	
	frame:SetAttribute("nameList", list);
	
	-- Main Tank Target's Target
	if ( showMTT ) then
		frame = prepareFrame(CT_RAMTTGroup, CT_RAMTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, nil, 1, "targettarget");
		frame:SetAttribute("nameList", list);
	end
	
	-- Player Targets
	local showPTT = tempOptions["ShowPTT"] or false;
	local frame = prepareFrame(CT_RAPTGroup, CT_RAPTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, "플레이어 대상", showPTT and -1, nil, true);
	list, obj = "";
	for i = 1, 10, 1 do
		obj = CT_RA_PTargets[i];
		if ( obj ) then
			list = list .. obj .. ",";
		end
	end
	list = strsub(list, 0, -2);
	frame:SetAttribute("nameList", list);
	
	-- Player Target's Target
	if ( showPTT ) then
		frame = prepareFrame(CT_RAPTTGroup, CT_RAPTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, nil, 1, "target", true);
		frame:SetAttribute("nameList", list);
	end
	CT_RA_UpdateRaidFrames();
end

function CT_RA_UpdateRaidFrameOptions()
	local tempOptions = CT_RAMenu_Options["temp"];
	local numRaidMembers = CT_RA_NumRaidMembers;
	local sorting = tempOptions["SORTTYPE"];
	if ( sorting == "class" ) then
		if ( UnitFactionGroup("player") == "Horde" ) then
			CT_RA_ClassIndices[8] = CT_RA_ClassIndices[9];
		end
	end
	
	-- Normal groups
	local showGroups = tempOptions["ShowGroups"];
	local subSortByName = tempOptions["SubSortByName"];
	for i = 1, 8, 1 do
		frame = prepareFrame(getglobal("CT_RAGroup"..i), getglobal("CT_RAGroupDrag"..i), "CT_RAGroupMemberTemplate", CT_RA_SetupFrame)
		if ( not frame ) then
			frame = getglobal("CT_RAGroup"..i);
		end
		frame:SetAttribute("groupFilter", (sorting=="class" and CT_RA_ClassIndices[i] ) or i);
		if ( subSortByName ) then
			frame:SetAttribute("sortMethod", "NAME");
		else
			frame:SetAttribute("sortMethod", "INDEX");
		end
	end

	-- Main Tanks
	local showMTT = tempOptions["ShowMTTT"] or false;
	local frame = prepareFrame(CT_RAMTGroup, CT_RAMTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, "메인탱커 대상", showMTT and -1, "target");
	local list, num, obj = "", 0;
	if ( not tempOptions["HideMTs"] ) then
		for i = 1, 10, 1 do
			obj = CT_RA_MainTanks[i];
			if ( obj ) then
				list = list .. obj .. ",";
				num = num + 1;
				if ( num == tempOptions["ShowNumMTs"] ) then
					break;
				end
			end
		end
		list = strsub(list, 0, -2);
	end
	
	frame:SetAttribute("nameList", list);
	
	-- Main Tank Target's Target
	if ( showMTT ) then
		frame = prepareFrame(CT_RAMTTGroup, CT_RAMTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, nil, 1, "targettarget");
		frame:SetAttribute("nameList", list);
	end
	
	-- Player Targets
	local showPTT = tempOptions["ShowPTT"] or false;
	local frame = prepareFrame(CT_RAPTGroup, CT_RAPTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, "플레이어 대상", showPTT and -1, nil, true);
	list, obj = "";
	for i = 1, 10, 1 do
		obj = CT_RA_PTargets[i];
		if ( obj ) then
			list = list .. obj .. ",";
		end
	end
	list = strsub(list, 0, -2);
	frame:SetAttribute("nameList", list);
	
	-- Player Target's Target
	if ( showPTT ) then
		frame = prepareFrame(CT_RAPTTGroup, CT_RAPTGroupDrag, "CT_RAMTMemberTemplate", CT_RA_SetupMTFrame, nil, 1, "target", true);
		frame:SetAttribute("nameList", list);
	end
	CT_RAPTGroup.GroupName:ClearAllPoints();
	CT_RAPTGroup.GroupName:SetPoint("CENTER", CT_RAPTGroupDrag);
	CT_RA_UpdateRaidFrames();
end

function CT_RA_GetFrameData(id)
	local tempOptions = CT_RAMenu_Options["temp"];
	local width, height, scale = 90, tempOptions["MemberHeight"], tempOptions["WindowScaling"];
	local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(id);
	
	if ( ( ( class == CT_RA_WARRIOR or class == CT_RA_ROGUE ) and tempOptions["HideRP"] ) or ( class ~= CT_RA_WARRIOR and class ~= CT_RA_ROGUE and tempOptions["HideMP"] ) ) then
		height = height - 4;
	end
	
	if ( tempOptions["HideBorder"] ) then
		if ( not online ) then height = 37; else height = height - 3; end
	else
		if ( not online ) then height = 40; end
	end
	
	return width, height, scale;
end

function CT_RA_GetMTFrameData()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( tempOptions["HideBorder"] ) then
		return 90, 90, 36;
	else
		return 90, 90, 40;
	end
end

function CT_RA_SetupFrame(frame)
	local width, height, scale = CT_RA_GetFrameData(-1);
	frame:SetAttribute("initial-width", width);
	frame:SetAttribute("initial-height", height);
	if ( CT_RAMenu_Options.temp.SubSortByName ) then
		frame:SetAttribute("sortMethod", "NAME");
	end
end

function CT_RA_SetupMTFrame(frame)
	local mtwidth, ptwidth, height = CT_RA_GetMTFrameData();
	if ( frame.frameParent.isPT ) then
		frame:SetAttribute("initial-width", ptwidth);
	else
		frame:SetAttribute("initial-width", mtwidth);
	end
	frame:SetAttribute("initial-height", height);
	if ( frame.frameParent.useModifier ) then
		frame:SetAttribute("unitsuffix", frame.frameParent.useModifier);
	end
end

function CT_RA_UpdateRaidGroup(updateType)
	local tempOptions = CT_RAMenu_Options["temp"];
	local sortType = tempOptions["SORTTYPE"];
	if ( sortType == "group" ) then
		CT_RA_SortByGroup();
	elseif ( sortType == "class" ) then
		CT_RA_SortByClass();
	end
	local numRaidMembers = GetNumRaidMembers();
	local name, rank, subgroup, level, class, fileName, zone, online, isDead;

	for i=1, MAX_RAID_MEMBERS do
		if ( i <= numRaidMembers ) then
			local unitid = "raid" .. i;
			name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i);
			if ( UnitIsDead(unitid) or UnitIsGhost(unitid) ) then
				isDead = 1;
			end
			-- Set Rank
			if ( name == UnitName("player") ) then
				if ( rank >= 2 and CT_RA_Level and CT_RA_Level < 2 ) then
					-- Check if we have to auto-promote people
					for j = 1, numRaidMembers, 1 do
						local pName, pRank = GetRaidRosterInfo(j);
						if ( pRank < 1 and pName and CT_RATab_AutoPromotions and CT_RATab_AutoPromotions[pName] ) then
							PromoteToAssistant(pName);
							CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. pName .. "|r님을 자동 승급했습니다.", 1, 0.5, 0);
						end
					end
				end
				CT_RA_Level = rank;
			end
			local button = CT_RA_UnitIDFrameMap["raid"..i];
			if ( button ) then
				local group = button.frameParent;
				if ( group ) then
					if ( tempOptions["ShowGroups"] and tempOptions["ShowGroups"][group.id] ) then
						button.Name:SetText(name);
						button.unitName = name;
						if ( button.update or updateType == 0 ) then
							CT_RA_UpdateUnitStatus(button);
						else
							CT_RA_UpdateUnitDead(button);
							local stats = CT_RA_Stats[name];
							if ( updateType == 2 and stats ) then
								CT_RA_UpdateUnitBuffs(stats["Buffs"], button, name);
							end
						end
						button.update = nil;
					end
				end
			end
		end
	end
	CT_RA_UpdateVisibility(1);
end
		
function CT_RA_MemberFrame_OnEnter()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( SpellIsTargeting() ) then
		SetCursor("CAST_CURSOR");
	end
	local parent = this.frameParent;
	local id = this.id;
	if ( strsub(this.name, 1, 12) == "CT_RAMTGroup" ) then
		local name;
		if ( CT_RA_MainTanks[id] ) then
			name = CT_RA_MainTanks[id];
		end
		for i = 1, GetNumRaidMembers(), 1 do
			local memberName = GetRaidRosterInfo(i);
			if ( name == memberName ) then
				id = i;
				break;
			end
		end
	elseif ( strsub(this.name, 1, 12) == "CT_RAPTGroup" ) then
		local name;
		if ( CT_RA_PTargets[id] ) then
			name = CT_RA_PTargets[id];
		end
		for i = 1, GetNumRaidMembers(), 1 do
			local memberName = GetRaidRosterInfo(i);
			if ( name == memberName ) then
				id = i;
				break;
			end
		end
	end
	local unitid = "raid"..id;
	if ( SpellIsTargeting() and not SpellCanTargetUnit(unitid) ) then
		SetCursor("CAST_ERROR_CURSOR");
	end
	if ( tempOptions["HideTooltip"] ) then
		return;
	end
	local xp = "LEFT";
	local yp = "BOTTOM";
	local xthis, ythis = this:GetCenter();
	local xui, yui = UIParent:GetCenter();
	if ( xthis < xui ) then
		xp = "RIGHT";
	end
	if ( ythis < yui ) then
		yp = "TOP";
	end
	GameTooltip:SetOwner(this, "ANCHOR_" .. yp .. xp);
	local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(id);
	local stats = CT_RA_Stats[name];
	local version = stats;
	if ( version ) then
		version = version["Version"];
	end
	if ( name == UnitName("player") ) then
		zone = GetRealZoneText();
		version = CT_RA_VersionNumber;
	end
	local color = RAID_CLASS_COLORS[fileName];
	if ( not color ) then
		color = { ["r"] = 1, ["g"] = 1, ["b"] = 1 };
	end
	GameTooltip:AddDoubleLine(name, level, color.r, color.g, color.b, 1, 1, 1);
	if ( UnitRace(unitid) and class ) then
		GameTooltip:AddLine(UnitRace(unitid) .. " " .. class, 1, 1, 1);
	end
	GameTooltip:AddLine(zone, 1, 1, 1);
	
	if ( not version ) then
		if ( not stats or not stats["Reporting"] ) then
			GameTooltip:AddLine("공격대 도우미 없음", 0.7, 0.7, 0.7);
		else
			GameTooltip:AddLine("공격대 도우미 <1.077", 1, 1, 1);
		end
	else
		GameTooltip:AddLine("공격대 도우미 " .. version, 1, 1, 1);
	end
		
	if ( stats and stats["AFK"] ) then
		if ( type(stats["AFK"][1]) == "string" ) then
			GameTooltip:AddLine("자리비움: " .. stats["AFK"][1]);
		end
		GameTooltip:AddLine(CT_RA_FormatTime(stats["AFK"][2]) .. " 째 자리비움 상태");
	elseif ( CT_RA_Stats[name] and stats["DND"] ) then
		if ( type(stats["DND"][1]) == "string" ) then
			GameTooltip:AddLine("다른용무중: " .. stats["DND"][1]);
		end
		GameTooltip:AddLine(CT_RA_FormatTime(stats["DND"][2]) .. " 째 다른 용무중 상태");
	end
	if ( stats and stats["Offline"] ) then
		GameTooltip:AddLine(CT_RA_FormatTime(stats["Offline"]) .. " 째 오프라인 상태");
	elseif ( stats and stats["FD"] ) then
		if ( stats["FD"] < 360 ) then
			GameTooltip:AddLine(CT_RA_FormatTime(360-stats["FD"]) .. " 째 죽은척 중");
		end
	elseif ( stats and stats["Dead"] ) then
		if ( stats["Dead"] < 360 and not UnitIsGhost(unitid) ) then
			GameTooltip:AddLine("무덤이동까지 " .. CT_RA_FormatTime(360-stats["Dead"]) .. " 남음");
		else
			GameTooltip:AddLine(CT_RA_FormatTime(stats["Dead"]) .. " 째 유령 상태");
		end
	end
	if ( stats and stats["Rebirth"] and stats["Rebirth"] > 0 ) then
		GameTooltip:AddLine("환생 남은시간: " .. CT_RA_FormatTime(stats["Rebirth"]));
	elseif ( stats and stats["Reincarnation"] and stats["Reincarnation"] > 0 ) then
		GameTooltip:AddLine("윤회 남은시간: " .. CT_RA_FormatTime(stats["Reincarnation"]));
	elseif ( stats and stats["Soulstone"] and stats["Soulstone"] > 0 ) then
		GameTooltip:AddLine("영혼석 부활 남은시간: " .. CT_RA_FormatTime(stats["Soulstone"]));
	end
	GameTooltip:Show();
	CT_RA_CurrentMemberFrame = this;
end
		
function CT_RA_FormatTime(num)
	num = floor(num + 0.5);
	local hour, min, sec, str = 0, 0, 0, "";

	hour = floor(num/3600);
	min = floor(mod(num, 3600)/60);
	sec = mod(num, 60);
	
	if ( hour > 0 ) then
		str = hour .. "시간";
	end

	if ( min > 0 ) then
		if ( strlen(str) > 0 ) then
			str = str .. ", ";
		end
		str = str .. min .. "분";
	end

	if ( sec > 0 or strlen(str) == 0 ) then
		if ( strlen(str) > 0 ) then
			str = str .. ", ";
		end
		str = str .. sec .. "초";
	end
	return str;

end


function CT_RA_Drag_OnEnter()
	CT_RAMenuHelp_SetTooltip();
	GameTooltip:SetText("드래그 하세요");
end

function CT_RA_BuffButton_OnEnter()
	if ( CT_RA_LockPosition ) then return; end
	CT_RAMenuHelp_SetTooltip();
	local left, secure;
	local stats = CT_RA_Stats[this.owner];
	if ( stats and stats["Buffs"][this.name] and stats["Buffs"][this.name][2] ) then
		left = stats["Buffs"][this.name][2];
		if ( stats["Reporting"] and ( stats["Version"] or 0 ) >= 1.38 ) then
			secure = 1;
		end
	end
	if ( this.name and left ) then
		local str;
		if ( left >= 60 ) then
			secs = mod(left, 60);
			mins = (left-secs)/60;
		else
			mins = 0;
			secs = left;
		end
		if ( mins < 0 ) then mins = "00"; elseif ( mins < 10 ) then mins = "0" .. mins; end
		if ( secs < 0 ) then secs = "00"; elseif ( secs < 10 ) then secs = "0" .. secs; end
		if ( not secure ) then
			GameTooltip:SetText(this.name .. " (" .. mins .. ":" .. secs .. "?)");
		else
			GameTooltip:SetText(this.name .. " (" .. mins .. ":" .. secs .. ")");
		end
	elseif ( this.name ) then
		GameTooltip:SetText(this.name);
	end
end

function CT_RA_AssistMT(id)
	if ( CT_RA_MainTanks[id] ) then
		for i = 1, GetNumRaidMembers(), 1 do
			local uId = "raid" .. i;
			if ( UnitName(uId) == CT_RA_MainTanks[id] ) then
				AssistUnit(uId);
				return;
			end
		end
	end
end

function CT_RA_SendStatus()
	CT_RA_Auras = { 
		["buffs"] = { },
		["debuffs"] = { }
	}; -- Reset everything so every buff & debuff is treated as new
	CT_RA_AddMessage("V " .. CT_RA_VersionNumber);
end

function CT_RA_AddToQueue(type, nick, name)
	tinsert(CT_RA_BuffsToCure, { ["type"] = type, ["nick"] = nick, ["name"] = name });
end

function CT_RA_GetDebuff()
	return tremove(CT_RA_BuffsToCure);
end

function CT_RA_GetCure(school)
	local arr = {
		[CT_RA_PRIEST] = { [CT_RA_MAGIC] = CT_RA_DISPELMAGIC, [CT_RA_DISEASE] = { CT_RA_CUREDISEASE, CT_RA_ABOLISHDISEASE } },
		[CT_RA_SHAMAN] = { [CT_RA_DISEASE] = CT_RA_CUREDISEASE, [CT_RA_POISON] = CT_RA_CUREPOISON },
		[CT_RA_DRUID] = { [CT_RA_CURSE] = CT_RA_REMOVECURSE, [CT_RA_POISON] = { CT_RA_CUREPOISON, CT_RA_ABOLISHPOISON } },
		[CT_RA_MAGE] = { [CT_RA_CURSE] = CT_RA_REMOVELESSERCURSE },
		[CT_RA_PALADIN] = { [CT_RA_MAGIC] = CT_RA_CLEANSE, [CT_RA_POISON] = { CT_RA_PURIFY, CT_RA_CLEANSE }, [CT_RA_DISEASE] = { CT_RA_PURIFY, CT_RA_CLEANSE } }
	};
	local playerArr = arr[UnitClass("player")];
	if ( playerArr and playerArr[school] ) then
		local tmp = playerArr[school];
		if ( type(tmp) == "table" ) then
			for i = #tmp, 1, -1 do
				if ( CT_RA_ClassSpells[tmp[i]] ) then
					return tmp[i];
				end
			end
			return nil;
		else
			if ( CT_RA_ClassSpells[tmp] ) then
				return tmp;
			else
				return nil;
			end
		end
	end
	return nil;
end

--[[
function CT_RA_UpdateRaidGroupColors()
	local tempOptions = CT_RAMenu_Options["temp"];
	local defaultColors = tempOptions["DefaultColor"];
	local r, g, b, a = defaultColors.r, defaultColors.g, defaultColors.b, defaultColors.a;
	for y = 1, 40, 1 do
		local frame = CT_RA_UnitIDFrameMap["raid"..y];
		if ( y <= 10 ) then
			local mt = getglobal("CT_RAMTGroupUnitButton" .. y);
			mt:SetBackdropColor(r, g, b, a);
			mt.Percent:SetTextColor(r, g, b);
			mt = getglobal("CT_RAPTGroupUnitButton" .. y);
			mt:SetBackdropColor(r, g, b, a);
			mt.Percent:SetTextColor(r, g, b);
		end
		if ( not frame.status ) then
			frame:SetBackdropColor(r, g, b, a);
		end
		frame.Percent:SetTextColor(r, g, b);
		local name = UnitName("raid"..y);
		if ( CT_RA_Stats[name] ) then
			CT_RA_UpdateUnitBuffs(CT_RA_Stats[name]["Buffs"], frame, name);
		end
	end
end ]]

function CT_RA_UpdateRaidGroupColors()
	local tempOptions = CT_RAMenu_Options["temp"];
	local member;
	for y = 1, 40, 1 do
		if ( y <= 10 ) then
			member = CT_RAMTGroup:GetAttribute("child".. y);
			if ( member ) then
				member:SetBackdropColor(tempOptions["DefaultColor"].r, tempOptions["DefaultColor"].g, tempOptions["DefaultColor"].b, tempOptions["DefaultColor"].a);
				member.Percent:SetTextColor(tempOptions["PercentColor"].r, tempOptions["PercentColor"].g, tempOptions["PercentColor"].b);
			end
		end
		member = CT_RA_UnitIDFrameMap["raid"..y];
		if ( member ) then
			if ( not member.status ) then
				member:SetBackdropColor(tempOptions["DefaultColor"].r, tempOptions["DefaultColor"].g, tempOptions["DefaultColor"].b, tempOptions["DefaultColor"].a);
			end
			member.Percent:SetTextColor(tempOptions["PercentColor"].r, tempOptions["PercentColor"].g, tempOptions["PercentColor"].b);
			if ( CT_RA_Stats[UnitName("raid"..y)] ) then
				CT_RA_UpdateUnitBuffs(CT_RA_Stats[UnitName("raid"..y)]["Buffs"], member, UnitName("raid"..y));
			end
		end
	end
end

function CT_RA_UpdateRaidMovability()
	local tempOptions = CT_RAMenu_Options["temp"];
	for i = 1, 8, 1 do
		if ( tempOptions["LockGroups"] or not tempOptions["ShowGroups"] or not tempOptions["ShowGroups"][i] ) then
			getglobal("CT_RAGroupDrag" .. i):Hide();
		else
			if ( getglobal("CT_RAGroup" .. i).hasMembers ) then
				getglobal("CT_RAGroupDrag" .. i):Show();
			end
		end
	end
	if ( tempOptions["LockGroups"] or not tempOptions["ShowMTs"] or tempOptions["HideMTs"] ) then
		getglobal("CT_RAMTGroupDrag"):Hide();
	else
		for i = 1, 10, 1 do
			if ( CT_RA_MainTanks[i] ) then
				CT_RAMTGroupDrag:Show();
				break;
			else
				CT_RAMTGroupDrag:Hide();
			end
		end
	end
end

function CT_RA_AddToBuffQueue(name, nick)
	tinsert(CT_RA_BuffsToRecast, { ["name"] = name, ["nick"] = nick });
end

function CT_RA_GetBuff()
	return tremove(CT_RA_BuffsToRecast);
end

function CT_RA_Print(msg, r, g, b)
	if ( SIMPLE_CHAT == "1" ) then
		DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b);
	else
		local oldArgs = {
			event, this, arg1, arg2, arg3, arg4, arg6
		};
		event = "CHAT_MSG_CTRAID";
		arg1 = msg;
		arg2, arg3, arg4, arg6 = "", "", "", "";
		local info = CT_RA_ChatInfo[UnitName("player")];

		if ( not info ) then
			info = CT_RA_ChatInfo["Default"];
		end
		for i = 1, 7, 1 do
			for k, v in pairs(info["show"]) do
				local frameName = "ChatFrame" .. i;
				if ( v == frameName ) then
					this = getglobal(frameName);
					CT_RA_OldChatFrame_MessageEventHandler(event);
					break;
				end
			end
		end
		event, this, arg1, arg2, arg3, arg4, arg6 = oldArgs[1], oldArgs[2], oldArgs[3], oldArgs[4], oldArgs[5], oldArgs[6], oldArgs[7];
	end
end

function CT_RA_SubSortByName()
	local tempOptions = CT_RAMenu_Options["temp"];
	-- Sort the name of the players in the raid.
	-- Returns an array containing raid roster numbers in player name sequence, followed by unfilled player slots.
	-- Thanks to Dargen of Eternal Keggers for this function
	local temp;
	local subsort = {};
	local count;
	local name;
	count = GetNumRaidMembers();
	if ( not tempOptions["SubSortByName"] ) then
		for i = 1, MAX_RAID_MEMBERS, 1 do
			subsort[i] = {};
			subsort[i][1] = i;
		end
		return subsort;
	end
	local playerName = UnitName("player");
	for i = 1, MAX_RAID_MEMBERS, 1 do
		subsort[i] = {};
		subsort[i][1] = i;
		if ( i <= count ) then
			name = UnitName("raid" .. i);
			if ( not name ) then name = playerName; end
			if ((name == nil) or (name == UNKNOWNOBJECT) or (name == UKNOWNBEING)) then name = ""; end
			subsort[i][2] = name;
		else
			subsort[i][2] = "";
		end
	end
	local swap;
	for j = 1, count - 1, 1 do
		swap = false;
		for i = 1, count - j, 1 do
			if ( subsort[i][2] > subsort[i+1][2] ) then
				-- Swap
				temp = subsort[i];
				subsort[i] = subsort[i+1];
				subsort[i+1] = temp;
				swap = true;
			end
		end
		if ( not swap ) then
			break;
		end
	end
	return subsort;
end

function CT_RA_SortByClass()
	local tempOptions = CT_RAMenu_Options["temp"];
	CT_RA_SetSortType("class");
	CT_RA_ButtonIndexes = { };
	CT_RA_CurrPositions = { };
	local groupnum = 1;
	local membernum = 1;
	
	for k, v in pairs(CT_RA_ClassPositions) do
		if ( k ~= CT_RA_SHAMAN or ( UnitFactionGroup("player") and UnitFactionGroup("player") == "Horde" ) ) then
			getglobal("CT_RAOptionsGroup" .. v .. "Label"):SetText(k);
		end
	end
	for i = 1, 40, 1 do
		if ( i <= 8 ) then
			getglobal("CT_RAGroup" .. i).num = 0;
		end
	end
	local subsort = CT_RA_SubSortByName();
	for j = 1, GetNumRaidMembers(), 1 do
		i = subsort[j][1];
			local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i);
		if ( class and CT_RA_ClassPositions[class] ) then
			local group = getglobal("CT_RAGroup" .. CT_RA_ClassPositions[class]);
			if ( name ) then
				CT_RA_CurrPositions[name] = { CT_RA_ClassPositions[class], i };
			end
			local button = CT_RA_UnitIDFrameMap["raid"..i];
			getglobal(group:GetName() .. "GroupName"):SetText(class);
		end
	end
end

function CT_RA_SortByGroup()
	local tempOptions = CT_RAMenu_Options["temp"];
	CT_RA_SetSortType("group");
	CT_RA_ButtonIndexes = { };
	CT_RA_CurrPositions = { };
	local groupnum = 1;
	local membernum = 1;
	for i = 1, 40, 1 do
		if ( i <= 8 ) then
			local group = getglobal("CT_RAGroup"..i);
			group.num = 0;
			local label = getglobal("CT_RAOptionsGroup" .. i .. "Label");
			if ( label ) then
				label:SetText("파티 " .. i);
			end
		end
	end
	local subsort = CT_RA_SubSortByName();
	for j = 1, GetNumRaidMembers(), 1 do
		i = subsort[j][1];
		local name, rank, subgroup, level, class, fileName, zone, online, isDead = GetRaidRosterInfo(i);
		if ( CT_RA_Stats[name] ) then
			local groupid = subgroup;
			if ( name and CT_RA_CurrPositions[name] ) then
				groupid = CT_RA_CurrPositions[name][1];
			elseif ( name ) then
				CT_RA_CurrPositions[name] = { subgroup, j };
			end
			local group = getglobal("CT_RAGroup" .. groupid);
			frameCache[group].GroupName:SetText("파티 " .. subgroup);
		end
	end
end

function CT_RA_LinkDrag(frame, drag, point, relative, x, y)
	frame:ClearAllPoints();
	frame:SetPoint(point, drag:GetName(), relative, x, y);
end

CT_RA_ConvertedRaid = 1;
CT_RA_HasInvited = { };

local CT_RA_GameTooltip_ClearMoney;

local function CT_RA_MoneyToggle()
	if( CT_RA_GameTooltip_ClearMoney ) then
		GameTooltip_ClearMoney = CT_RA_GameTooltip_ClearMoney;
		CT_RA_GameTooltip_ClearMoney = nil;
	else
		CT_RA_GameTooltip_ClearMoney = GameTooltip_ClearMoney;
		GameTooltip_ClearMoney = CT_RA_GameTooltipFunc_ClearMoney;
	end
end

function CT_RA_GameTooltipFunc_ClearMoney()

end

function CT_RA_GetBuffIndex(name, filter)
	local i = 1;
	local buffIndex, untilCancelled = GetPlayerBuff(i, filter);
	while ( buffIndex ~= 0 ) do
		CT_RA_MoneyToggle();
		CT_RATooltip:SetPlayerBuff(buffIndex);
		CT_RA_MoneyToggle();
		local tooltipName = CT_RATooltipTextLeft1:GetText();
		if ( tooltipName and name == tooltipName ) then
			return buffIndex;
		end
		i = i + 1;
		buffIndex, untilCancelled = GetPlayerBuff(i, filter);
	end
	return nil;
end
	

function CT_RA_UpdateFrame_OnUpdate(elapsed)
	if ( this.showDialog ) then
		this.showDialog = this.showDialog - elapsed;
		if ( this.showDialog <= 0 ) then
			if ( CT_RAChanges_DisplayDialog ) then
				CT_RAChanges_DisplayDialog();
			end
			this.showDialog = nil;
		end
	end

	if ( this.lastInvite ) then
		this.lastInvite = this.lastInvite - elapsed;
		if ( this.lastInvite <= 0 ) then
			this.lastInvite = nil;
			this.inviteName = nil;
		end
	end
	if ( this.invite ) then
		this.invite = this.invite - elapsed;
		if ( this.invite <= 0 ) then
			if ( not CT_RA_ConvertedRaid ) then
				GuildRoster();
				CT_RA_ConvertedRaid = 1;
				ConvertToRaid();
				this.invite = 3;
			else
				CT_RA_InviteGuild(CT_RA_MinLevel, CT_RA_MaxLevel);
				this.invite = nil;
			end
		end
	end
	if ( this.startinviting ) then
		this.startinviting = this.startinviting - elapsed;
		if ( this.startinviting <= 0 ) then
			this.startinviting = nil;
			CT_RA_HasInvited = { };
			if ( GetNumRaidMembers() == 0 ) then
				CT_RA_ConvertedRaid = nil;
			else
				CT_RA_ConvertedRaid = 1;
			end
			local inZone = "";
			if ( CT_RA_ZoneInvite ) then
				inZone = GetRealZoneText() .. "에 있는 ";
			end
			local numInvites = CT_RA_InviteGuild(CT_RA_MinLevel, CT_RA_MaxLevel);
			if ( CT_RA_MinLevel == CT_RA_MaxLevel ) then
				CT_RA_Print("<공격대 도우미> " .. inZone .. "|c00FFFFFF" .. CT_RA_MinLevel .. "|r레벨의 길드원을 초대하였습니다.", 1, 0.5, 0);
			else
				CT_RA_Print("<공격대 도우미> " .. inZone .. "|c00FFFFFF" .. CT_RA_MinLevel .. "|r부터 |c00FFFFFF" .. CT_RA_MaxLevel .. "|r레벨의 길드원을 초대하였습니다.", 1, 0.5, 0);
			end
		end
	end
	if ( this.closeroster ) then
		this.closeroster = this.closeroster - elapsed;
		if ( this.closeroster <= 0 ) then
			HideUIPanel(FriendsFrame);
			this.closeroster = nil;
		end
	end
	
	-- Only run the ones below if we're in a raid.
	if ( CT_RA_NumRaidMembers == 0 ) then
		return;
	end
	
	this.mouseOverUpdate = this.mouseOverUpdate - elapsed;
	if ( this.mouseOverUpdate <= 0 ) then
		this.mouseOverUpdate = 0.1;
		if ( CT_RA_CurrentMemberFrame ) then
			local parent = CT_RA_CurrentMemberFrame.frameParent;
			if ( SpellIsTargeting() and ( strsub(parent.name, 1, 12) == "CT_RAMTGroup" or SpellCanTargetUnit("raid" .. parent.id) ) ) then
				SetCursor("CAST_CURSOR");
			elseif ( SpellIsTargeting() ) then
				SetCursor("CAST_ERROR_CURSOR");
			end
		end
	end
	
	if ( this.hasAggroAlert ) then
		this.hasAggroAlert = this.hasAggroAlert - elapsed;
		if ( this.hasAggroAlert <= 0 ) then
			this.hasAggroAlert = nil;
		end
	end
	this.updateAFK = this.updateAFK + elapsed;
	if ( this.updateAFK >= 1 ) then
		this.updateAFK = this.updateAFK - 1;
		for k, v in pairs(CT_RA_Stats) do
			if ( v["AFK"] ) then
				v["AFK"][2] = v["AFK"][2] + 1;
			end
			if ( v["DND"] ) then
				v["DND"][2] = v["DND"][2] + 1;
			end
			if ( v["Dead"] ) then
				v["Dead"] = v["Dead"] + 1;
			end
			if ( v["Offline"] ) then
				v["Offline"] = v["Offline"] + 1;
			end
			if ( v["FD"] ) then
				v["FD"] = v["FD"] + 1;
			end
			if ( v["Rebirth"] ) then
				v["Rebirth"] = v["Rebirth"] - 1;
			end
			if ( v["Reincarnation"] ) then
				v["Reincarnation"] = v["Reincarnation"] - 1;
			end
			if ( v["Soulstone"] ) then
				v["Soulstone"] = v["Soulstone"] - 1;
			end
		end
	end

	this.update = this.update + elapsed;
	if ( this.update >= 1 ) then
		for k, v in pairs(CT_RA_BuffTimeLeft) do
			local buffIndex, buffTimeLeft, buffName;
			buffIndex = CT_RA_GetBuffIndex(k, "HELPFUL");
			if ( buffIndex ) then
				buffTimeLeft = GetPlayerBuffTimeLeft(buffIndex);
				if ( buffTimeLeft ) then
					if ( abs(CT_RA_BuffTimeLeft[k]-buffTimeLeft) >= 2 ) then
						local index, num;
						for key, val in pairs(CT_RAMenu_Options["temp"]["BuffArray"]) do
							if ( type(val["name"]) == "table" ) then
								if ( k == val["name"][1] ) then
									buffName = k;
									index, num = val["index"], 1;
									break;
								elseif ( k == val["name"][2] ) then
									buffName = k;
									index, num = val["index"], 2;
									break;
								end
							elseif ( val["name"] == k ) then
								buffName = k;
								index, num = val["index"], 0;
								break;
							end
						end
						if ( not index and not num ) then
							if ( k == CT_RA_FEIGNDEATH[CT_RA_GetLocale()] ) then
								buffName = CT_RA_FEIGNDEATH[CT_RA_GetLocale()];
								index, num = -1, 0;
							end
						end
						if ( index and num ) then
							local playerName = UnitName("player");
							local stats = CT_RA_Stats[playerName];
							if ( not stats ) then
								CT_RA_Stats[playerName] = {
									["Buffs"] = { },
									["Debuffs"] = { },
									["Position"] = { }
								};
								stats = CT_RA_Stats[playerName];
							end
							stats["Buffs"][buffName] = { string.find(GetPlayerBuffTexture(buffIndex), "([%w_&]+)$"), floor(buffTimeLeft+0.5) };
							CT_RA_AddMessage("RN " .. floor(buffTimeLeft+0.5) .. " " .. index .. " " .. num);
						end
					end
					CT_RA_BuffTimeLeft[k] = buffTimeLeft;
				end
			end
		end
		for k, v in pairs(CT_RA_Stats) do
			if ( v["Buffs"] ) then
				for key, val in pairs(v["Buffs"]) do
					if ( key ~= "n" and type(val) == "table" and val[2] ) then
						val[2] = val[2] - 1;
					end
				end
			end
		end
		this.update = this.update - 1;
	end
	if ( this.time ) then
		this.time = this.time - elapsed;
		if ( this.time <= 0 ) then
			this.time = nil;
			CT_RA_AddMessage("SR");
			if ( CT_RA_VersionNumber ) then
				CT_RA_AddMessage("V " .. CT_RA_VersionNumber);
			end
		end
		if ( this.SS ) then
			this.SS = nil;
		end
	end

	if ( this.SS ) then
		this.SS = this.SS - elapsed;
		if ( this.SS <= 0 ) then
			this.SS = nil;
			CT_RA_AddMessage("SR");
			if ( CT_RA_VersionNumber ) then
				CT_RA_AddMessage("V " .. CT_RA_VersionNumber);
			end
		end
	end
	if ( this.scheduleUpdate ) then
		this.scheduleUpdate = this.scheduleUpdate - elapsed;
		if ( this.scheduleUpdate <= 0 ) then
			if ( CT_RA_InCombat ) then
				this.scheduleUpdate = 1;
			else
				this.scheduleUpdate = nil;
				for i = 1, GetNumRaidMembers(), 1 do
					if ( UnitIsUnit("raid" .. i, "player") ) then
						local useless, useless, subgroup = GetRaidRosterInfo(i);
						this.updateDelay = subgroup;
						return;
					end
				end
			end
		end
	end
	if ( this.scheduleMTUpdate ) then
		this.scheduleMTUpdate = this.scheduleMTUpdate - elapsed;
		if ( this.scheduleMTUpdate <= 0 ) then
			this.scheduleMTUpdate = nil;
			if ( CT_RA_IsSendingWithVersion(1.08) ) then
				for k, v in pairs(CT_RA_MainTanks) do
					CT_RA_AddMessage("SET " .. k .. " " .. v);
				end
			end
		end
	end
	if ( this.updateDelay ) then
		this.updateDelay = this.updateDelay - elapsed;
		if ( this.updateDelay <= 0 ) then
			this.updateDelay = nil;
			CT_RA_SendStatus();
			CT_RA_UpdateRaidGroup(1);
		end
	end
	if ( this.voteTimer ) then
		this.voteTimer = this.voteTimer - elapsed;
		if ( this.voteTimer <= 0 ) then
			if ( CT_RA_VotePerson ) then
				local numCount = 0;
				for i = 1, GetNumRaidMembers(), 1 do
					if ( UnitIsConnected("raid" .. i) ) then
						numCount = numCount + 1;
					end
				end
				local noVotes = numCount-(CT_RA_VotePerson[2]+CT_RA_VotePerson[3]+1);
				local yesPercent, noPercent, noVotePercent = 0, 0, 0;
				if ( CT_RA_VotePerson[2] > 0 ) then
					yesPercent = floor(CT_RA_VotePerson[2]/(CT_RA_VotePerson[2]+CT_RA_VotePerson[3]+noVotes)*100+0.5);
				end
				if ( CT_RA_VotePerson[3] > 0 ) then
					noPercent = floor(CT_RA_VotePerson[3]/(CT_RA_VotePerson[2]+CT_RA_VotePerson[3]+noVotes)*100+0.5);
				end
				if ( yesPercent+noPercent < 100 ) then
					noVotePercent = 100-(yesPercent+noPercent);
				end
				CT_RA_Print("<공격대 도우미> \"|c00FFFFFF" .. CT_RA_VotePerson[4] .. "|r\"에 대한 투표 결과: |c00FFFFFF" .. CT_RA_VotePerson[2] .. "|r (|c00FFFFFF" .. yesPercent .. "%|r) 예 / |c00FFFFFF" .. CT_RA_VotePerson[3] .. "|r (|c00FFFFFF" .. noPercent .. "%|r) 아니오 / |c00FFFFFF" .. noVotes .. "|r (|c00FFFFFF" .. noVotePercent .. "%|r) 기권.", 1, 0.5, 0);
				SendChatMessage("<공격대 도우미> \"" .. CT_RA_VotePerson[4] .. "\"에 대한 투표 결과: " .. CT_RA_VotePerson[2] .. " (" .. yesPercent .. "%) 예 / " .. CT_RA_VotePerson[3] .. " (" .. noPercent .. "%) 아니오 / " .. noVotes .. " (" .. noVotePercent .. "%) 기권.", "RAID");
				CT_RA_VotePerson = nil;
			end
			this.voteTimer = nil;
		end
	end
	if ( this.readyTimer ) then
		this.readyTimer = this.readyTimer - elapsed;
		if ( this.readyTimer <= 0 ) then
			CT_RA_CheckReady_Person = nil;
			this.readyTimer = nil;
			local numNotReady, numAfk = 0, 0
			local notReadyString, afkString = "", "";
			for k, v in pairs(CT_RA_Stats) do
				if ( v["notready"] and v["notready"] == 2 ) then
					numNotReady = numNotReady + 1;
					if ( strlen(notReadyString) > 0 ) then
						notReadyString = notReadyString .. ", ";
					end
					notReadyString = notReadyString .. "|c00FFFFFF" .. k .. "|r";
				elseif ( v["notready"] and v["notready"] == 1 ) then
					numAfk = numAfk + 1;
					if ( strlen(afkString) > 0 ) then
						afkString = afkString .. ", ";
					end
					afkString = afkString .. "|c00FFFFFF" .. k .. "|r";
				end
				CT_RA_Stats[k]["notready"] = nil;
			end
			if ( numNotReady > 0 ) then
				if ( numNotReady == 1 ) then
					CT_RA_Print("<공격대 도우미> " .. notReadyString .. "님이 준비되지 않았습니다.", 1, 1, 0);
				elseif ( numNotReady >= 8 ) then
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numNotReady .. "|r명의 공대원이 준비되지 않았습니다.", 1, 1, 0);
				else
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numNotReady .. "|r명의 공대원(" .. notReadyString .. ")이 준비되지 않았습니다.", 1, 1, 0);
				end
				CT_RA_UpdateRaidGroup(1);
			end
			if ( numAfk > 0 ) then
				if ( numAfk == 1 ) then
					CT_RA_Print("<공격대 도우미> " ..afkString .. "님이 키보드에서 손을 뗀지 오래되셨습니다.", 1, 1, 0);
				elseif ( numAfk >= 8 ) then
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numAfk.. "|r명의 공대원이 키보드에서 손을 뗀지 오래되었습니다.", 1, 1, 0);
				else
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numAfk .. "|r명의 공대원이 (" .. afkString .. ") 키보드에서 손을 뗀지 오래되었습니다.", 1, 1, 0);
				end
				CT_RA_UpdateRaidGroup(1);
			end
		end
	end
	if ( this.rlyTimer ) then
		this.rlyTimer = this.rlyTimer - elapsed;
		if ( this.rlyTimer <= 0 ) then
			this.rlyTimer = nil;
			local numNotReady, numAfk = 0, 0
			local notReadyString, afkString = "", "";
			for k, v in pairs(CT_RA_Stats) do
				if ( v["rly"] and v["rly"] == 2 ) then
					numNotReady = numNotReady + 1;
					if ( strlen(notReadyString) > 0 ) then
						notReadyString = notReadyString .. ", ";
					end
					notReadyString = notReadyString .. "|c00FFFFFF" .. k .. "|r";
				elseif ( v["rly"] and v["rly"] == 1 ) then
					numAfk = numAfk + 1;
					if ( strlen(afkString) > 0 ) then
						afkString = afkString .. ", ";
					end
					afkString = afkString .. "|c00FFFFFF" .. k .. "|r";
				end
				CT_RA_Stats[k]["rly"] = nil;
			end
			if ( numNotReady > 0 ) then
				if ( numNotReady == 1 ) then
					CT_RA_Print("<공격대 도우미> " .. notReadyString .. "명이 |c00FFFFFF준비 안됨|r이라고 말했습니다.", 1, 1, 0);
				elseif ( numNotReady >= 8 ) then
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numNotReady .. "|r명의 공대원이 |c00FFFFFF준비 안됨.|r이라고 말했습니다.", 1, 1, 0);
				else
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numNotReady .. "|r명의 공대원이 (" .. notReadyString .. ") |c00FFFFFF준비 안됨|r이라고 말했습니다.", 1, 1, 0);
				end
				CT_RA_UpdateRaidGroup(1);
			end
			if ( numAfk > 0 ) then
				if ( numAfk == 1 ) then
					CT_RA_Print("<공격대 도우미> " ..afkString .. "명이 무응답 상태입니다.", 1, 1, 0);
				elseif ( numAfk >= 8 ) then
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numAfk.. "|r명의 공대원이 무응답 상태입니다.", 1, 1, 0);
				else
					CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. numAfk .. "|r명의 공대원이 (" .. afkString .. ") 무응답 상태입니다.", 1, 1, 0);
				end
				CT_RA_UpdateRaidGroup(1);
			end
		end
	end
	if ( CT_RA_Squelch > 0 ) then
		CT_RA_Squelch = CT_RA_Squelch - elapsed;
		if ( CT_RA_Squelch <= 0 ) then
			CT_RA_Squelch = 0;
			CT_RA_Print("<공격대 도우미> 정숙 모드가 자동으로 해제되었습니다. (시간 만료).", 1, 0.5, 0);
		end
	end
	if ( this.updateMT ) then
		this.updateMT = this.updateMT - elapsed;
		if ( this.updateMT <= 0 ) then
			this.updateMT = 0.25;
			CT_RA_UpdateMTs();
			CT_RA_UpdatePTs();
		end
	end
	for k, v in pairs(CT_RA_CurrDebuffs) do
		CT_RA_CurrDebuffs[k][1] = CT_RA_CurrDebuffs[k][1] - elapsed;
		if ( CT_RA_CurrDebuffs[k][1] < 0 ) then
			local _, _, name, dType = string.find(k, "^([^@]+)@(.+)$");
			local msg = "";
			if ( name == dType ) then
				dType = "";
			else
				dType = " (|c00FFFFFF" .. dType .. "|r)";
			end
			if ( CT_RA_CurrDebuffs[k][2] == 1 ) then
				CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. CT_RA_CurrDebuffs[k][4] .. "|r님이 '|c00FFFFFF" .. name .. "|r' 디버프에 걸렸습니다." .. dType .. msg , 1, 0.5, 0);
			else
				CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. CT_RA_CurrDebuffs[k][2] .. "|r명의 사용자가 '|c00FFFFFF" .. name .. "|r' 디버프에 걸렸습니다." .. dType .. msg , 1, 0.5, 0);
			end
			CT_RA_CurrDebuffs[k] = nil;
		end
	end
end
function CT_RA_UpdateFrame_OnEvent(event)
	if ( event == "PARTY_MEMBERS_CHANGED" ) then
		if ( not CT_RA_ConvertedRaid ) then
			this.invite = 3;
		end
	elseif ( event == "CHAT_MSG_SYSTEM" ) then
		local _, _, name = string.find(arg1, "(.+)님은 이미 파티에 속해 있습니다.");
		if ( name and this.inviteName and this.inviteName == name ) then
			this.inviteName = nil;
			this.lastInvite = nil;
			SendChatMessage("<공격대 도우미> 당신은 이미 파티에 속해 있습니다.", "WHISPER", nil, name);
		end
	end
end

function CT_RA_InviteGuild(min, max)
	local offline = GetGuildRosterShowOffline();
	local selection = GetGuildRosterSelection();
	SetGuildRosterShowOffline(0);
	SetGuildRosterSelection(0);
	GetGuildRosterInfo(0);
	local inviteBeforeRaid = 4-GetNumPartyMembers();
	local numInvites = 0;
	local numGuildMembers = GetNumGuildMembers();
	CT_RA_UpdateFrame.closeroster = 2;
	local RealZoneText = GetRealZoneText();
	if (RealZoneText == nil) then RealZoneText = "?"; end
	local playerName = UnitName("player");
	for i = 1, numGuildMembers, 1 do
		local name, rank, rankIndex, level, class, zone, note, officernote, online, status = GetGuildRosterInfo(i);
		if ( level >= min and level <= max and name ~= playerName and not CT_RA_HasInvited[i] and online ) then
			if ( zone == nil ) then zone = "???"; end
				if ( not CT_RA_ZoneInvite or ( CT_RA_ZoneInvite and zone == RealZoneText ) ) then
				CT_RA_HasInvited[i] = 1;
				InviteUnit(name);
				numInvites = numInvites + 1;
				if ( numInvites == inviteBeforeRaid and not CT_RA_ConvertedRaid ) then 
					CT_RA_UpdateFrame.invite = 1.5;
					break;
				end
			end
		end
	end
	SetGuildRosterShowOffline(offline);
	SetGuildRosterSelection(selection);
	return numInvites;
end

function CT_RA_ProcessMessages(elapsed)
	if ( this.flush ) then
		this.flush = this.flush - elapsed;
		if ( this.flush <= 0 ) then
			this.flush = 1;
			this.numMessagesSent = 0;
		end
	end
	if ( this.elapsed ) then
		this.elapsed = this.elapsed - elapsed;
		if ( this.elapsed <= 0 ) then
			if ( #CT_RA_Comm_MessageQueue > 0 and this.numMessagesSent < 4 ) then
				CT_RA_SendMessageQueue();
			end
			this.elapsed = 0.1;
		end
	end
end
function CT_RA_SendMessageQueue()
	local retstr = "";
	local numSent = 0;

	for key, val in pairs(CT_RA_Comm_MessageQueue) do
		if ( strlen(retstr)+strlen(val)+1 > 255 ) then
			CT_RA_SendMessage(retstr, 1);
			this.numMessagesSent = this.numMessagesSent + 1;
			tremove(CT_RA_Comm_MessageQueue, key);
			if ( this.numMessagesSent == 4 ) then
				return;
			end
			retstr = "";
		end
		if ( retstr ~= "" ) then
			retstr = retstr .. "#";
		end
		retstr = retstr .. val;
	end
	if ( retstr ~= "" ) then
		CT_RA_SendMessage(retstr, 1);
		this.numMessagesSent = this.numMessagesSent + 1;
	end
	CT_RA_Comm_MessageQueue = { };
end

function CT_RA_Split(msg, char)
	local arr = { };
	while (string.find(msg, char) ) do
		local iStart, iEnd = string.find(msg, char);
		tinsert(arr, strsub(msg, 1, iStart-1));
		msg = strsub(msg, iEnd+1, strlen(msg));
	end
	if ( strlen(msg) > 0 ) then
		tinsert(arr, msg);
	end
	return arr;
end

function CT_RA_IsSendingWithVersion(version)
	local playerName = UnitName("player");
	local names = { };
	if ( not CT_RA_Level or CT_RA_Level < 1 ) then
		return nil;
	end
	for i = 1, GetNumRaidMembers(), 1 do
		local name, rank, subgroup, level, class, fileName = GetRaidRosterInfo(i);
		local stats = CT_RA_Stats[name];
		if ( rank >= 1 and name ~= playerName and stats and stats["Version"] and stats["Version"] >= version and name < playerName ) then
			return nil;
		end
	end
	return 1;
end

function CT_RA_ScanPartyAuras(unit)
	local name = UnitName(unit);
	if ( not name ) then
		return;
	end
	local id = string.gsub(unit, "^raid(%d+)$", "%1");
	local stats = CT_RA_Stats[name];
	if ( not stats ) then
		CT_RA_Stats[name] = {
			["Buffs"] = { },
			["Debuffs"] = { },
			["Position"] = { }
		};
		stats = CT_RA_Stats[name];
		CT_RA_ScanUnitBuffs(unit, name, id);
		CT_RA_ScanUnitDebuffs(unit, name, id);
		CT_RA_UpdateUnitBuffs(CT_RA_Stats[name]["Buffs"], CT_RA_UnitIDFrameMap["raid"..id], name);
	else
		local stats = CT_RA_Stats[name];
		local frame = CT_RA_UnitIDFrameMap["raid"..id];
		local num, numDebuffs, debuff = 0, 0, UnitDebuff(unit, 1);
		numDebuffs, debuff = 0, UnitDebuff(unit, 1);
		while ( debuff ) do
			numDebuffs = numDebuffs + 1;
			debuff = UnitDebuff(unit, numDebuffs+1);
		end
		if ( stats["Debuffs"].n ~= numDebuffs ) then 
			-- Debuffs
			CT_RA_ScanUnitDebuffs(unit, name, id);
		else
			CT_RA_ScanUnitBuffs(unit, name, id);
			local isFD = CT_RA_CheckFD(name, "raid" .. id);
			if ( isFD > 0 ) then
				CT_RA_UpdateUnitDead(frame);
			end
		end
		CT_RA_UpdateUnitBuffs(stats["Buffs"], frame, name);
	end
end

function CT_RA_CheckFD(name, unit)
	local class = UnitClass(unit);
	if ( class ~= CT_RA_HUNTER and class ~= CT_RA_PRIEST ) then
		return 0;
	end
	local hasFD = 0;
	local num, buff, _, texture = 0, UnitBuff(unit, 1);
	while ( buff ) do
		if ( texture == "Interface\\Icons\\Ability_Rogue_FeignDeath" ) then
			hasFD = 1;
			break;
		elseif ( texture == "Interface\\Icons\\Spell_Holy_GreaterHeal" ) then
			hasFD = 2;
			break;
		end
		num = num + 1;
		buff, _, texture = UnitBuff(unit, num+1);
	end
	return hasFD;
end

function CT_RA_ScanUnitBuffs(unit, name, id)
	local tempOptions = CT_RAMenu_Options["temp"];
	local oldAuras = { };
	local stats = CT_RA_Stats[name]["Buffs"];
	for k, v in pairs(stats) do
		if ( k ~= "n" ) then
			oldAuras[k] = 1;
		end
	end
	stats.n = 0;
	local num, buffName, _, buff = 0, UnitBuff(unit, 1);
	local duplicateTextures = {
		["Interface\\Icons\\Spell_Nature_Regeneration"] = true,
		["Interface\\Icons\\Spell_Nature_LightningShield"] = true
	};
	while ( buff ) do
		num = num + 1;
		local buffT = CT_RA_BuffTextures[buffName];
		if ( buffT and not stats[buffName] ) then
			stats[buffName] = { buff, buffT[2] };
			if ( UnitIsUnit(unit, "player") ) then
				CT_RA_BuffTimeLeft[buffName] = buffT[2];
			end
		end
		stats.n = stats.n + 1;
		oldAuras[buffName] = nil;
		buffName, _, buff = UnitBuff(unit, num+1);
	end
	for k, v in pairs(oldAuras) do
		stats[k] = nil;
		local buffTbl;
		for key, val in pairs(tempOptions["BuffArray"]) do
			if ( k == val["name"] ) then
				buffTbl = val;
				break;
			end
		end
		if ( buffTbl ) then
			local uId = "raid" .. id;
			if ( not UnitIsDead(uId) and UnitIsVisible(uId) and not tempOptions["NotifyDebuffs"]["hidebuffs"] and k ~= CT_RA_POWERWORDSHIELD and k ~= CT_RA_ADMIRALSHAT ) then
				if ( buffTbl["show"] ~= -1 ) then
					local currPos = CT_RA_CurrPositions[name];
					if ( currPos ) then
						if ( tempOptions["NotifyDebuffs"][currPos[1]] and tempOptions["NotifyDebuffsClass"][CT_RA_ClassPositions[UnitClass("raid" .. currPos[2])]] ) then
							if ( CT_RA_ClassSpells and CT_RA_ClassSpells[k] ) then
								CT_RA_Print("<공격대 도우미> '|c00FFFFFF" .. name .. "|r'님에게서 '|c00FFFFFF" .. k .. "|r' 버프가 사라졌습니다.", 1, 0.5, 0);
							end
						end
					end
				end
			end
		end
	end
end

function CT_RA_ScanUnitDebuffs(unit, name, id)
	local tempOptions = CT_RAMenu_Options["temp"];
	local oldAuras = { };
	local stats = CT_RA_Stats[name]["Debuffs"];
	for k, v in pairs(stats) do
		if ( k ~= "n" ) then
			oldAuras[k] = 1;
		end
	end
	stats.n = 0;
	local num, debuffName, _, debuff, applications, dType = 0, UnitDebuff(unit, 1);
	while ( debuff ) do
		stats.n = stats.n + 1;
		num = num + 1;
		oldAuras[debuffName] = nil;
		if ( not stats[debuffName] ) then
			if ( debuffName == CT_RA_WEAKENEDSOUL ) then
				dType = CT_RA_WEAKENEDSOUL;
			elseif ( debuffName == CT_RA_RECENTLYBANDAGED ) then
				dType = CT_RA_RECENTLYBANDAGED;
			end
			local debuffType;
			for k, v in pairs(tempOptions["DebuffColors"]) do
				if ( dType == v["type"] ) then
					debuffType = v;
					break;
				end
			end
			if ( debuffType ) then
				local uId = "raid" .. id;
				stats[debuffName] = { dType, 0, gsub(debuff, "^Interface\\Icons\\(.+)$", "%1") };
				if ( CastParty_AddDebuff ) then
					CastParty_AddDebuff(uId, dType);
				end
				if ( tempOptions["NotifyDebuffs"]["main"] and debuffName ~= CT_RA_RECENTLYBANDAGED and debuffName ~= CT_RA_MINDVISION and debuffType["id"] ~= -1 ) then
					local currPos = CT_RA_CurrPositions[name];
					if ( currPos ) then
						if ( tempOptions["NotifyDebuffs"][currPos[1]] and tempOptions["NotifyDebuffsClass"][CT_RA_ClassPositions[UnitClass(uId)]] ) then
							CT_RA_AddToQueue(dType, uId);
							CT_RA_AddDebuffMessage(debuffName, dType, name);
						end
					end
				end
			end
		end
		debuffName, _, debuff, applications, dType = UnitDebuff(unit, num+1);
	end
	for k, v in pairs(oldAuras) do
		stats[k] = nil;
	end
end

function CT_RA_ShowHideDebuffs()
	local tempOptions = CT_RAMenu_Options["temp"];
	tempOptions["ShowDebuffs"] = not tempOptions["ShowDebuffs"];
	if ( tempOptions["ShowDebuffs"] ) then
		UIDropDownMenu_SetSelectedID(CT_RAMenuFrameBuffsBuffsDropDown, 2);
		CT_RAMenuFrameBuffsBuffsDropDownText:SetText("디버프 표시");
	elseif ( tempOptions["ShowBuffsDebuffed"] ) then
		UIDropDownMenu_SetSelectedID(CT_RAMenuFrameBuffsBuffsDropDown, 3);
		CT_RAMenuFrameBuffsBuffsDropDownText:SetText("디버프 걸리기 전까지 버프 표시");
	else
		UIDropDownMenu_SetSelectedID(CT_RAMenuFrameBuffsBuffsDropDown, 1);
		CT_RAMenuFrameBuffsBuffsDropDownText:SetText("버프 표시");
	end
	CT_RA_UpdateRaidGroup(2);
end

-- Thanks to Darco for the idea & some of the code
CT_RA_OldChatFrame_MessageEventHandler = ChatFrame_MessageEventHandler;
function CT_RA_NewChatFrame_MessageEventHandler(event)
--[[	if ( event == "CHAT_MSG_SYSTEM" ) then
		local iStart, iEnd, sName, iID, iDays, iHours, iMins, iSecs = string.find(arg1, "(.+) %(ID=(%w+)%): (%d+)d (%d+)h (%d+)m (%d+)s");
		if ( sName ) then
			local table = date("*t");
			table["sec"] = table["sec"] + (tonumber(iDays) * 86400) + (tonumber(iHours) * 3600) + (tonumber(iMins) * 60) + iSecs;
			local timetext = date("%A %b %d, %I:%M%p", time(table));
			local iStart1, iEnd1, iWeekday, iMonth, iDay, iHour, iMin, iAMPM = string.find(timetext, "(.+) (.+) (%d+), (%d+):(%d+)(%w+)");
			local Weekdays_Table = {
						Monday = "월요일",
						Tuesday = "화요일",
						Wednesday = "수요일",
						Thursday = "목요일",
						Friday = "금요일",
						Saturday = "토요일",
						Sunday = "일요일",
						};
			local Months_Table = {
						Jan = "1월",
						Feb = "2월",
						Mar = "3월",
						Apr = "4월",
						May = "5월",
						Jun = "6월",
						Jul = "7월",
						Aug = "8월",
						Sep = "9월",
						Oct = "10월",
						Nov = "11월",
						Dec = "12월",
						};
			local AMPM_Table = { AM = "오전", PM = "오후" };
			local iTime = iHour..":"..iMin;
			if ( iWeekday and iMonth and iAMPM ) then
				iWeekday = Weekdays_Table[iWeekday];
				iMonth = Months_Table[iMonth];
				iAMPM = AMPM_Table[iAMPM];
			end
			iDay = tonumber(iDay);
			--arg1 = arg1 .. " ("..date("%A %b %d, %I:%M%p", time(table)) .. ")";
			arg1 = arg1 .. " ("..iMonth.." "..iDay.."일 "..iWeekday..", ".. iAMPM .. " ".. iTime .. ")";
		end]]
	if ( event == "CHAT_MSG_WHISPER_INFORM" ) then
		if ( arg1 == "<공격대 도우미> 이미 파티에 속해 있습니다." or string.find(arg1, "<공격대 도우미> 공격대에 정숙 모드가 작동했습니다%. 정숙해주세요%. %d+ 초 남았습니다%.") ) then
			return;
		end
	end
	CT_RA_OldChatFrame_MessageEventHandler(event);
end

ChatFrame_MessageEventHandler = CT_RA_NewChatFrame_MessageEventHandler;

local oldDialogs = { };
oldDialogs["RESURRECTSHOW"] = StaticPopupDialogs["RESURRECT"].OnShow;
oldDialogs["RESURRECT_NO_SICKNESSSHOW"] = StaticPopupDialogs["RESURRECT_NO_SICKNESS"].OnShow;
oldDialogs["RESURRECT_NO_TIMERSHOW"] = StaticPopupDialogs["RESURRECT_NO_TIMER"].OnShow;
oldDialogs["DEATHSHOW"] = StaticPopupDialogs["DEATH"].OnShow;

StaticPopupDialogs["RESURRECT"].OnShow = function() oldDialogs["RESURRECTSHOW"]() CT_RA_AddMessage("RESSED") end;
StaticPopupDialogs["RESURRECT_NO_SICKNESS"].OnShow = function() oldDialogs["RESURRECT_NO_SICKNESSSHOW"]() CT_RA_AddMessage("RESSED") end;
StaticPopupDialogs["RESURRECT_NO_TIMER"].OnShow = function() oldDialogs["RESURRECT_NO_TIMERSHOW"]() CT_RA_AddMessage("RESSED") end;
StaticPopupDialogs["RESURRECT"].OnHide = function() CT_RA_AddMessage("NORESSED") end;
StaticPopupDialogs["RESURRECT_NO_SICKNESS"].OnHide = function() CT_RA_AddMessage("NORESSED") end;
StaticPopupDialogs["RESURRECT_NO_TIMER"].OnHide = function() if ( not StaticPopup_FindVisible("DEATH") ) then CT_RA_AddMessage("NORESSED") end end;
StaticPopupDialogs["DEATH"].OnShow = function() oldDialogs["DEATHSHOW"]() if ( HasSoulstone() ) then CT_RA_AddMessage("CANRES") end end;

CT_RA_OldStaticPopup_OnShow = StaticPopup_OnShow;
function CT_RA_NewStaticPopup_OnShow()
	if ( this.which and strsub(this.which, 1, 9) == "RESURRECT" ) then
		CT_RA_AddMessage("RESSED");
	end
	CT_RA_OldStaticPopup_OnShow();
end
StaticPopup_OnShow = CT_RA_NewStaticPopup_OnShow;

function CT_RA_ResFrame_DropDown_OnClick()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( this:GetID() == 2 ) then
		tempOptions["LockMonitor"] = not tempOptions["LockMonitor"];
	elseif ( this:GetID() == 4 ) then
		tempOptions["ShowMonitor"] = nil;
		CT_RA_ResFrame:Hide();
	end
end

function CT_RA_ResFrame_InitButtons()
	local tempOptions = CT_RAMenu_Options["temp"];
	local dropdown, info;
	if ( UIDROPDOWNMENU_OPEN_MENU ) then
		dropdown = getglobal(UIDROPDOWNMENU_OPEN_MENU);
	else
		dropdown = this;
	end

	info = {};
	info.text = "부활 현황";
	info.isTitle = 1;
	info.justifyH = "CENTER";
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	info = { };
	if ( tempOptions["LockMonitor"] ) then
		info.text = "창 위치 잠금해제";
	else
		info.text = "창 위치 잠금";
	end
	info.notCheckable = 1;
	info.func = CT_RA_ResFrame_DropDown_OnClick;
	UIDropDownMenu_AddButton(info);

	info = { };
	info.text = "배경색 설정";
	info.hasColorSwatch = 1;
	info.hasOpacity = 1;
	if ( tempOptions["RMBG"] ) then
		info.r = ( tempOptions["RMBG"].r );
		info.g = ( tempOptions["RMBG"].g );
		info.b = ( tempOptions["RMBG"].b );
		info.opacity = ( tempOptions["RMBG"].a );
	else
		info.r = 0;
		info.g = 0;
		info.b = 1;
		info.opacity = 0.5;
	end
	info.notClickable = 1;
	info.swatchFunc = CT_RA_ResFrame_DropDown_SwatchFunc;
	info.opacityFunc = CT_RA_ResFrame_DropDown_OpacityFunc;
	info.cancelFunc = CT_RA_ResFrame_DropDown_CancelFunc;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	info = { };
	info.text = "창 숨김";
	info.notCheckable = 1;
	info.func = CT_RA_ResFrame_DropDown_OnClick;
	UIDropDownMenu_AddButton(info);

end

function CT_RA_ResFrame_DropDown_SwatchFunc()
	local tempOptions = CT_RAMenu_Options["temp"];
	local r, g, b = ColorPickerFrame:GetColorRGB();
	if ( not tempOptions["RMBG"] ) then
		tempOptions["RMBG"] = { ["r"] = r, ["g"] = g, ["b"] = b, ["a"] = 0 };
	else
		tempOptions["RMBG"]["r"] = r;
		tempOptions["RMBG"]["g"] = g;
		tempOptions["RMBG"]["b"] = b;
	end
	CT_RA_ResFrame:SetBackdropColor(r, g, b, tempOptions["RMBG"]["a"]);
	CT_RA_ResFrame:SetBackdropBorderColor(1, 1, 1, tempOptions["RMBG"]["a"]);
end

function CT_RA_ResFrame_DropDown_OpacityFunc()
	local tempOptions = CT_RAMenu_Options["temp"];
	local r, g, b = 1, 1, 1;
	if ( tempOptions["RMBG"] ) then
		r, g, b = tempOptions["RMBG"].r, tempOptions["RMBG"].g, tempOptions["RMBG"].b;
	end
	local a = OpacitySliderFrame:GetValue();
	tempOptions["RMBG"]["a"] = a;
	CT_RA_ResFrame:SetBackdropColor(r, g, b, a);
	CT_RA_ResFrame:SetBackdropBorderColor(1, 1, 1, a);
end

function CT_RA_ResFrame_DropDown_CancelFunc(val)
	local tempOptions = CT_RAMenu_Options["temp"];
	tempOptions["RMBG"] = { 
		["r"] = val.r,
		["g"] = val.g,
		["b"] = val.b,
		["a"] = val.opacity
	};
	CT_RA_ResFrame:SetBackdropColor(val.r, val.g, val.b, val.opacity);
	CT_RA_ResFrame:SetBackdropBorderColor(1, 1, 1, val.opacity);
end

function CT_RA_ResFrame_OnLoad()
	UIDropDownMenu_Initialize(this, CT_RA_ResFrame_InitButtons, "MENU");
end

function CT_RA_SendReady()
	CT_RA_AddMessage("READY");
end

function CT_RA_SendNotReady()
	CT_RA_AddMessage("NOTREADY");
end

function CT_RA_SendYes()
	CT_RA_AddMessage("VOTEYES");
end

function CT_RA_SendNo()
	CT_RA_AddMessage("VOTENO");
end

function CT_RA_SendRly()
	CT_RA_AddMessage("YARLY");
end

function CT_RA_SendNoRly()
	CT_RA_AddMessage("NORLY");
end

function CT_RA_ReadyFrame_OnUpdate(elapsed)
	if ( this.hide ) then
		this.hide = this.hide - elapsed;
		if ( this.hide <= 0 ) then
			this:Hide();
		end
	end
end

function CT_RA_ToggleGroupSort()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( tempOptions["SORTTYPE"] == "group" ) then
		CT_RA_SetSortType("class");
	else
		CT_RA_SetSortType("group");
	end

	CT_RA_UpdateRaidGroup(0);
	CT_RA_UpdateRaidFrameOptions();
	CT_RAOptions_UpdateGroups();
end

function CT_RA_SetSortType(sort_type)
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( sort_type == "class" ) then
		tempOptions["SORTTYPE"] = "class";
		if ( CT_RAMenuFrameGeneralMiscDropDown and CT_RAMenuFrame:IsVisible() ) then
			UIDropDownMenu_SetSelectedID(CT_RAMenuFrameGeneralMiscDropDown, 2);
		end
		if ( CT_RAMenuFrameGeneralMiscDropDownText ) then
			CT_RAMenuFrameGeneralMiscDropDownText:SetText("직업");
		end
	else
		tempOptions["SORTTYPE"] = "group";
		if ( CT_RAMenuFrameGeneralMiscDropDown and CT_RAMenuFrame:IsVisible() ) then
			UIDropDownMenu_SetSelectedID(CT_RAMenuFrameGeneralMiscDropDown, 1);
		end
		if ( CT_RAMenuFrameGeneralMiscDropDownText ) then
			CT_RAMenuFrameGeneralMiscDropDownText:SetText("파티");
		end
	end
end

function CT_RA_DragAllWindows(start)
	local id = tonumber(this:GetName():match("(%d+)$"));
	if ( start ) then
		local group = getglobal("CT_RAGroupDrag" .. id);
		local x, y = group:GetLeft(), group:GetTop();

		if ( not x or not y ) then
			return;
		end
		for i = 1, 8, 1 do
			if ( i ~= id ) then
				local oGroup = getglobal("CT_RAGroupDrag" .. i);
				local oX, oY = oGroup:GetLeft(), oGroup:GetTop();
				if ( oX and oY ) then
					oGroup:ClearAllPoints();
					oGroup:SetPoint("TOPLEFT", "CT_RAGroupDrag" .. id, "TOPLEFT", oX-x, oY-y);
				end
			end
		end
	else
		for i = 1, 8, 1 do
			if ( i ~= id ) then
				local oGroup = getglobal("CT_RAGroupDrag" .. i);
				local oX, oY = oGroup:GetLeft(), oGroup:GetTop();
				if ( oX and oY ) then
					oGroup:ClearAllPoints();
					oGroup:SetPoint("TOPLEFT", "UIParent", "TOPLEFT", oX, oY-UIParent:GetTop());
				end
			end
		end
	end
end

function CT_RA_CheckGroups()
	if ( GetNumRaidMembers() == 0 ) then
		return;
	end
	local numPartyMembers = GetNumPartyMembers();
	if ( not CT_RA_PartyMembers ) then
		CT_RA_PartyMembers = { };
		if ( UnitName("party" .. numPartyMembers) ) then
			for i = 1, numPartyMembers, 1 do
				CT_RA_PartyMembers[UnitName("party"..i)] = i;
			end
		end
		return;
	end
	local joined, left, numleft, numjoin = "", "", 0, 0;
	if ( not UnitName("party" .. numPartyMembers) and numPartyMembers > 0 ) then
		CT_RA_PartyMembers = { };
		return;
	end
	for i = 1, numPartyMembers, 1 do
		local uName = UnitName("party" .. i);
		if ( uName and not CT_RA_PartyMembers[uName] ) then
			if ( numjoin > 0 ) then
				joined = joined .. "|r, |c00FFFFFF";
			end
			joined = joined .. uName;
			numjoin = numjoin + 1;
		end
		CT_RA_PartyMembers[uName] = nil;
	end

	for k, v in pairs(CT_RA_PartyMembers) do
		if ( numleft > 0 ) then
			left = left .. "|r, |c00FFFFFF";
		end
		left = left .. k;
		numleft = numleft + 1;
	end
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( tempOptions["NotifyGroupChange"] and ( numjoin > 0 or numleft > 0 ) ) then
		if ( tempOptions["NotifyGroupChangeSound"] ) then
			PlaySoundFile("Sound\\Spells\\Thorns.wav");
		end
		if ( numjoin >= 1 ) then
			CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. joined .. "|r님께서 파티에 들어오셨습니다.", 1, 0.5, 0);
		end
		if ( numleft >= 1 ) then
			CT_RA_Print("<공격대 도우미> |c00FFFFFF" .. left .. "|r님께서 파티를 나가셨습니다.", 1, 0.5, 0);
		end
	end
	CT_RA_PartyMembers = { };
	for i = 1, numPartyMembers, 1 do
		local uName = UnitName("party" .. i);
		if ( uName ) then
			CT_RA_PartyMembers[uName] = 1;
		end
	end
end

local classSpells = { [CT_RA_DRUID] = "치유의 손길", [CT_RA_PRIEST] = "상급 치유", 
	[CT_RA_PALADIN] = "성스러운 빛", [CT_RA_SHAMAN] = "치유의 물결" };
function CT_RA_Emergency_UpdateHealth()
	local tempOptions = CT_RAMenu_Options["temp"];
	local numRaidMembers = GetNumRaidMembers();
	if ( not tempOptions["ShowEmergency"] or ( numRaidMembers == 0 and not tempOptions["ShowEmergencyOutsideRaid"] ) ) then
		CT_RA_EmergencyFrame:Hide();
		CT_RA_EmergencyFrameDrag:Hide();
		return;
	else
		CT_RA_EmergencyFrame:Show();
	end
	for i = 1, 5, 1 do
		CT_RA_EmergencyFrame["frame"..i]:Hide();
	end
	CT_RA_EmergencyFrame.maxPercent = nil;
	local healthThreshold = tempOptions["EMThreshold"];
	if ( not healthThreshold ) then
		healthThreshold = 0.9;
	end
	CT_RA_Emergency_Units = { };
	local health;
	if ( not tempOptions["ShowEmergencyParty"] and GetNumRaidMembers() > 0 ) then
		health = CT_RA_Emergency_RaidHealth;
		health = { };
		local numMembers = GetNumRaidMembers();
		for i = 1, numMembers, 1 do
			local uId = "raid" .. i;
			local curr, max = UnitHealth(uId), UnitHealthMax(uId);
			if ( curr and max and curr/max <= healthThreshold ) then
				tinsert(health, { curr, max, uId, i, curr/max });
			end
		end
	else
		health = { };
		for i = 1, GetNumPartyMembers(), 1 do
			local uId = "party" .. i;
			local curr, max = UnitHealth(uId), UnitHealthMax(uId);
			if ( curr and max and curr/max <= healthThreshold) then
				tinsert(health, { curr, max, uId, nil, curr/max });
			end
		end
		local curr, max = UnitHealth("player"), UnitHealthMax("player");
		if ( curr/max <= healthThreshold ) then
			tinsert(health, { curr, max, "player", nil, curr/max });
		end
	end
	
	table.sort(
		health, 
		function(v1, v2)
			return v1[5] < v2[5];
		end
	);
	CT_RA_EmergencyFrameTitle:Show();
	CT_RA_EmergencyFrameDrag:Show();
	local nextFrame = 0;
	
	local checkRangeFunc = tempOptions["ShowEmergencyRange"] and classSpells[UnitClass("player")];
	
	for k, v in pairs(health) do
		if ( not UnitIsDead(v[3]) and not UnitIsGhost(v[3]) and UnitIsConnected(v[3]) and 
			UnitIsVisible(v[3]) and ( not CT_RA_Stats[UnitName(v[3])] or not CT_RA_Stats[UnitName(v[3])]["Dead"] ) and
			( not tempOptions["EMClasses"] or not tempOptions["EMClasses"][UnitClass(v[3])] ) 
			and ( not checkRangeFunc or IsSpellInRange(checkRangeFunc, v[3]) ~= 0 ) ) then
			local name, rank, subgroup, level, class, fileName;
			local obj = CT_RA_EmergencyFrame["frame" .. (nextFrame+1)];
			if ( GetNumRaidMembers() > 0 and not tempOptions["ShowEmergencyParty"] and v[4] ) then
				name, rank, subgroup, level, class, fileName = GetRaidRosterInfo(v[4]);
				local colors = RAID_CLASS_COLORS[fileName];
				if ( colors ) then
					obj.Name:SetTextColor(colors.r, colors.g, colors.b);
				end
			else
				obj.Name:SetTextColor(1, 1, 1);
			end
			if ( not subgroup or not tempOptions["EMGroups"] or not tempOptions["EMGroups"][subgroup] ) then
				nextFrame = nextFrame + 1;
				obj:Show();
				CT_RA_EmergencyFrame.maxPercent = v[5];
				CT_RA_Emergency_Units[UnitName(v[3])] = 1;
				obj.ClickFrame.unitid = v[3];
				obj.HPBar:SetMinMaxValues(0, v[2]);
				obj.HPBar:SetValue(v[1]);
				obj.Name:SetText(UnitName(v[3]));
				obj.Deficit:SetText(v[1]-v[2]);
				
				if ( UnitIsUnit(v[3], "player") ) then
					obj.HPBar:SetStatusBarColor(1, 0, 0);
					obj.HPBG:SetVertexColor(1, 0, 0, tempOptions["BGOpacity"]);
				elseif ( UnitInParty(v[3]) ) then
					obj.HPBar:SetStatusBarColor(0, 1, 1);
					obj.HPBG:SetVertexColor(0, 1, 1, tempOptions["BGOpacity"]);
				else
					obj.HPBar:SetStatusBarColor(0, 1, 0);
					obj.HPBG:SetVertexColor(0, 1, 0, tempOptions["BGOpacity"]);
				end
			end
		end
		if ( nextFrame == 5 ) then
			break;
		end
	end
end

function CT_RA_Emergency_OnEnter()
	if ( SpellIsTargeting() ) then
		SetCursor("CAST_CURSOR");
	elseif ( not SpellCanTargetUnit(this.unitid) and SpellIsTargeting() ) then
		SetCursor("CAST_ERROR_CURSOR");
	end
end

function CT_RA_Emergency_OnUpdate(elapsed)
	this.update = this.update - elapsed;
	if ( this.update <= 0 ) then
		this.update = 0.1;
		if ( this.cursor ) then
			if ( SpellIsTargeting() and SpellCanTargetUnit(this.unitid) ) then
				SetCursor("CAST_CURSOR");
			elseif ( SpellIsTargeting() ) then
				SetCursor("CAST_ERROR_CURSOR");
			end
		end
	end
end

function CT_RA_Emergency_DropDown_OnLoad()
	UIDropDownMenu_Initialize(this, CT_RA_Emergency_DropDown_Initialize, "MENU");
end

function CT_RA_Emergency_DropDown_Initialize()
	local tempOptions = CT_RAMenu_Options["temp"];
	local dropdown, info;
	if ( UIDROPDOWNMENU_OPEN_MENU ) then
		dropdown = getglobal(UIDROPDOWNMENU_OPEN_MENU);
	else
		dropdown = this;
	end
	if ( UIDROPDOWNMENU_MENU_VALUE == "직업 단위로 표시" ) then
		info = {};
		info.text = "직업 단위로 표시";
		info.isTitle = 1;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
		for k, v in pairs(CT_RA_ClassPositions) do
			if ( ( k ~= CT_RA_SHAMAN or ( UnitFactionGroup("player") and UnitFactionGroup("player") == "Horde" ) ) and ( k ~= CT_RA_PALADIN or ( UnitFactionGroup("player") and UnitFactionGroup("player") == "Alliance" ) ) ) then
				info = {};
				info.text = k;
				info.value = k;
				info.func = CT_RA_Emergency_DropDown_OnClick;
				info.checked = ( not tempOptions["EMClasses"] or not tempOptions["EMClasses"][k] );
				info.keepShownOnClick = 1;
				info.tooltipTitle = "표시할 직업군 선택";
				info.tooltipText = "해당 직업에 대한 응급 현황을 표시하거나 표시하지 않습니다.";
				UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
			end
		end
		return;
	end

	if ( UIDROPDOWNMENU_MENU_VALUE == "파티 단위로 표시" ) then
		info = {};
		info.text = "파티 단위로 표시";
		info.isTitle = 1;
		info.notCheckable = 1;
		UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
		for i = 1, 8, 1 do
			info = {};
			info.text = "파티 " .. i;
			info.value = i;
			info.func = CT_RA_Emergency_DropDown_OnClick;
			info.checked = ( not tempOptions["EMGroups"] or not tempOptions["EMGroups"][i] );
			info.keepShownOnClick = 1;
			info.tooltipTitle = "표시할 파티 선택";
			info.tooltipText = "응급 현황이 표시될 파티를 선택합니다.";
			UIDropDownMenu_AddButton(info, UIDROPDOWNMENU_MENU_LEVEL);
		end
		return;
	end
	info = {};
	info.text = "응급 현황";
	info.isTitle = 1;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);
	
	info = {};
	info.text = "직업 단위로 표시";
	info.hasArrow = 1;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);
	
	info = {};
	info.text = "파티 단위로 표시";
	info.hasArrow = 1;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);

	info = { };
	info.text = "배경색 설정";
	info.hasColorSwatch = 1;
	info.hasOpacity = 1;
	if ( tempOptions["EMBG"] ) then
		info.r = ( tempOptions["EMBG"].r );
		info.g = ( tempOptions["EMBG"].g );
		info.b = ( tempOptions["EMBG"].b );
		info.opacity = ( tempOptions["EMBG"].a );
	else
		info.r = 0;
		info.g = 0;
		info.b = 1;
		info.opacity = 0;
	end
	info.notClickable = 1;
	info.swatchFunc = CT_RA_Emergency_DropDown_SwatchFunc;
	info.opacityFunc = CT_RA_Emergency_DropDown_OpacityFunc;
	info.cancelFunc = CT_RA_Emergency_DropDown_CancelFunc;
	info.notCheckable = 1;
	UIDropDownMenu_AddButton(info);
end

function CT_RA_Emergency_DropDown_SwatchFunc()
	local tempOptions = CT_RAMenu_Options["temp"];
	local r, g, b = ColorPickerFrame:GetColorRGB();
	if ( not tempOptions["EMBG"] ) then
		tempOptions["EMBG"] = { ["r"] = r, ["g"] = g, ["b"] = b, ["a"] = 0 };
	else
		tempOptions["EMBG"]["r"] = r;
		tempOptions["EMBG"]["g"] = g;
		tempOptions["EMBG"]["b"] = b;
	end
	CT_RA_EmergencyFrame:SetBackdropColor(r, g, b, tempOptions["EMBG"]["a"]);
	CT_RA_EmergencyFrame:SetBackdropBorderColor(1, 1, 1, tempOptions["EMBG"]["a"]);
end

function CT_RA_Emergency_DropDown_OpacityFunc()
	local tempOptions = CT_RAMenu_Options["temp"];
	local r, g, b = 1, 1, 1;
	if ( tempOptions["EMBG"] ) then
		r, g, b = tempOptions["EMBG"].r, tempOptions["EMBG"].g, tempOptions["EMBG"].b;
	end
	local a = OpacitySliderFrame:GetValue();
	tempOptions["EMBG"]["a"] = a;
	CT_RA_EmergencyFrame:SetBackdropColor(r, g, b, a);
	CT_RA_EmergencyFrame:SetBackdropBorderColor(1, 1, 1, a);
end

function CT_RA_Emergency_DropDown_CancelFunc(val)
	local tempOptions = CT_RAMenu_Options["temp"];
	tempOptions["EMBG"] = { 
		["r"] = val.r,
		["g"] = val.g,
		["b"] = val.b,
		["a"] = val.opacity
	};
	CT_RA_EmergencyFrame:SetBackdropColor(val.r, val.g, val.b, val.opacity);
	CT_RA_EmergencyFrame:SetBackdropBorderColor(1, 1, 1, val.opacity);
end

function CT_RA_Emergency_DropDown_OnClick()
	local tempOptions = CT_RAMenu_Options["temp"];
	if ( UIDROPDOWNMENU_MENU_VALUE == "직업 단위로 표시" ) then
		if ( not tempOptions["EMClasses"] ) then
			tempOptions["EMClasses"] = { };
		end
		tempOptions["EMClasses"][this.value] = not tempOptions["EMClasses"][this.value];
		CT_RA_Emergency_UpdateHealth();
	elseif ( UIDROPDOWNMENU_MENU_VALUE == "파티 단위로 표시" ) then
		if ( not tempOptions["EMGroups"] ) then
			tempOptions["EMGroups"] = { };
		end
		tempOptions["EMGroups"][this.value] = not tempOptions["EMGroups"][this.value];
		CT_RA_Emergency_UpdateHealth();	
	end
end

function CT_RA_Emergency_ToggleDropDown()
	local left, top = this:GetCenter();
	local uileft, uitop = UIParent:GetCenter();
	if ( left > uileft ) then
		CT_RA_EmergencyFrameDropDown.point = "TOPRIGHT";
		CT_RA_EmergencyFrameDropDown.relativePoint = "BOTTOMLEFT";
	else
		CT_RA_EmergencyFrameDropDown.point = "TOPLEFT";
		CT_RA_EmergencyFrameDropDown.relativePoint = "BOTTOMRIGHT";
	end
	CT_RA_EmergencyFrameDropDown.relativeTo = this:GetName();
	ToggleDropDownMenu(1, nil, CT_RA_EmergencyFrameDropDown);
end

-- RADurability stuff
function CT_RADurability_GetDurability()
	local currDur, maxDur, brokenItems = 0, 0, 0;
	local itemIds = {
		1, 2, 3, 5, 6, 7, 8, 9, 10, 16, 17, 18
	};
	for k, v in pairs(itemIds) do
		CT_RADurationTooltip:ClearLines();
		CT_RADurationTooltip:SetInventoryItem("player", v);
		for i = 1, CT_RADurationTooltip:NumLines(), 1 do
			local useless, useless, sMin, sMax = string.find(getglobal("CT_RADurationTooltipTextLeft" .. i):GetText() or "", CT_RA_DURABILITY);
			if ( sMin and sMax ) then
				local iMin, iMax = tonumber(sMin), tonumber(sMax);
				if ( iMin == 0 ) then
					brokenItems = brokenItems + 1;
				end
				currDur = currDur + iMin;
				maxDur = maxDur + iMax;
				break;
			end
		end
	end
	return currDur, maxDur, brokenItems;
end

function CT_RAReagents_GetReagents()
	local numItems = 0;
	local classes = {
		[CT_RA_PRIEST] = {CT_REG_PRIEST , CT_REG_PRIEST_SPELL},
		[CT_RA_MAGE] = { CT_REG_MAGE , CT_REG_MAGE_SPELL },
		[CT_RA_DRUID] = { CT_REG_DRUID ,CT_REG_DRUID_SPELL },
		[CT_RA_WARLOCK] = { CT_REG_WARLOCK },
		[CT_RA_PALADIN] = { CT_REG_PALADIN, CT_REG_PALADIN_SPELL },
		[CT_RA_SHAMAN] = { CT_REG_SHAMAN, CT_REG_SHAMAN_SPELL }
	};
	local plClass = classes[UnitClass("player")];
	if ( not plClass or ( plClass[2] and not CT_RA_ClassSpells[plClass[2]] ) ) then
		return;
	end
	for i = 0, 4, 1 do
		for y = 1, MAX_CONTAINER_ITEMS, 1 do
			local link = GetContainerItemLink(i, y);
			if ( link ) then
				local _, _, name = string.find(link, "%[(.+)%]");
				if ( name ) then
					if ( plClass and plClass[1] == name ) then
						local texture, itemCount, locked, quality, readable = GetContainerItemInfo(i,y);
						numItems = numItems + itemCount;
					end
				end
			end
		end
	end
	return numItems;
end

function CT_RAItem_GetItems(itemName)
	local numItems = 0;
	for i = 0, 4, 1 do
		for y = 1, MAX_CONTAINER_ITEMS, 1 do
			local link = GetContainerItemLink(i, y);
			if ( link ) then
				local _, _, name = string.find(link, "%[(.+)%]");
				if ( name == itemName ) then
					local texture, itemCount, locked, quality, readable = GetContainerItemInfo(i,y);
					numItems = numItems + itemCount;
				end
			end
		end
	end
	return numItems;
end

CT_RADurability_Shown = { };
CT_RADurability_Sorting = {
	["curr"] = 4,
	[3] = { "a", "a" },
	[4] = { "a", "a" }
		};
tinsert(UISpecialFrames, "CT_RA_DurabilityFrame");


function CT_RADurability_Add(name, info, fileName, ...)
	local tbl = { name, info, fileName };
	for i = 1, select('#', ...), 1 do
		tinsert(tbl, ( tonumber((select(i, ...))) or select(i, ...) ));
	end
	tinsert(CT_RADurability_Shown, tbl);
	CT_RADurability_Sort(CT_RADurability_Sorting["curr"], 1);
	CT_RADurability_Update();
end

function CT_RADurability_Sort(sortBy, maintain)
	if ( CT_RADurability_Sorting["curr"] ~= sortBy ) then
		CT_RADurability_Sorting[sortBy][1] = CT_RADurability_Sorting[sortBy][2];
	end
	CT_RADurability_Sorting["curr"] = sortBy;
	if ( CT_RADurability_Sorting[sortBy][1] == "a" ) then
		if ( not maintain ) then
			CT_RADurability_Sorting[sortBy][1] = "b";
		end
	else
		if ( not maintain ) then
			CT_RADurability_Sorting[sortBy][1] = "a";
		end
	end
	if ( CT_RADurability_Sorting[sortBy][1] == "b" ) then
		table.sort(CT_RADurability_Shown,
			function(t1, t2)
				if (t1[sortBy] == t2[sortBy] ) then
					if ( t1[3] == t2[3] ) then
					return t1[1] < t2[1]
					else
						return t1[3] < t2[3]
					end
				else
					return t1[sortBy] < t2[sortBy]
				end
			end
		);
	else
		table.sort(CT_RADurability_Shown,
			function(t1, t2)
				if (t1[sortBy] == t2[sortBy] ) then
					if ( t1[3] == t2[3] ) then
						return t1[1] < t2[1]
					else
						return t1[3] < t2[3]
					end
				else
					return t1[sortBy] > t2[sortBy]
				end
			end
		);
	end
	CT_RADurability_Update();
end

function CT_RADurability_Update()
	local numEntries = #CT_RADurability_Shown;
	FauxScrollFrame_Update(CT_RA_DurabilityFrameScrollFrame, numEntries, 19, 20);

	for i = 1, 19, 1 do
		local button = getglobal("CT_RA_DurabilityFramePlayer" .. i);
		local index = i + FauxScrollFrame_GetOffset(CT_RA_DurabilityFrameScrollFrame);
		if ( index <= numEntries ) then
			if ( numEntries <= 19 ) then
				button:SetWidth(275);
			else
				button:SetWidth(253);
			end
			if ( CT_RA_DurabilityFrame.type ~= "RARST" or numEntries <= 19 ) then
				CT_RA_DurabilityFrameScrollFrame:SetPoint("TOPLEFT", "CT_RA_DurabilityFrame", "TOPLEFT", 19, -27);
				getglobal(button:GetName() .. "Resist1"):SetPoint("LEFT", button:GetName(), "LEFT", 127, 0);
				CT_RA_DurabilityFrameNameTab:SetWidth(135);
			else
				CT_RA_DurabilityFrameScrollFrame:SetPoint("TOPLEFT", "CT_RA_DurabilityFrame", "TOPLEFT", 19, -32);
				getglobal(button:GetName() .. "Resist1"):SetPoint("LEFT", button:GetName(), "LEFT", 110, 0);
				CT_RA_DurabilityFrameNameTab:SetWidth(118);
			end
			button:Show();
			getglobal(button:GetName() .. "Name"):SetText(CT_RADurability_Shown[index][1]);
			local color = RAID_CLASS_COLORS[CT_RADurability_Shown[index][3]];
			if ( color ) then
				getglobal(button:GetName() .. "Name"):SetTextColor(color.r, color.g, color.b);
			end
			getglobal(button:GetName() .. "Info"):SetText(CT_RADurability_Shown[index][2]);
			for i = 1, 5, 1 do
				if ( CT_RA_DurabilityFrame.type == "RARST" and CT_RADurability_Shown[index][3+i] ~= -1 ) then
					getglobal(button:GetName() .. "Resist" .. i):SetText(CT_RADurability_Shown[index][3+i]);
					getglobal(button:GetName() .. "Resist" .. i):Show();
				else
					getglobal(button:GetName() .. "Resist" .. i):Hide();
				end
			end
		else
			button:Hide();
		end
	end

end

CT_RA_CurrDebuffs = { };

function CT_RA_AddDebuffMessage(name, dType, player)
	if ( not dType ) then
		return;
	end
	if ( CT_RA_CurrDebuffs[name .. "@" .. dType] ) then
		if ( not CT_RA_CurrDebuffs[name .. "@" .. dType][3][player] ) then
			CT_RA_CurrDebuffs[name .. "@" .. dType][3][player] = 1;
			CT_RA_CurrDebuffs[name .. "@" .. dType][2] = CT_RA_CurrDebuffs[name .. "@" .. dType][2] + 1;
			CT_RA_CurrDebuffs[name .. "@" .. dType][1] = 0.4;
		end
	else
		CT_RA_CurrDebuffs[name .. "@" .. dType] = {
			0.4, 1, {
				[player] = 1
			},
			player
		};
	end
end

function CT_RA_RGBToHex(r, g, b)
	return format("%.2x%.2x%.2x", floor(r*255), floor(g*255), floor(b*255));
end