local XPerl_Party_Events = {}
local checkRaidNextUpdate
local PartyFrames = {}
local conf
XPerl_RequestConfig(function(new) conf = new end)

----------------------
-- Loading Function --
----------------------
function XPerl_Party_Events_OnLoad(self)
	local events = {"PLAYER_ENTERING_WORLD", "PARTY_MEMBER_ENABLE", "PARTY_MEMBER_DISABLE", "RAID_ROSTER_UPDATED", "PARTY_MEMBERS_CHANGED",
			"UNIT_COMBAT", "UNIT_SPELLMISS", "UNIT_FACTION", "UNIT_DYNAMIC_FLAGS", "UNIT_FLAGS", "UNIT_AURA", "UNIT_MODEL_CHANGED",
			"UNIT_TARGET", "UNIT_RAGE", "UNIT_MAXRAGE", "UNIT_ENERGY", "UNIT_MAXENERGY", "UNIT_MANA", "UNIT_MAXMANA",
			"UNIT_HEALTH", "UNIT_MAXHEALTH", "UNIT_LEVEL", "UNIT_DISPLAYPOWER", "UNIT_NAME_UPDATE", "PLAYER_FLAGS_CHANGED"}
	for i,event in pairs(events) do
		self:RegisterEvent(event)
	end

	XPerl_BlizzFrameDisable(PartyMemberFrame1)
	XPerl_BlizzFrameDisable(PartyMemberFrame2)
	XPerl_BlizzFrameDisable(PartyMemberFrame3)
	XPerl_BlizzFrameDisable(PartyMemberFrame4)

	hooksecurefunc("ShowPartyFrame",
		function()
			if (not InCombatLockdown()) then
				for i = 1,4 do
					getglobal("PartyMemberFrame"..i):Hide()
				end
			end
		end
	)

	self:SetScript("OnEvent", XPerl_Party_OnEvent)
	XPerl_RegisterOptionChanger(XPerl_Party_Set_Bits)
	XPerl_Highlight:Register(XPerl_Party_HighlightCallback)
end

-- XPerl_Party_HighlightCallback(updateName)
function XPerl_Party_HighlightCallback(updateName)
	for i = 1,GetNumPartyMembers() do
		if (UnitName("party"..i) == updateName) then
			XPerl_Highlight:SetHighlight(getglobal("XPerl_party"..i), updateName)
		end
	end
end

-- onAttrChanged
local function onAttrChanged(self, name, value)
	if (name == "unit") then
		if (value and value ~= "party0") then
			--SetPartyFrame(value, self)
			if (conf) then
				XPerl_Party_UpdateDisplay(self)
			end
			if (XPerl_ArcaneBar_RegisterFrame) then
				XPerl_ArcaneBar_RegisterFrame(self.nameFrame, value, "ArcaneBarParty")
			end
		end
	end
end

-- XPerl_Party_OnLoad
function XPerl_Party_OnLoad(self)
	XPerl_SetChildMembers(self)
	self.targetFrame.statsFrame = self.targetFrame.healthBar	-- So the healthbar fades as part of pseudo statsFrame

	self.buffSetup = {
		debuffOnEnter = XPerl_Party_SetDeBuffTooltip,
		buffOnEnter = XPerl_Party_SetBuffTooltip,
		buffOnLeave = XPerl_PlayerTipHide,
		buffSizeOpt = "PartyBuffSize",
		debuffSizeMod = 0.4,
		debuffAnchor1 = function(self, b) b:SetPoint("LEFT", self.statsFrame, "RIGHT", 0, 0) end,
		buffAnchor1 = function(self, b) b:SetPoint("TOPLEFT", self.buffFrame, "TOPLEFT", 0, 0) end,
	}

	XPerl_Party_SecureHeader:SetAttribute("child"..self:GetID(), self)
	self.partyid = "party"..self:GetID()
	PartyFrames[self.partyid] = self

	self.hitIndicator.text:SetPoint("CENTER", self.portraitFrame, "CENTER", 0, 0)
	CombatFeedback_Initialize(self.hitIndicator.text, 30)

	XPerl_SecureUnitButton_OnLoad(self, self.partyid, XPerl_ShowGenericMenu)
	XPerl_SecureUnitButton_OnLoad(self.nameFrame, self.partyid, XPerl_ShowGenericMenu)

	self.time = 0

	--self.highlight.tex:SetVertexColor(0.86, 0.82, 0.41)
	XPerl_RegisterHighlight(self.highlight, 3)
	XPerl_RegisterHighlight(self.targetFrame, 3)
	XPerl_RegisterPerlFrames(self, {self.nameFrame, self.statsFrame, self.portraitFrame, self.levelFrame, self.targetFrame})

	self.FlashFrames = {self.nameFrame, self.levelFrame, self.statsFrame, self.portraitFrame}

	self:SetScript("OnUpdate", XPerl_Party_OnUpdate)
	self:SetScript("OnAttributeChanged", onAttrChanged)
end

-- ShowHideValues
local function ShowHideValues(self)
	if (conf.ShowPartyValues == 1) then
        	self.statsFrame.healthBar.text:Show()
        	self.statsFrame.manaBar.text:Show()
	else
        	self.statsFrame.healthBar.text:Hide()
        	self.statsFrame.manaBar.text:Hide()
	end
end

-- XPerl_Party_UpdateHealth
local function XPerl_Party_UpdateHealth(self)
	local numID = strmatch(self.partyid, "party(%d)")			-- TODO - will be wrong later
	local Partyhealth = UnitHealth(self.partyid)
	local Partyhealthmax = UnitHealthMax(self.partyid)
	local reason

	if (Partyhealth > Partyhealthmax) then
		-- New glitch with 1.12.1
		if (UnitIsDeadOrGhost(self.partyid)) then
			Partyhealth = 0
		else
			Partyhealth = Partyhealthmax
		end
	end

	if (self.feigning and not XPerl_IsFeignDeath(self.partyid)) then
		self.feigning = nil
	end
	if (self.feigning) then
		self.statsFrame.healthBar:SetMinMaxValues(0, 1)
	        self.statsFrame.healthBar:SetValue(0)
		XPerl_SetSmoothBarColor(self.statsFrame.healthBar, 0)
	else
		XPerl_SetHealthBar(self.statsFrame.healthBar, Partyhealth, Partyhealthmax, conf.HealerModeParty, conf.HealerModePartyType)
	end

	self.statsFrame.healthBar.percent:SetText(format("%d",(100*(Partyhealth / Partyhealthmax))+0.5).."%")

	if (not UnitIsConnected(self.partyid)) then
		reason = XPERL_LOC_OFFLINE
	else
		if (self.feigning) then
			reason = XPERL_LOC_FEIGNDEATH

		elseif (self.afk) then
			reason = CHAT_MSG_AFK

		elseif (self.dnd) then
			reason = CHAT_MSG_DND

		elseif (UnitIsDead(self.partyid)) then
			reason = XPERL_LOC_DEAD

		elseif (UnitIsGhost(self.partyid)) then
			reason = XPERL_LOC_GHOST

		elseif ((Partyhealth==1) and (Partyhealthmax==1)) then
			reason = XPERL_LOC_UPDATING
		end
	end

	ShowHideValues(self)
	if (reason) then
		if (conf.ShowPartyPercent == 1) then
			local old = self.statsFrame.healthBar.percent:GetText()
			self.statsFrame.healthBar.percent:SetText(reason)

			if (self.statsFrame.healthBar.percent:GetStringWidth() > (self.statsFrame:GetWidth() - self.statsFrame.healthBar:GetWidth() - 8)) then
				self.statsFrame.healthBar.percent:SetText(old)
				self.statsFrame.healthBar.text:SetText(reason)
				self.statsFrame.healthBar.text:Show()
			else
				self.statsFrame.healthBar.percent:Show()
			end
		else
			self.statsFrame.healthBar.text:SetText(reason)
			self.statsFrame.healthBar.text:Show()
		end

		self.statsFrame:SetGrey()
	else
		if (self.statsFrame.greyMana) then
			self.statsFrame.greyMana = nil
			XPerl_SetManaBarType(self)
		end
	end
end

-- XPerl_Party_UpdatePlayerFlags(self)
local function XPerl_Party_UpdatePlayerFlags(self)
	if (UnitIsAFK(self.partyid)) then
		if (not self.afk) then
			change = true
			self.afk = GetTime()
			self.dnd = nil
		end
	elseif (UnitIsDND(self.partyid)) then
	--	if (not self.dnd) then
			change = true
	--		self.dnd = GetTime()
			self.afk = nil
	--	end
	else
		if (self.afk or self.dnd) then
			self.afk, self.dnd = nil, nil
			change = true
		end
	end

	if (change) then
		XPerl_Party_UpdateHealth(self)
	end
end

--------------------
-- Buff Functions --
--------------------

-- XPerl_Party_SetDebuffLocation
function XPerl_Party_SetDebuffLocation(self)
	local debuff1 = self.buffFrame.debuff and self.buffFrame.debuff[1]
	if (debuff1) then
        	debuff1:ClearAllPoints()

		if (conf.PartyDebuffsBelow == 0) then
			local petFrame = getglobal("XPerl_partypet"..self:GetID())
			if (petFrame and petFrame:IsVisible()) then
        	                debuff1:SetPoint("TOPLEFT", petFrame.nameFrame, "TOPRIGHT", 0, -4)
			else
        	                debuff1:SetPoint("TOPLEFT", self.statsFrame, "TOPRIGHT", 0, -4)
			end
		else
			local buff1 = self.buffFrame.buff and self.buffFrame.buff[1]
			if (not buff1) then
        	        	debuff1:SetPoint("TOPLEFT", self.buffFrame, "TOPLEFT", 0, -20)
			else
        	        	debuff1:SetPoint("TOPLEFT", buff1, "BOTTOMLEFT", 0, -2)
			end
		end
	end
end

-- XPerl_Party_SetBuffLocation
local function XPerl_Party_SetBuffLocation(self)
	local buff1 = self.buffFrame.buff and self.buffFrame.buff[1]
	if (buff1) then
        	buff1:ClearAllPoints()
		buff1:SetPoint("TOPLEFT", self.buffFrame, "TOPLEFT", 0, 0)
	end
end

-- XPerl_Party_Buff_UpdateAll
local function XPerl_Party_Buff_UpdateAll(self)
	if (conf.PartyBuffs == 0 and conf.PartyDebuffs == 0) then
		self.buffFrame:Hide()
	else
		self.buffFrame:Show()

		if (UnitExists(self.partyid)) then
			if (conf.PartyBuffs == 1) then
				for buffnum=1,16 do
					local name, rank, buff, count = XPerl_UnitBuff(self.partyid, buffnum, conf.PartyCastableBuffs)

					if (buff) then
						local button = XPerl_GetBuffButton(self, buffnum, 0, true)
						button.icon:SetTexture(buff)
						if (count > 1) then
							button.count:SetText(count)
							button.count:Show()
						else
							button.count:Hide()
						end
						button:Show()
					else
						local button = self.buffFrame.buff and self.buffFrame.buff[buffnum]
						if (button) then
							button:Hide()
						end
					end
				end

				XPerl_Party_SetBuffLocation(self)
			else
				for buffnum=1,16 do
					local button = self.buffFrame.buff and self.buffFrame.buff[buffnum]
					if (button) then
						button:Hide()
					end
				end
			end

			if (conf.PartyDebuffs == 1) then
				for buffnum=1,8 do
					local name, rank, buff, count, debuffType = XPerl_UnitDebuff(self.partyid, buffnum, conf.PartyCurableDebuffs)

					if (buff) then
						local button = XPerl_GetBuffButton(self, buffnum, 1, buff)

						button.icon:SetTexture(buff)

						if (count > 1) then
							button.count:SetText(count)
							button.count:Show()
						else
							button.count:Hide()
						end

						local borderColor = DebuffTypeColor[(debuffType or "none")]
						button.border:SetVertexColor(borderColor.r, borderColor.g, borderColor.b)

						button:Show()
					else
						local button = self.buffFrame.debuff and self.buffFrame.debuff[buffnum]
						if (button) then
							button:Hide()
						end
					end
				end

				XPerl_Party_SetDebuffLocation(self)
			else
				for buffnum=1,8 do
					local button = self.buffFrame.debuff and self.buffFrame.debuff[buffnum]
					if (button) then
						button:Hide()
					end
				end
			end
		end
	end

	local _, class = UnitClass(self.partyid)
	if (class == "HUNTER") then
		local feigning = XPerl_IsFeignDeath(self.partyid)
		if (feigning ~= self.feigning) then
			self.feigning = feigning
			XPerl_Party_UpdateHealth(self)
		end
	end

	XPerl_CheckDebuffs(self, self.partyid)
end

-- XPerl_Party_SetBuffTooltip
function XPerl_Party_SetBuffTooltip(self)
	local partyid = self:GetParent():GetParent():GetAttribute("unit")
	if (partyid) then
		GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT",30,0)
		XPerl_TooltipSetUnitBuff(GameTooltip, partyid, self:GetID(), conf.PartyCastableBuffs)
		XPerl_ToolTip_AddBuffDuration(partyid)
	end
end

-- XPerl_Party_SetDeBuffTooltip
function XPerl_Party_SetDeBuffTooltip(self)
	local partyid = self:GetParent():GetParent():GetAttribute("unit")
	if (partyid) then
		GameTooltip:SetOwner(self, "ANCHOR_BOTTOMRIGHT",30,0)
		XPerl_TooltipSetUnitDebuff(GameTooltip, partyid, self:GetID(), conf.PartyCurableDebuffs)
		XPerl_ToolTip_AddDebuffDuration(partyid, nil, self:GetID(), conf.PartyCurableDebuffs)
	end
end

-------------------------
-- The Update Function --
-------------------------
local function XPerl_Party_CombatFlash(self, elapsed, argNew, argGreen)
	if (XPerl_CombatFlashSet (elapsed, self, argNew, argGreen)) then
		XPerl_CombatFlashSetFrames(self)
	end
end

-- XPerl_Party_UpdatePortrait
function XPerl_Party_UpdatePortrait(self)
	if (conf.ShowPartyPortrait == 1) then
		if (conf.ShowPartyPortrait3D == 1 and UnitIsVisible(self.partyid)) then
			self.portraitFrame.portrait:Hide()
			self.portraitFrame.portrait3D:Show()
			XPerlSetPortrait3D(self.portraitFrame.portrait3D, self.partyid)
		else
			self.portraitFrame.portrait3D.last3DTime = nil
			self.portraitFrame.portrait:Show()
			self.portraitFrame.portrait3D:Hide()
			SetPortraitTexture(self.portraitFrame.portrait, self.partyid)
		end
	end
end

-- XPerl_Party_UpdateName
local function XPerl_Party_UpdateName(self)
	local Partyname = UnitName(self.partyid)
	if (Partyname) then
		self.nameFrame.text:SetFontObject(GameFontNormal)
                self.nameFrame.text:SetText(Partyname)

		if (self.nameFrame.text:GetStringWidth() > self.nameFrame:GetWidth() - 4) then
			self.nameFrame.text:SetFontObject(GameFontNormalSmall)
		else
			self.nameFrame.text:SetFontObject(GameFontNormal)
		end

		XPerl_ColourFriendlyUnit(self.nameFrame.text, self.partyid)
	end
end

-- XPerl_Party_UpdateLeader
local function XPerl_Party_UpdateLeader(self)
	if ("party"..GetPartyLeaderIndex() == self.partyid) then
		self.nameFrame.leaderIcon:Show()
	else
		self.nameFrame.leaderIcon:Hide()
	end

	local lootMethod
	local lootMaster
	lootMethod, lootMaster = GetLootMethod()
	if (self.partyid == "party"..(lootMaster or "nil") ) then
		self.nameFrame.masterIcon:Show()
	else
		self.nameFrame.masterIcon:Hide()
	end
end

-- XPerl_Party_Update_Control
local function XPerl_Party_Update_Control(self)
	if (UnitIsVisible(self.partyid) and UnitIsCharmed(self.partyid)) then
		self.nameFrame.warningIcon:Show()
	else
		self.nameFrame.warningIcon:Hide()
	end
end

-- XPerl_Party_UpdatePVP
local function XPerl_Party_UpdatePVP(self)
	if (conf.ShowPartyPVP == 1 and UnitIsPVP(self.partyid)) then
		self.nameFrame.pvpIcon:SetTexture("Interface\\TargetingFrame\\UI-PVP-"..(UnitFactionGroup(self.partyid) or "FFA"))
		self.nameFrame.pvpIcon:Show()
	else
		self.nameFrame.pvpIcon:Hide()
	end
end

-- XPerl_Party_UpdateCombat
function XPerl_Party_UpdateCombat(self)
	XPerl_Party_UpdateCombat1(self, self.partyid)
	XPerl_Party_Update_Control(self)
end

-- XPerl_Party_UpdateCombat1
function XPerl_Party_UpdateCombat1(self, argID)
	if (UnitAffectingCombat(argID)) then
		self.nameFrame.combatIcon:Show()
	else
		self.nameFrame.combatIcon:Hide()
	end
end

-- XPerl_Party_UpdateLevel
local function XPerl_Party_UpdateLevel(self)
	local Partylevel = UnitLevel(self.partyid)
	local color = GetDifficultyColor(Partylevel)

	self.levelFrame.text:SetTextColor(color.r,color.g,color.b)
	self.levelFrame.text:SetText(Partylevel)
end

-- XPerl_Party_UpdateClass
local function XPerl_Party_UpdateClass(self)
	if (UnitIsPlayer(self.partyid)) then
		local _, PlayerClass = UnitClass(self.partyid)
		local r, l, t, b = XPerl_ClassPos(PlayerClass)
		self.levelFrame.classTexture:SetTexCoord(r, l, t, b)
	end

	if (conf.ShowPartyClassIcon == 1) then
	        self.levelFrame.classTexture:Show()
	else
	        self.levelFrame.classTexture:Hide()
	end
end

-- XPerl_Party_UpdateMana
local function XPerl_Party_UpdateMana(self)
	local Partymana = UnitMana(self.partyid)
	local Partymanamax = UnitManaMax(self.partyid)

	if (Partymanamax == 1 and Partymana > Partymanamax) then
		Partymanamax = Partymana
	end

	self.statsFrame.manaBar:SetMinMaxValues(0, Partymanamax)
	self.statsFrame.manaBar:SetValue(Partymana)
	pmanaPct = (Partymana * 100.0) / Partymanamax
	pmanaPct =  format("%3.0f", pmanaPct)

	if (UnitPowerType(self.partyid)>=1) then
	        self.statsFrame.manaBar.percent:SetText(Partymana)
	else
	        self.statsFrame.manaBar.percent:SetText(format("%d",(100*(Partymana / Partymanamax))+0.5).."%")
	end

	if (conf.ShowPartyValues == 1) then
		self.statsFrame.manaBar.text:Show()
	else
		self.statsFrame.manaBar.text:Hide()
	end

	self.statsFrame.manaBar.text:SetText(Partymana.."/"..Partymanamax)

	if (not UnitIsConnected(self.partyid)) then
	        self.statsFrame.healthBar.text:SetText(XPERL_LOC_OFFLINE)
		if (not self.statsFrame.greyMana) then
			self.statsFrame:SetGrey()
		end
	end
end

-- XPerl_Party_UpdateRange
local function XPerl_Party_UpdateRange(self, overrideUnit)
	local partyid = overrideUnit or self.partyid
	if (partyid) then
		if (conf.ShowParty30YardSymbol == 0 or CheckInteractDistance(self.partyid, 4) or not UnitIsConnected(self.partyid)) then
			self.nameFrame.rangeIcon:Hide()
		else
			self.nameFrame.rangeIcon:Show()
			self.nameFrame.rangeIcon:SetAlpha(1)
		end
	end
end

-- CheckRaid
local function CheckRaid()
	if (InCombatLockdown()) then
		tinsert(XPerl_OutOfCombatQueue, CheckRaid)
	else
		XPerl_Party_Anchor:StopMovingOrSizing()
		if (GetNumRaidMembers() > 0) then
			if (conf.ShowPartyRaid == 1) then
				XPerl_Party_SecureHeader:Show()
			else
				XPerl_Party_SecureHeader:Hide()
			end
		else
			XPerl_Party_SecureHeader:Show()
		end
	end
end

-- XPerl_Party_TargetUpdateHealth
local function XPerl_Party_TargetUpdateHealth(self)
	local unit = self.partyid.."target"
	local hp, hpMax = UnitHealth(unit), UnitHealthMax(unit)
	self.targetFrame.lastHP, self.targetFrame.lastHPMax = hp, hpMax
	self.targetFrame.lastUpdate = GetTime()

	self.targetFrame.healthBar:SetMinMaxValues(0, hpMax)
	self.targetFrame.healthBar:SetValue(hp)
	self.targetFrame.healthBar.text:SetText(format("%d"..PERCENT_SYMBOL, 100 * hp / hpMax))
	self.targetFrame.healthBar.text:Show()

	if (UnitIsDead(unit)) then
		self.targetFrame.healthBar:SetStatusBarColor(0.5, 0.5, 0.5, 1)
		self.targetFrame.healthBar.bg:SetVertexColor(0.5, 0.5, 0.5, 0.5)
		self.targetFrame.healthBar.text:SetText(XPERL_LOC_DEAD)
	else
		XPerl_SetSmoothBarColor(self.targetFrame.healthBar, hp / hpMax)
	end

	if (UnitAffectingCombat(unit)) then
	        self.targetFrame.combatIcon:SetTexCoord(0.5, 1.0, 0.0, 0.5)
	        self.targetFrame.combatIcon:Show()
	else
	        self.targetFrame.combatIcon:Hide()
	end

	if (conf.ShowPartyPVP == 1 and UnitIsPVP(unit)) then
		self.targetFrame.pvpIcon:SetTexture("Interface\\TargetingFrame\\UI-PVP-"..(UnitFactionGroup(unit) or "FFA"))
		self.targetFrame.pvpIcon:Show()
	else
		self.targetFrame.pvpIcon:Hide()
	end
end

-- XPerl_Party_UpdateTarget
local function XPerl_Party_UpdateTarget(self)
	if (conf.ShowPartyTarget == 1) then
		if (UnitIsConnected(self.partyid) and UnitExists(self.partyid) and UnitIsVisible(self.partyid)) then
			local unit = self.partyid.."target"
			local targetname = UnitName(unit)

			if (targetname and targetname ~= UNKNOWNOBJECT) then
				self.targetFrame.text:SetText(targetname)

				XPerl_SetUnitNameColor (unit, self.targetFrame.text)

				XPerl_Party_TargetUpdateHealth(self)
			end
		end
	end
end

-- XPerl_Party_OnUpdate
function XPerl_Party_OnUpdate(self, elapsed)
	CombatFeedback_OnUpdate(elapsed)

	if (self.PlayerFlash) then
		XPerl_Party_CombatFlash(self, arg1, false)
	end

	self.time = elapsed + self.time
	if (self.time >= 0.2) then
		self.time = 0
		local id = "party"..self:GetID()

		if (conf.PartyTargetLarge == 1 and self.targetFrame:IsVisible()) then
			local hp, hpMax = UnitHealth(id.."target"), UnitHealthMax(id.."target")
			if (hp ~= self.targetFrame.lastHP or hpMax ~= self.targetFrame.lastHPMax or GetTime() > self.targetFrame.lastUpdate + 5000) then
				XPerl_Party_TargetUpdateHealth(self)
			end
		end

		XPerl_Party_UpdateRange(self, id)
		XPerl_UpdateSpellRange(self, id)
		XPerl_UpdateSpellRange(self.targetFrame, id.."target")

		if (checkRaidNextUpdate) then
			checkRaidNextUpdate = checkRaidNextUpdate - 1
			if (checkRaidNextUpdate <= 0) then
				checkRaidNextUpdate = nil
				CheckRaid()

				-- Due to a bug in the API (WoW 2.0.1), GetPartyLeaderIndex() can often claim
				-- that party1 is the leader, even when they're not. So, we do a delayed check
				-- after a party change
				for i,frame in pairs(PartyFrames) do
					XPerl_Party_UpdateLeader(frame)
				end
			end
		end
	end
end

-- XPerl_Party_UpdateDisplayAll
function XPerl_Party_UpdateDisplayAll()
	local any
	for i,frame in pairs(PartyFrames) do
		XPerl_Party_UpdateDisplay(frame)
		if (frame:IsShown()) then
			any = true
		end
	end
end

-- XPerl_Party_UpdateDisplay
function XPerl_Party_UpdateDisplay(self)
	if (UnitExists(self.partyid)) then
		self.afk, self.dnd = nil,nil
		XPerl_Party_UpdateName(self)
		XPerl_Party_UpdateLeader(self)
		XPerl_Party_UpdateClass(self)
		XPerl_SetManaBarType(self)
		XPerl_Party_UpdateHealth(self)
		XPerl_Party_UpdatePlayerFlags(self)
		XPerl_Party_UpdateMana(self)
		XPerl_Party_UpdateLevel(self)
		XPerl_Party_Update_Control(self)
        	XPerl_Party_UpdateCombat(self)
		XPerl_Party_UpdatePVP(self)
		XPerl_Party_UpdatePortrait(self)
		XPerl_Party_Buff_UpdateAll(self)
		XPerl_Party_UpdateTarget(self)
		XPerl_UpdateSpellRange(self, self.partyid)
	end
end

-------------------
-- Event Handler --
-------------------
function XPerl_Party_OnEvent(self, event, unit)
	local func = XPerl_Party_Events[event]
	if (func) then
		if (strmatch(event, "^UNIT_")) then
			local f = PartyFrames[unit]
			if (f) then
				func(f)
			end
		else
			func(self, unit)
		end
	else
XPerl_ShowMessage("EXTRA EVENT")
	end
end

-- PARTY_LEADER_CHANGED
function XPerl_Party_Events:PARTY_LEADER_CHANGED()
	for i,frame in pairs(PartyFrames) do
		XPerl_Party_UpdateLeader(frame)
	end
end

XPerl_Party_Events.PARTY_LOOT_METHOD_CHANGED	= XPerl_Party_Events.PARTY_LEADER_CHANGED

-- UNIT_COMBAT
function XPerl_Party_Events:UNIT_COMBAT()
	if (arg1 == self.partyid) then
		if (conf.PartyCombatHitIndicator == 1 and conf.ShowPartyPortrait == 1) then
			local o = this
			this = self
			CombatFeedback_OnCombatEvent(arg2, arg3, arg4, arg5)
			this = o
		end

		XPerl_Party_UpdateCombat(self)
		if (arg2 == "HEAL") then
			XPerl_Party_CombatFlash(self, 0, true, true)
		elseif (arg4 and arg4 > 0) then
			XPerl_Party_CombatFlash(self, 0, true)
		end
	end
end

-- UNIT_SPELLMISS
function XPerl_Party_Events:UNIT_SPELLMISS()
	if (arg1 == self.partyid) then
		if (conf.PartyCombatHitIndicator == 1 and conf.ShowPartyPortrait == 1) then
			CombatFeedback_OnSpellMissEvent(arg2)
		end
	end
end

-- UNIT_HEALTH, UNIT_MAXHEALTH
function XPerl_Party_Events:UNIT_HEALTH()
	XPerl_Party_UpdateHealth(self)
end

-- PARTY_MEMBER_ENABLE
function XPerl_Party_Events:PARTY_MEMBER_ENABLE()
	for k,v in pairs(PartyFrames) do
		v.afk, v.dnd = nil,nil
		XPerl_Party_UpdateHealth(v)
		XPerl_Party_UpdatePlayerFlags(v)
		XPerl_Party_UpdateCombat(v)
		XPerl_Party_UpdateTarget(v)
	end
end

XPerl_Party_Events.PARTY_MEMBER_DISABLE	= XPerl_Party_Events.PARTY_MEMBER_ENABLE

-- UNIT_MAXHEALTH
function XPerl_Party_Events:UNIT_MAXHEALTH()
	XPerl_Party_UpdateHealth(self)
	XPerl_Party_UpdateLevel(self)	-- Level not available until we've received maxhealth
	XPerl_Party_UpdateClass(self)
end

-- PLAYER_ENTERING_WORLD
function XPerl_Party_Events:PLAYER_ENTERING_WORLD()
	XPerl_ProtectedCall(XPerl_Party_SetInitialAttributes)
end

-- RAID_ROSTER_UPDATED
function XPerl_Party_Events:RAID_ROSTER_UPDATED()
	checkRaidNextUpdate = 3
	CheckRaid()
end

XPerl_Party_Events.PARTY_MEMBERS_CHANGED = XPerl_Party_Events.RAID_ROSTER_UPDATED

-- UNIT_MODEL_CHANGED
function XPerl_Party_Events:UNIT_MODEL_CHANGED()
	XPerl_Party_UpdatePortrait(self)
end
--XPerl_Party_Events.UNIT_PORTRAIT_UPDATE = XPerl_Party_Events.UNIT_MODEL_CHANGED

-- UNIT_MANA
function XPerl_Party_Events:UNIT_MANA()
	XPerl_Party_UpdateMana(self)
end

XPerl_Party_Events.UNIT_MAXMANA		= XPerl_Party_Events.UNIT_MANA
XPerl_Party_Events.UNIT_RAGE		= XPerl_Party_Events.UNIT_MANA
XPerl_Party_Events.UNIT_MAXRAGE		= XPerl_Party_Events.UNIT_MANA
XPerl_Party_Events.UNIT_ENERGY		= XPerl_Party_Events.UNIT_MANA
XPerl_Party_Events.UNIT_MAXENERGY	= XPerl_Party_Events.UNIT_MANA

-- UNIT_DISPLAYPOWER
function XPerl_Party_Events:UNIT_DISPLAYPOWER()
	XPerl_SetManaBarType(self)
	XPerl_Party_UpdateMana(self)
end

-- PLAYER_FLAGS_CHANGED()
function XPerl_Party_Events:PLAYER_FLAGS_CHANGED(unit)
	local f = PartyFrames[unit]
	if (f) then
		XPerl_Party_UpdatePlayerFlags(f)
	end
end

-- UNIT_NAME_UPDATE
function XPerl_Party_Events:UNIT_NAME_UPDATE()
	XPerl_Party_UpdateName(self)
end

-- UNIT_LEVEL
function XPerl_Party_Events:UNIT_LEVEL()
	XPerl_Party_UpdateLevel(self)
end

-- UNIT_AURA
function XPerl_Party_Events:UNIT_AURA()
	XPerl_Party_Buff_UpdateAll(self)
end

-- UNIT_FACTION
function XPerl_Party_Events:UNIT_FACTION(unit)
	XPerl_Party_UpdateName(self)
        XPerl_Party_UpdateCombat(self)
	XPerl_Party_Update_Control(self)
	XPerl_Party_UpdatePVP(self)
end

XPerl_Party_Events.UNIT_FLAGS = XPerl_Party_Events.UNIT_FACTION
XPerl_Party_Events.UNIT_DYNAMIC_FLAGS = XPerl_Party_Events.UNIT_FACTION

function XPerl_Party_Events:UNIT_TARGET()
	XPerl_Party_UpdateTarget(self)
end

---- Moving stuff ----
-- XPerl_Party_GetGap
function XPerl_Party_GetGap()
	--return floor(floor((XPerl_party1:GetBottom() - XPerl_party2:GetTop() + 0.01) * 100) / 100)
	return conf.PartySpacing
end

-- XPerl_Party_SetGap
function XPerl_Party_SetGap(newGap)
	if (type(newGap) ~= "number") then
		return
	end
	conf.PartySpacing = newGap
	XPerl_Party_SetMainAttributes()
	XPerl_Party_Virtual(true)
end

-- CalcWidth
local function CalcWidth(self)
	--return XPerl_party1Highlight:GetWidth()

	local w = self.statsFrame:GetWidth()

	if (conf.ShowPartyPortrait == 1) then
		w = w + self.portraitFrame:GetWidth() - 2

		if (conf.ShowPartyLevel == 1 or conf.ShowPartyClassIcon == 1) then
			w = w + self.levelFrame:GetWidth() - 2
		end
	else
		w = w + self.levelFrame:GetWidth() - 2
	end

	return w
end

-- CalcHeight
local function CalcHeight(self)
	--return XPerl_party1Highlight:GetHeight()

	if (conf.ShowPartyPortrait == 1) then
		return self.portraitFrame:GetHeight()
	end

	local h = self.statsFrame:GetHeight()

	if (conf.ShowPartyNames == 1) then
		h = h + self.nameFrame:GetHeight() - 2
	end

	return h
end

-- XPerl_Party_Set_Bits
function XPerl_Party_Set_Bits1(self)
	self.portraitFrame:ClearAllPoints()
	self.nameFrame:ClearAllPoints()
	self.levelFrame:ClearAllPoints()
	self.statsFrame:ClearAllPoints()
	self.levelFrame.classTexture:ClearAllPoints()
	self.levelFrame.text:ClearAllPoints()
	self.highlight:ClearAllPoints()

	if (conf.ShowPartyPortrait == 0) then
		self.portraitFrame:Hide()

		self.levelFrame:SetWidth(30)
		self.levelFrame:SetHeight(41)

		self.nameFrame:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
		self.levelFrame:SetPoint("TOPLEFT", self.nameFrame, "BOTTOMLEFT", 0, 3)
		self.statsFrame:SetPoint("TOPLEFT", self.levelFrame, "TOPRIGHT", -2, 0)

		self.levelFrame.text:SetPoint("BOTTOM", self.levelFrame, "BOTTOM", 0, 4)
		self.levelFrame.classTexture:SetPoint("TOPLEFT", self.levelFrame, "TOPLEFT", 5, -5)

		self.buffFrame:SetPoint("TOPLEFT", self.statsFrame, "BOTTOMLEFT", 5, 0)

		self.highlight:SetPoint("TOPLEFT", self.nameFrame, "TOPLEFT", 0, 0)
		self.highlight:SetPoint("BOTTOMRIGHT", self.statsFrame, "BOTTOMRIGHT", 0, 0)
	else
		self.portraitFrame:Show()

		self.levelFrame:SetWidth(27)
		self.levelFrame:SetHeight(22)

		self.levelFrame:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 0)
		self.portraitFrame:SetPoint("TOPLEFT", self.levelFrame, "TOPRIGHT", -2, 0)
		self.nameFrame:SetPoint("TOPLEFT", self.portraitFrame, "TOPRIGHT", -2, 0)
		self.statsFrame:SetPoint("TOPLEFT", self.nameFrame, "BOTTOMLEFT", 0, 3)

		self.levelFrame.text:SetPoint("CENTER", 0, 0)
		self.levelFrame.classTexture:SetPoint("BOTTOMRIGHT", self.portraitFrame, "BOTTOMLEFT", 0, 3)

		self.buffFrame:SetPoint("TOPLEFT", self.portraitFrame, "BOTTOMLEFT", 5, 0)

		self.highlight:SetPoint("TOPLEFT", self.levelFrame, "TOPLEFT", 0, 0)
		self.highlight:SetPoint("BOTTOMRIGHT", self.statsFrame, "BOTTOMRIGHT", 0, 0)
	end

	if (conf.ShowPartyLevel==0) then
		self.levelFrame.text:Hide()
	else
		self.levelFrame.text:Show()
		self.levelFrame:Show()

		if (conf.ShowPartyPortrait == 1) then
			self.levelFrame:SetWidth(27)
		else
			self.levelFrame:SetWidth(30)
		end
	end

	if (conf.ShowPartyClassIcon==0) then
		self.levelFrame.classTexture:Hide()

		if (conf.ShowPartyLevel == 0) then
			self.levelFrame:SetWidth(2)
			self.levelFrame:Hide()
		end
	else
		self.levelFrame.classTexture:Show()
		self.levelFrame:Show()

		if (conf.ShowPartyPortrait == 1) then
			self.levelFrame:SetWidth(27)
		else
			self.levelFrame:SetWidth(30)
		end
	end

	local width = (36 * conf.ShowPartyPercent) + 106;	-- 136 enabled, 106 disabled

	self.statsFrame:SetWidth(width)
	if (conf.ShowPartyPercent == 0) then
		self.statsFrame.healthBar.percent:Hide()
		self.statsFrame.manaBar.percent:Hide()
	else
		self.statsFrame.healthBar.percent:Show()
		self.statsFrame.manaBar.percent:Show()
	end

	local height = (conf.ShowPartyNames * 22) + 2;		-- 24 when enabled, 2 when disabled

	self.targetFrame:ClearAllPoints()
	self.nameFrame:SetHeight(height)

	if (conf.ShowPartyNames==0) then
		self.nameFrame:Hide()
		self.targetFrame:SetPoint("TOPLEFT", self.statsFrame, "BOTTOMLEFT", -2, 0)
	else
		self.nameFrame:Show()
		self.targetFrame:SetPoint("BOTTOMLEFT", self.nameFrame, "BOTTOMRIGHT", -2, 0)
	end

	if (conf.PartyTargetLarge == 1) then
		self.targetFrame.healthBar.text:SetTextColor(1, 1, 1)
		self.targetFrame:SetHeight(28)
		self.targetFrame.healthBar:Show()
	else
		self.targetFrame:SetHeight(20)
		self.targetFrame.healthBar:Hide()
	end

	self.targetFrame:SetWidth(conf.PartyTargetSize)

	conf.PartyBuffSize = tonumber(conf.PartyBuffSize) or 20
	XPerl_SetBuffSize(self)

	self:SetWidth(CalcWidth(self))

	if (conf.ShowPartyTarget == 1) then
		RegisterUnitWatch(self.targetFrame)
	else
		UnregisterUnitWatch(self.targetFrame)
		self.targetFrame:Hide()
	end

	XPerl_StatsFrameSetup(self)

	if (self:IsShown()) then
		XPerl_Party_UpdateDisplay(self)
	end
end

-- XPerl_Party_SetInitialAttributes()
function XPerl_Party_SetInitialAttributes()
	XPerl_Party_SecureHeader:Hide()
	XPerl_Party_SecureHeader:SetAttribute("minWidth", 80)
	XPerl_Party_SecureHeader:SetAttribute("minHeight", 10)
	XPerl_Party_SecureHeader:SetAttribute("xOffset", 0)
	XPerl_Party_SecureHeader:SetAttribute("template", "XPerl_Party_FrameTemplate")
	XPerl_Party_SecureHeader:SetAttribute("templateType", "Button")
	XPerl_Party_SetMainAttributes()
	CheckRaid()
end

-- XPerl_Party_SetMainAttributes
function XPerl_Party_SetMainAttributes()

	XPerl_Party_Anchor:StopMovingOrSizing()

	XPerl_Party_SecureHeader:ClearAllPoints()
	if (conf.PartyAnchor == "TOP") then
		XPerl_Party_SecureHeader:SetPoint("TOPLEFT", XPerl_Party_Anchor, "TOPLEFT", 0, 0)
        	XPerl_Party_SecureHeader:SetAttribute("xOffset", 0)
		XPerl_Party_SecureHeader:SetAttribute("yOffset", -conf.PartySpacing)
	elseif (conf.PartyAnchor == "LEFT") then
		XPerl_Party_SecureHeader:SetPoint("TOPLEFT", XPerl_Party_Anchor, "TOPLEFT", 0, 0)
		XPerl_Party_SecureHeader:SetAttribute("xOffset", conf.PartySpacing)
        	XPerl_Party_SecureHeader:SetAttribute("yOffset", 0)
	elseif (conf.PartyAnchor == "BOTTOM") then
		XPerl_Party_SecureHeader:SetPoint("BOTTOMLEFT", XPerl_Party_Anchor, "BOTTOMLEFT", 0, 0)
        	XPerl_Party_SecureHeader:SetAttribute("xOffset", 0)
		XPerl_Party_SecureHeader:SetAttribute("yOffset", conf.PartySpacing)
	elseif (conf.PartyAnchor == "RIGHT") then
		XPerl_Party_SecureHeader:SetPoint("BOTTOMRIGHT", XPerl_Party_Anchor, "BOTTOMRIGHT", 0, 0)
		XPerl_Party_SecureHeader:SetAttribute("xOffset", -conf.PartySpacing)
        	XPerl_Party_SecureHeader:SetAttribute("yOffset", 0)
	end

        XPerl_Party_SecureHeader:SetAttribute("point", conf.PartyAnchor or "TOP")
end

-- XPerl_Party_Virtual
function XPerl_Party_Virtual(on)
	if (on) then
		local w = CalcWidth(XPerl_party1)
		local h = CalcHeight(XPerl_party1)

		XPerl_Party_AnchorVirtual:ClearAllPoints()
		if (conf.PartyAnchor == "TOP") then
			XPerl_Party_AnchorVirtual:SetPoint("TOPLEFT", XPerl_Party_Anchor, "TOPLEFT", 0, 0)
			XPerl_Party_AnchorVirtual:SetHeight((h * 4) + (conf.PartySpacing * 3))
			XPerl_Party_AnchorVirtual:SetWidth(w)

		elseif (conf.PartyAnchor == "LEFT") then
			XPerl_Party_AnchorVirtual:SetPoint("TOPLEFT", XPerl_Party_Anchor, "TOPLEFT", 0, 0)
			XPerl_Party_AnchorVirtual:SetHeight(h)
			XPerl_Party_AnchorVirtual:SetWidth(w * 4 + (conf.PartySpacing * 3))

		elseif (conf.PartyAnchor == "BOTTOM") then
			XPerl_Party_AnchorVirtual:SetPoint("BOTTOMLEFT", XPerl_Party_Anchor, "BOTTOMLEFT", 0, 0)
			XPerl_Party_AnchorVirtual:SetHeight((h * 4) + (conf.PartySpacing * 3))
			XPerl_Party_AnchorVirtual:SetWidth(w)

		elseif (conf.PartyAnchor == "RIGHT") then
			XPerl_Party_AnchorVirtual:SetPoint("TOPRIGHT", XPerl_Party_Anchor, "TOPRIGHT", 0, 0)
			XPerl_Party_AnchorVirtual:SetHeight(h)
			XPerl_Party_AnchorVirtual:SetWidth(w * 4 + (conf.PartySpacing * 3))
		end

		XPerl_Party_AnchorVirtual:SetBackdropColor(conf.BackColour.r, conf.BackColour.g, conf.BackColour.b, conf.BackColour.a)
		XPerl_Party_AnchorVirtual:SetBackdropBorderColor(conf.BorderColour.r, conf.BorderColour.g, conf.BorderColour.b, 1)
		XPerl_Party_AnchorVirtual:Lower()
		XPerl_Party_AnchorVirtual:Show()
	else
		XPerl_Party_AnchorVirtual:Hide()
	end
end

-- XPerl_Party_Set_Bits
function XPerl_Party_Set_Bits()
	if (InCombatLockdown()) then
		tinsert(XPerl_OutOfCombatQueue, XPerl_Party_Set_Bits)
		return
	end

	for k,v in pairs(PartyFrames) do
		XPerl_Party_Set_Bits1(v)
	end

	XPerl_Party_SecureHeader:Hide()
	XPerl_Party_SetMainAttributes()
	CheckRaid()

	if (XPerl_Party_AnchorVirtual:IsShown()) then
		XPerl_Party_Virtual(true)
	end

	XPerl_Party_Anchor:SetScale(conf.Scale_PartyFrame)
end
