local IRF2 = CreateFrame("Frame", "InvenRaidFrames2")
IRF2:SetScript("OnEvent", function(self, event, ...) self[event](self, ...) end)
IRF2:RegisterEvent("ADDON_LOADED")

local _G = getfenv(0)
local type = _G.type
local pairs = _G.pairs
local unpack = _G.unpack
local select = _G.select
local tonumber = _G.tonumber
local tinsert = _G.table.insert
local CopyTable = _G.CopyTable
local GetTime = _G.GetTime
local GetNumRaidMembers = _G.GetNumRaidMembers
local GetNumPartyMembers = _G.GetNumPartyMembers
local InCombatLockdown = _G.InCombatLockdown
local UnitClass = _G.UnitClass
local UnitExists = _G.UnitExists
local GetRaidRosterInfo = _G.GetRaidRosterInfo
local IsPartyLeader = _G.IsPartyLeader
local IsRaidLeader = _G.IsRaidLeader
local IsRaidOfficer = _G.IsRaidOfficer
local InCombatLockdown = _G.InCombatLockdown
local IsAltKeyDown = _G.IsAltKeyDown
local IsMouseButtonDown = _G.IsMouseButtonDown
local GetSpellInfo = _G.GetSpellInfo
local UnitGUID = _G.UnitGUID
local UnitHasVehicleUI = _G.UnitHasVehicleUI

local Broker = LibStub("LibDataBroker-1.1")
local MapButton = LibStub("LibMapButton-1.1")

-- 기본 정보 저장 변수 선언
IRF2.version = GetAddOnMetadata("InvenRaidFrames2", "Version")
IRF2.units, IRF2.objects, IRF2.names, IRF2.guids  = {}, {}, {}, {}
IRF2.objectModules, IRF2.objectHandler = {}, {}
IRF2.auraHandler, IRF2.shownModules = {}, {}
IRF2.raidMembers = 0
IRF2.partyMembers = 0
local classEnglish = { ["전사"] = "WARRIOR", ["드루이드"] = "DRUID", ["성기사"] = "PALADIN", ["죽음의 기사"] = "DEATHKNIGHT", ["사제"] = "PRIEST", ["주술사"] = "SHAMAN", ["도적"] = "ROGUE", ["마법사"] = "MAGE", ["흑마법사"] = "WARLOCK", ["사냥꾼"] = "HUNTER", ["*"] = "9" }
local framePoints = { ["좌측"] = { "LEFT", 0, 0 }, ["상단"] = { "TOP", 0, 0 }, ["우측"] = { "RIGHT", 0, 0 }, ["하단"] = { "BOTTOM", 0, 0 }, ["좌측 상단"] = { "TOPLEFT", 0, 0 }, ["우측 상단"] = { "TOPRIGHT", 0, 0 }, ["중앙"] = { "CENTER", 0, 0 }, ["좌측 하단"] = { "BOTTOMLEFT", 0, 0 }, ["우측 하단"] = { "BOTTOMRIGHT", 0, 0 } }
local leaveCombat = {}
local callbackTimer = {}

function IRF2:ADDON_LOADED()
	self:UnregisterEvent("ADDON_LOADED")
	if self.numberVersion then return end
	-- 애드온 버전 정의
	local major, minor, subver = self.version:match("^(%d+).(%d+).(%d+)$")
	if not major then
		major, minor = self.version:match("^(%d+).(%d+)$")
	end
	self.numberVersion = tonumber(subver or 0) + tonumber(minor or 0) * 100 + tonumber(major or 0) * 10000
	self.newVersion = self.numberVersion
	self.playerFullName = UnitName("player")
	self.playerProfile = UnitName("player").." - "..GetRealmName()
	self.playerClass = select(2, UnitClass("player"))
	self:RegisterEvent("CHAT_MSG_ADDON")
	-- 설정값 정의
	self:InitDB()
	-- 슬래쉬 커맨드 등록
	SLASH_INVENRAIDFRAMES21 = "/irf"
	SLASH_INVENRAIDFRAMES22 = "/irf2"
	SLASH_INVENRAIDFRAMES23 = "/인벤레이드"
	SLASH_INVENRAIDFRAMES24 = "/인벤레이드프레임"
	SLASH_INVENRAIDFRAMES25 = "/invenraidframe"
	SLASH_INVENRAIDFRAMES26 = "/invenraidframes"
	SLASH_INVENRAIDFRAMES27 = "/ㅑㄱㄹ"
	SLASH_INVENRAIDFRAMES28 = "/ㅑㄱㄹ2"
	SlashCmdList["INVENRAIDFRAMES2"] = function() IRF2:OnClick() end
	-- 단축키 설정 이름 정의
	BINDING_HEADER_INVENRAIDFRAMES2 = "인벤 레이드 프레임 2"
	BINDING_NAME_INVENRAIDFRAMES2_TOGGLE = "설정창 열기/닫기"
	BINDING_NAME_INVENRAIDFRAMES2_USE = "사용/비사용"
	BINDING_NAME_INVENRAIDFRAMES2_DOREADYCHECK = "전투 준비 상황 확인"
	-- LDB 정의
	Broker:NewDataObject("InvenRaidFrames2", {
		type = "launcher",
		text = "IRF2",
		OnClick = function(_, button) IRF2:OnClick(button) end,
		icon = "Interface\\AddOns\\InvenRaidFrames2\\Texture\\Icon.tga",
		OnTooltipShow = function(tooltip)
			if not tooltip or not tooltip.AddLine then return end
			IRF2:OnTooltip(tooltip)
		end,
		OnLeave = GameTooltip_Hide,
	})
	-- 미니맵 버튼, 미니맵 메뉴 생성
	InvenRaidFrames2DB.minimapButton = InvenRaidFrames2DB.minimapButton or {}
	MapButton:CreateButton(self, "InvenRaidFrames2MapButton", "Interface\\AddOns\\InvenRaidFrames2\\Texture\\Icon.tga", 190, InvenRaidFrames2DB.minimapButton)
	self.mapButtonMenu = CreateFrame("Frame", "InvenRaidFrames2MapButtonMenu", InvenRaidFrames2MapButton, "UIDropDownMenuTemplate")
	self.mapButtonMenu:SetID(1)
	self.mapButtonMenu:Hide()
	self.mapButtonMenu:SetWidth(10)
	self.mapButtonMenu:SetHeight(10)
	UIDropDownMenu_Initialize(self.mapButtonMenu, function()
		local info = UIDropDownMenu_CreateInfo()
		info.text = "Inven Raid Frames 2"
		info.isTitle = 1
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "설정창 열기"
		info.func = function() IRF2:OnClick() end
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "사용하기"
		info.checked = function() return self.db.run end
		info.func = function()
			self.db.run = not self.db.run
			if self.db.run then
				self.db.use = self.db.shown
			else
				self.db.use = "사용 안함"
			end
			IRF2:Toggle()
			if IRF2.group.pet then
				IRF2:SetPetGroupOrder()
			end
		end
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "고정하기"
		info.checked = function() return self.db.lock end
		info.func = function() self.db.lock = not self.db.lock end
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "전투 준비 확인"
		info.disabled = not IRF2:IsReadyCheckable()
		info.func = function() DoReadyCheck() end
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "버전 확인"
		info.func = function() IRF2:VersionCheck() end
		UIDropDownMenu_AddButton(info, 1)
		info = UIDropDownMenu_CreateInfo()
		info.text = "닫기"
		info.func = function() UIDropDownMenu_ClearAll(IRF2.mapButtonMenu) end
		UIDropDownMenu_AddButton(info, 1)
	end, "MENU")
	-- 기본 앵거 생성
	self.anchor = CreateFrame("Frame", "InvenRaidFrames2MainFrame", UIParent)
	self.anchor:SetFrameStrata(self.db.strata)
	self.anchor:ClearAllPoints()
	if self.db.pos then
		self.anchor:SetPoint(self.db.pos[1], UIParent, self.db.pos[1], self.db.pos[2], self.db.pos[3])
	else
		self.anchor:SetPoint("CENTER", UIParent, "CENTER")
	end
	self.anchor:SetHeight(0.1)
	self.anchor:SetWidth(0.1)
	self.anchor:SetScale(self.db.layout.scale)
	self.anchor:EnableMouse(true)
	self.anchor:SetMovable(true)
	self.anchor:SetUserPlaced(nil)
	self.anchor:SetClampedToScreen(true)
	self:UpdateClampRectInsets()
	self.anchor:RegisterForDrag("LeftButton")
	self.anchor:SetScript("OnDragStart", function()
		if not IRF2.db.lock then
			IRF2.anchor.isMoving = true
			IRF2.anchor:StartMoving()
			IRF2.anchor.MouseUpCheck:Show()
		end
	end)
	self.anchor:SetScript("OnDragStop", function()
		if IRF2.anchor.isMoving then
			IRF2.anchor.MouseUpCheck:Hide()
			IRF2.anchor.isMoving = nil
			IRF2.anchor:StopMovingOrSizing()
			self.db.pos = self.db.pos or {}
			self.db.pos[1], _, _, self.db.pos[2], self.db.pos[3] = IRF2.anchor:GetPoint()
			IRF2.anchor:SetUserPlaced(nil)
		end
	end)
	self.anchor.MouseUpCheck = CreateFrame("Frame", nil, self.anchor)
	self.anchor.MouseUpCheck:Hide()
	self.anchor.MouseUpCheck:SetScript("OnShow", function(self)
		self.buttonDown = IsMouseButtonDown()
	end)
	self.anchor.MouseUpCheck:SetScript("OnUpdate", function(self)
		if self.buttonDown ~= IsMouseButtonDown() then
			self.buttonDown = IsMouseButtonDown()
			if not self.buttonDown then
				self:Hide()
				self:GetParent():GetScript("OnDragStop")()
			end
		end
	end)
	-- 옵션 프레임 생성
	self.optionFrame = CreateFrame("Frame", "InvenRaidFrames2OptionFrame", InterfaceOptionsFramePanelContainer)
	self.optionFrame:Hide()
	self.optionFrame.name = "인벤 레이드 프레임 2"
	self.optionFrame:SetScript("OnShow", function()
		LoadAddOn("InvenRaidFrames2_Option")
	end)
	InterfaceOptions_AddCategory(self.optionFrame)
	InterfaceOptionsFrame:HookScript("OnShow", function(self)
		self:ClearAllPoints()
		self:SetPoint("CENTER")
	end)
	-- 하이라이트 생성(유니트 툴팁 기능도 수행)
	self.highlightFrame = CreateFrame("Frame", nil, UIParent)
	self.highlightFrame:SetFrameStrata(self.db.strata)
	self.highlightFrame:SetFrameLevel(15)
	self.highlightFrame.texture = self.highlightFrame:CreateTexture(nil, "ADD")
	self.highlightFrame.texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
	self.highlightFrame.texture:SetBlendMode("ADD")
	self.highlightFrame.texture:SetAllPoints(self.highlightFrame)
	self.highlightFrame:SetAlpha(self.db.highlightFrameAlpha)
	self.highlightFrame:Hide()
	self.highlightFrame:SetScript("OnUpdate", function(self, timer)
		self.timer = (self.timer or 1) + timer
		if self.timer > 0.25 then
			self.timer = 0
			if IRF2.db.tooltip == "항상 표시" or (IRF2.db.tooltip == "비전투 중에만" and not InCombatLockdown()) then
				GameTooltip_SetDefaultAnchor(GameTooltip, self)
				GameTooltip:SetUnit(self.parent.vehicleUnit or self.parent.unit)
				GameTooltip:Show()
			end
		end
	end)
	hooksecurefunc(self.highlightFrame, "SetAllPoints", function(self) self.timer = 1; self:GetScript("OnUpdate")(self, 1) end)
	-- 글자 너비 측정용 임시 FontString 생성
	self.tempText = self:CreateFontString(nil, "OVERLAY")
	self:SetFont(self.tempText)
	if IsLoggedIn() then
		self:PLAYER_LOGIN()
	else
		self:RegisterEvent("PLAYER_LOGIN")
	end
end

function IRF2:PLAYER_LOGIN()
	-- 유니트 그룹 생성, 및 파티 이름표 생성
	self.group = {}
	local tag
	local function createTag()
		tag = CreateFrame("Frame", nil, self.anchor)
		tag:Hide()
		tag:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16 })
		tag.text = tag:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
		tag.text:SetPoint("CENTER", tag, "CENTER")
		tag.text:SetTextColor(1, 1, 1, 1)
		tag:EnableMouse(true)
		tag:RegisterForDrag("LeftButton")
		tag:SetScript("OnDragStart", self.anchor:GetScript("OnDragStart"))
		tag:SetScript("OnDragStop", self.anchor:GetScript("OnDragStop"))
		return tag
	end
	for i = 0, 8 do
		self:CreateGroup(i)
		self.group[i].partyTag = createTag()
		self.group[i].partyTag:SetWidth(self.db.layout.width)
		self.group[i].partyTag:SetHeight(12)
		self.group[i].partyTag:SetBackdropColor(unpack(self.db.partyTagColor[i]))
		self.group[i].partyTag.text:SetText(i == 0 and "내 파티" or ("파티 %d"):format(i))
		self.group[i].partyTag1 = createTag()
		self.group[i].partyTag1:SetWidth(12)
		self.group[i].partyTag1:SetHeight(self.db.layout.height)
		self.group[i].partyTag1:SetBackdropColor(unpack(self.db.partyTagColor[i]))
		self.group[i].partyTag1.text:SetText(i == 0 and "P" or i)
		if self.db.anchor == "좌측 상단" then
			self.group[i].partyTag:SetPoint("BOTTOM", self.group[i], "TOP", 0, 0)
			self.group[i].partyTag1:SetPoint("RIGHT", self.group[i], "LEFT", 0, 0)
		elseif self.db.anchor == "우측 상단" then
			self.group[i].partyTag:SetPoint("BOTTOM", self.group[i], "TOP", 0, 0)
			self.group[i].partyTag1:SetPoint("LEFT", self.group[i], "RIGHT", 0, 0)
		elseif self.db.anchor == "좌측 하단" then
			self.group[i].partyTag:SetPoint("TOP", self.group[i], "BOTTOM", 0, 0)
			self.group[i].partyTag1:SetPoint("RIGHT", self.group[i], "LEFT", 0, 0)
		elseif self.db.anchor == "우측 하단" then
			self.group[i].partyTag:SetPoint("TOP", self.group[i], "BOTTOM", 0, 0)
			self.group[i].partyTag1:SetPoint("LEFT", self.group[i], "RIGHT", 0, 0)
		end
	end
	if InvenRaidFrames2CharDB.usePet then
		IRF2:CreatePetGroup()
	end
	-- 유니트 그룹 위치 설정
	self.raidMembers = GetNumRaidMembers()
	self.partyMembers = GetNumPartyMembers()
	self:SetGroupPosition()
	-- 전투 유무 처리 이벤트 등록
	self:RegisterEvent("PLAYER_REGEN_DISABLED")
	self:RegisterEvent("PLAYER_REGEN_ENABLED")
	-- 파티나 공격대 구성원 업데이트 처리
	self.PARTY_MEMBERS_CHANGED = self.RAID_ROSTER_UPDATE
	self:RegisterEvent("PLAYER_ENTERING_WORLD")
	self:RegisterEvent("RAID_ROSTER_UPDATE")
	self:RegisterEvent("PARTY_MEMBERS_CHANGED")
	-- 아웃라인 옵션이 대상일 경우 처리
	if self.db.outline == "대상" then
		self:RegisterEvent("PLAYER_TARGET_CHANGED")
	end
	-- 옵션창 너비 증가 및 이동 기능 추가
	if InterfaceOptionsFrame:GetWidth() < 780 then
		InterfaceOptionsFrame:SetWidth(780)
	end
	if InterfaceOptionsFrame:GetHeight() < 520 then
		InterfaceOptionsFrame:SetHeight(520)
	end
	InterfaceOptionsFrame:SetMovable(true)
	InterfaceOptionsFrame:RegisterForDrag("LeftButton")
	InterfaceOptionsFrame:SetScript("OnMouseUp", nil)
	InterfaceOptionsFrame:SetScript("OnMouseDown", nil)
	InterfaceOptionsFrame:SetScript("OnDragStart", function(self) self:StartMoving() end)
	InterfaceOptionsFrame:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end)
	local mover = CreateFrame("Button", nil, InterfaceOptionsFrame)
	mover:EnableMouse(true)
	mover:RegisterForDrag("LeftButton")
	mover:SetPoint("TOP", InterfaceOptionsFrame, "TOP", 0, 10)
	mover:SetWidth(160)
	mover:SetHeight(40)
	mover:SetScript("OnDragStart", function(self) self:GetParent():StartMoving() end)
	mover:SetScript("OnDragStop", function(self) self:GetParent():StopMovingOrSizing() end)
	-- 빠른 체력바 반응을 위해 LibQuickHealth-2.0 사용
	self:QuickHealth()
	-- 첫 접속 업데이트
	self:RAID_ROSTER_UPDATE()
end

function IRF2:UpdateClampRectInsets()
	if self.db.anchor == "좌측 상단" then
		self.anchor:SetClampRectInsets(-2, 2 + self.db.layout.width, 2, -2 - self.db.layout.height)
	elseif self.db.anchor == "좌측 하단" then
		self.anchor:SetClampRectInsets(-2, 2 + self.db.layout.width, 2 + self.db.layout.height, 2)
	elseif self.db.anchor == "우측 상단" then
		self.anchor:SetClampRectInsets(-2 - self.db.layout.width, 2, 2, -2 - self.db.layout.height)
	elseif self.db.anchor == "우측 하단" then
		self.anchor:SetClampRectInsets(-2 - self.db.layout.width, 2, 2 + self.db.layout.height, 2)
	end
end

function IRF2:Toggle()
	-- 사용/비사용에 따른 핸들러
	self:SetGroupPosition()
	if self.group.pet then
		self:SetPetGroupOrder()
	end
end

local buffCheckFunc

function IRF2:PLAYER_REGEN_DISABLED()
	self.incombat = true
	if InvenRaidFrames2CharDB.buffCheck and InvenRaidFrames2CharDB.buffCheck.check and not InvenRaidFrames2CharDB.buffCheck.showInCombat then
		buffCheckFunc = self:GetAuraEventFunction("BuffCheck")
		if buffCheckFunc then
			for _, object in pairs(self.units) do
				buffCheckFunc(object)
			end
		end
	end
end

function IRF2:PLAYER_REGEN_ENABLED()
	self.incombat = false
	for event, data in pairs(leaveCombat) do
		data[1](select(2, unpack(data)))
		leaveCombat[event] = nil
	end
	if InvenRaidFrames2CharDB.buffCheck and InvenRaidFrames2CharDB.buffCheck.check and not InvenRaidFrames2CharDB.buffCheck.showInCombat then
		buffCheckFunc = self:GetAuraEventFunction("BuffCheck")
		if buffCheckFunc then
			for _, object in pairs(self.units) do
				buffCheckFunc(object)
			end
		end
	end
end

function IRF2:RegisterLeaveCombat(event, object, method, ...)
	-- 전투 종료 시점 시작 핸들러
	if type(object) == "table" and type(method) == "string" and type(object[method]) == "function" then
		if InCombatLockdown() then
			leaveCombat[event] = { object[method], object, ... }
		else
			object[method](object, ...)
		end
	elseif type(object) == "function" then
		if InCombatLockdown() then
			leaveCombat[event] = { object, method, ... }
		else
			object(method, ...)
		end
	end
end

function IRF2:UnregisterLeaveCombat(event)
	leaveCombat[event] = nil
end

function IRF2:RegisterObjectModule(name, func)
	-- 유닛 프레임 생성시 추가적으로 실행될 모듈 등록
	if type(name) == "string" and type(func) == "function" then
		self.objectModules[name] = func
	end
end

function IRF2:RegisterObjectHandler(name, func)
	if type(name) == "string" and type(func) == "function" then
		self.objectHandler[name] = func
	end
end

function IRF2:RegisterShownModules(name, func)
	if type(name) == "string" and type(func) == "function" then
		self.shownModules[name] = func
	end
end

function IRF2:HandleShownStatus(status)
	if self.shownStatus ~= status then
		self.shownStatus = status
		for _, func in pairs(self.shownModules) do
			func(status)
		end
	end
end

local function callbackTimerFunc(self)
	self.curTime = GetTime()
	self.clearCallback = true
	for callback, data in pairs(callbackTimer) do
		self.clearCallback = false
		if data[1] <= self.curTime then
			data[2](select(3, unpack(data)))
			callbackTimer[callback] = nil
		end
	end
	if self.clearCallback then
		self:SetScript("OnUpdate", nil)
	end
end

function IRF2:CallbackTimer(callback, timer, object, method, ...)
	-- 일정 시간 후 타이머 실행
	if type(object) == "table" and type(method) == "string" and type(object[method]) == "function" then
		callbackTimer[callback] = { GetTime() + timer, object[method], object, ... }
	elseif type(object) == "function" then
		callbackTimer[callback] = { GetTime() + timer, object, method, ... }
	end
	if callbackTimer[callback] and self:GetScript("OnUpdate") == nil then
		self:SetScript("OnUpdate", callbackTimerFunc)
	end
end

function IRF2:UnregisterCallbackTimer(callback)
	callbackTimer[callback] = nil
end

function IRF2:IsReadyCheckable()
	return (self.raidMembers > 0 or self.partyMembers > 0) and (IsPartyLeader() or IsRaidLeader() or IsRaidOfficer())
end

function IRF2:GetPartyHeaderShown()
	self.raidMembers = GetNumRaidMembers()
	self.partyMembers = GetNumPartyMembers()
	if self.db.shown == "항상" then
		if GetNumRaidMembers() == 0 then
			-- 공격대 아닐때
			return true
		elseif self.raidMembers > 0 and self.db.groupOrder.showParty then
			-- 공격대 참여 + 자기 파티
			return true
		end
	elseif self.db.shown == "파티" then
		if self.raidMembers == 0 and self.partyMembers > 0 then
			-- 파티 참여시
			return true
		elseif self.raidMembers > 0 and self.db.groupOrder.showParty then
			-- 공격대 참여 + 자기 파티
			return true
		end
	elseif self.db.shown == "공격대" and self.raidMembers > 0 and self.db.groupOrder.showParty then
		-- 공격대 참여 + 자기 파티
		return true
	end
	return false
end

function IRF2:GetGroupOrder()
	local groupOrder = classEnglish[self.db.groupOrder.class[1]]
	for i = 2, #self.db.groupOrder.class do
		groupOrder = ("%s,%s"):format(groupOrder, classEnglish[self.db.groupOrder.class[i]])
	end
	return groupOrder
end

local fullName, unitGuid, unitName, unitServer

function IRF2:GetUnitName(unit)
	unitName, unitServer = UnitName(unit)
	if type( unitServer) == "string" and  unitServer:len() > 1 then
		return unitName.."-".. unitServer
	end
	return unitName
end

local function addName(unit)
	if IRF2.units[unit] then
		fullName = IRF2:GetUnitName(unit)
		unitGuid = UnitGUID(unit)
		IRF2.names[fullName] = IRF2.names[fullName] or {}
		tinsert(IRF2.names[fullName], IRF2.units[unit])
		if UnitIsUnit("player", unit) then
			return true
		end
	end
	return nil
end

local subgroup

function IRF2:RAID_ROSTER_UPDATE()
	self.prevRM = (self.raidMembers or 0)
	self.prevPM = (self.partyMembers or 0)
	self.raidMembers = GetNumRaidMembers()
	self.partyMembers = GetNumPartyMembers()
	for unit, object in pairs(self.units) do
		if not(UnitExists(unit) and object.unit == unit and object:IsShown()) then
			self.units[unit] = nil
		end
	end
	for name, objects in pairs(self.names) do
		for index, object in pairs(objects) do
			self.names[name][index] = nil
		end
	end
	for guid, objects in pairs(self.guids) do
		for index, object in pairs(objects) do
			self.guids[guid][index] = nil
		end
	end
	addName("player")
	if self.partyMembers > 0 then
		for i = 1, self.partyMembers do
			addName(("party%d"):format(i))
		end
		self.group[0].members = self.partyMembers + 1
	else
		self.group[0].members = 1
	end
	for i = 1, 8 do
		self.group[i].members = 0
	end
	if self.raidMembers > 0 then
		for i = 1, self.raidMembers do
			subgroup = select(3, GetRaidRosterInfo(i))
			if addName(("raid%d"):format(i)) then
				self.myGroup = subgroup
			end
			self.group[subgroup].members = self.group[subgroup].members + 1
		end
	else
		self.myGroup = 0
	end
	for i = 1, 8 do
		if self.group[i].partyTag then
			if i == self.myGroup then
				self.group[i].partyTag:SetBackdropColor(unpack(self.db.partyTagColor[0]))
				self.group[i].partyTag1:SetBackdropColor(self.group[i].partyTag:GetBackdropColor())
			else
				self.group[i].partyTag:SetBackdropColor(unpack(self.db.partyTagColor[i]))
				self.group[i].partyTag1:SetBackdropColor(self.group[i].partyTag:GetBackdropColor())
			end
		end
	end
	if self.db.run then
		if ((self.prevRM == 0 and self.raidMembers > 0) or (self.prevRM > 0 and self.raidMembers == 0)) or (self.prevRM == 0 and self.raidMembers == 0 and ((self.prevPM > 0 and self.partyMembers == 0) or (self.prevPM == 0 and self.partyMembers > 0))) then
			if InCombatLockdown() then
				self:RegisterLeaveCombat("Group", self, "SetGroupPosition")
				self:RegisterLeaveCombat("PetGroup", self, "SetPetGroupOrder")

			else
				self:SetGroupPosition()
				self:SetPetGroupOrder()
			end
		end
		self:UpdateAllPlayerUnits()
		if self.initPEW then
			self:UpdateBorder()
		end
	end
	self:UpdatePartyTag()
	self:CallbackTimer("PetBorder", 0.1, self, "BorderPet")
	if self.prevRM < self.raidMembers or self.prevPM < self.partyMembers then
		self:SendVersionMessage()
	end
end

function IRF2:UpdatePartyTag()
	if not self.db.run or self.db.groupOrder.byClass or self.db.groupOrder.byName or not self.db.partyTag then
		for i = 0, 8 do
			self.group[i].partyTag:Hide()
			self.group[i].partyTag1:Hide()
		end
	elseif self.db.groupOrder.point == "세로" then
		for i = 0, 8 do
			if self.group[i]:IsVisible() and self:GetGroupMembers(i) > 0 then
				self.group[i].partyTag:Show()
			else
				self.group[i].partyTag:Hide()
			end
			self.group[i].partyTag1:Hide()
		end
	else
		for i = 0, 8 do
			if self.group[i]:IsVisible() and self:GetGroupMembers(i) > 0 then
				self.group[i].partyTag1:Show()
			else
				self.group[i].partyTag1:Hide()
			end
			self.group[i].partyTag:Hide()
		end
	end
end

function IRF2:GetGroupMembers(index)
	if self.group[index] then
		return self.group[index].members or 0
	else
		return 0
	end
end

function IRF2:RaidFrameIsShown()
	if not self.db.run then
		return false
	elseif self.db.shown == "항상" then
		return true
	elseif self.db.shown == "파티" and (GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0) then
		return true
	elseif self.db.shown == "공격대" and GetNumRaidMembers() > 0 then
		return true
	end
	return false
end

function IRF2:PLAYER_ENTERING_WORLD()
	if not self.initPEW and IsInGuild() then
		self:SendVersionMessage("GUILD")
	end
	self.initPEW = true
	self:RAID_ROSTER_UPDATE()
	self:CallbackTimer("ForceBorderUpdate", 2.5, self, "UpdateBorder")
end

function IRF2:PLAYER_TARGET_CHANGED()
	for unit, object in pairs(self.units) do
		object:UpdateOutline()
	end
end

function IRF2:GetFramePoint(posText)
	return framePoints[posText]
end

function IRF2:StopMoving()
	self.anchor:StopMovingOrSizing()
	if self.petAnchor then
		self.petAnchor:StopMovingOrSizing()
	end
end

function IRF2:GetVehicleUnit(unit)
	if type(unit) == "string" then
		if UnitHasVehicleUI(unit) then
			if unit == "player" then
				return "pet"
			elseif unit:find("^party(%d+)$") then
				return unit:gsub("party", "partypet") or unit
			elseif unit:find("^raid(%d+)$") then
				return unit:gsub("raid", "raidpet") or unit
			end
		end
		return nil
	end
	return nil
end

local ownerUnit, runobject

function IRF2:GetUnitToObject(unit)
	if unit then
		if (unit == "vehicle" or unit == "pet") and IRF2.units.player and IRF2.units.player.vehicleUnit == "pet" then
			return IRF2.units.player
		elseif unit:find("pet") then
			ownerUnit = unit:gsub("pet", "")
			if IRF2.units[ownerUnit] and IRF2.units[ownerUnit].vehicleUnit == unit then
				return IRF2.units[ownerUnit]
			end
		end
		if IRF2.units[unit] and IRF2.units[unit].unit == unit then
			return IRF2.units[unit]
		end
	end
	return nil
end

function IRF2:RunUnitFunction(unit, func)
	if unit and UnitExists(unit) then
		runobject = self:GetUnitToObject(unit)
		if runobject then
			func(runobject)
			if IRF2.units[unit] and IRF2.units[unit] ~= runobject and IRF2.units[unit].unit == unit then
				func(IRF2.units[unit])
			end
		end
	end
end

function IRF2:Message(msg)
	ChatFrame1:AddMessage("|cffffff00IRF2: |r"..msg, 1, 1, 1)
end

function IRF2:OnClick(button)
	if button == "RightButton" then
		ToggleDropDownMenu(1, nil, IRF2.mapButtonMenu, "cursor")
	else
		if InterfaceOptionsFramePanelContainer.displayedPanel == IRF2.optionFrame then
			InterfaceOptionsFrameOkay_OnClick()
		else
			InterfaceOptionsFrame_OpenToCategory(IRF2.optionFrame)
		end
	end
end

function IRF2:OnTooltip(tooltip)
	tooltip = tooltip or GameTooltip
	tooltip:AddLine("인벤 레이드 프레임 2 v"..IRF2.version)
	tooltip:AddLine("http://wow.inven.co.kr", 1, 1, 1)
	tooltip:AddLine("좌클릭: 설정창 열기", 1, 1, 0)
	tooltip:AddLine("우클릭: 메뉴 열기", 1, 1, 0)
end

function IRF2.GetSpellLocale(spellID)
	return GetSpellInfo(spellID) or "*"
end