local ccRemoverFrameInitialAnchor =
{
	point = "TOPLEFT";
	relativePoint = "TOPRIGHT";
	xOffset = 2;
	yOffset = -1;
}

local debuffFrameInitialAnchor =
{
	point = "RIGHT";
	relativePoint = "LEFT";
	xOffset = -3;
	yOffset = 0;
}

local useClassColorsCvarName = "pvpFramesDisplayClassColor";
CVarCallbackRegistry:SetCVarCachable(useClassColorsCvarName);

local spellDiminishEnemiesCvarName = "spellDiminishPVPEnemiesEnabled";
CVarCallbackRegistry:SetCVarCachable(spellDiminishEnemiesCvarName);

local function GetUseClassColors()
	return CVarCallbackRegistry:GetCVarValueBool(useClassColorsCvarName);
end

local function GetArenaSize()
	-- Use opponent specs first since we know those before the match has started
	local numOpponentSpecs = GetNumArenaOpponentSpecs();
	if numOpponentSpecs and numOpponentSpecs > 0 then
		return numOpponentSpecs;
	end

	-- If we don't know opponent specs, we're probably in an arena which doesn't have a set size
	-- In this case base it on whoever happens to be in the arena
	-- Note we won't know this until the match actually starts
	local numOpponents = GetNumArenaOpponents();
	if numOpponents and numOpponents > 0 then
		return numOpponents;
	end

	return 0;
end

local function IsMatchEngaged()
	return C_PvP.GetActiveMatchState() == Enum.PvPMatchState.Engaged;
end

local function IsInArena()
	return C_PvP.IsMatchConsideredArena() and (C_PvP.IsMatchActive() or C_PvP.IsMatchComplete());
end

local function GetUnitToken(unitIndex)
	return "arena"..unitIndex;
end

local function GetPetUnitToken(unitIndex)
	return "arenapet"..unitIndex;
end

local function SetRoleIconTexture(texture, role)
	if role and (role == "TANK" or role == "HEALER" or role == "DAMAGER") then
		texture:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES");
		texture:SetTexCoord(GetTexCoordsForOldRoleSmallCircle(role));
		texture:Show();
		texture:SetSize(12, 12);
	else
		texture:Hide();
		texture:SetSize(1, 12);
	end
end

local function SetFrameBarColor(barTexture, class)
	local r, g, b = 1.0, 0.0, 0.0;
	if GetUseClassColors() and class then
		local classColor = RAID_CLASS_COLORS[class];
		r, g, b = classColor.r, classColor.g, classColor.b;
	end
	barTexture:SetVertexColor(r, g, b);
end

function CompactArenaFrame_Generate()
	local frame = CompactArenaFrame;
	local didCreate = false;
	if not frame then
		frame = CreateFrame("Frame", "CompactArenaFrame", UIParent, "CompactArenaFrameTemplate");
		frame:RegisterEvent("ARENA_OPPONENT_UPDATE");
		frame:RegisterEvent("ARENA_PREP_OPPONENT_SPECIALIZATIONS");
		frame:RegisterEvent("PVP_MATCH_STATE_CHANGED");
		didCreate = true;
	end
	return frame, didCreate;
end

CompactArenaFrameMixin = CreateFromMixins(CompactPartyFrameMixin);

function CompactArenaFrameMixin:OnLoad()
	CompactPartyFrameMixin.OnLoad(self);
	self.updateLayoutFunc = self.UpdateLayout;

	for i, memberUnitFrame in ipairs(self.memberUnitFrames) do
		memberUnitFrame.frameIndex = i;
		CompactUnitFrame_SubscribeToVisibilityChanged(memberUnitFrame, self,
			function(subscribingFrame, unitFrame)
				-- If a frame stopped showing but we're in edit mode and the frame should be shown then RefreshMembers to make it show again by putting a dummy unit into it
				if not unitFrame:IsShown() and self.isInEditMode and unitFrame.frameIndex <= EditModeManagerFrame:GetNumArenaFramesForcedShown() then
					self:RefreshMembers();
				end
			end);

		-- Arena frames set focus when right clicked rather than opening a dropdown menu
		memberUnitFrame:SetAttribute("*type2", "focus");

		-- Create CcRemover frame
		local ccRemoverFrame = CreateFrame("Frame", nil, memberUnitFrame, "ArenaUnitFrameCcRemoverTemplate");
		memberUnitFrame.CcRemoverFrame = ccRemoverFrame;
		ccRemoverFrame:SetPoint(ccRemoverFrameInitialAnchor.point, memberUnitFrame, ccRemoverFrameInitialAnchor.relativePoint, ccRemoverFrameInitialAnchor.xOffset, ccRemoverFrameInitialAnchor.yOffset);

		-- Create debuff frame
		local debuffFrame = CreateFrame("Frame", nil, memberUnitFrame, "ArenaUnitFrameDebuffTemplate");
		memberUnitFrame.DebuffFrame = debuffFrame;
		debuffFrame:SetPoint(debuffFrameInitialAnchor.point, memberUnitFrame, debuffFrameInitialAnchor.relativePoint, debuffFrameInitialAnchor.xOffset, debuffFrameInitialAnchor.yOffset);

		-- Create casting bar
		local castingBarFrame = CreateFrame("StatusBar", nil, memberUnitFrame, "ArenaUnitFrameCastingBarTemplate");
		memberUnitFrame.CastingBarFrame = castingBarFrame;
		castingBarFrame:SetPoint("TOPRIGHT", debuffFrame, "LEFT", -5, -5);

		-- Create Spell Diminish status tray
		local spellDiminishStatusTray = CreateFrame("Frame", nil, memberUnitFrame, "SpellDiminishStatusTrayTemplate");
		memberUnitFrame.SpellDiminishStatusTray = spellDiminishStatusTray;
		spellDiminishStatusTray:SetPoint("BOTTOMRIGHT", memberUnitFrame.CastingBarFrame, "TOPRIGHT", 0, 2);


		-- Create stealthed unit frames
		local stealthedUnitFrame = CreateFrame("Frame", nil, self, "StealthedArenaUnitFrameTemplate");
		self["StealthedUnitFrame"..i] = stealthedUnitFrame;
		stealthedUnitFrame:SetPoint("TOPLEFT", memberUnitFrame, "TOPLEFT");
		stealthedUnitFrame:SetPoint("BOTTOMRIGHT", memberUnitFrame, "BOTTOMRIGHT");
	end

	self:RefreshMembers();
	EditModeSystemMixin.OnSystemLoad(self);

	CVarCallbackRegistry:RegisterCallback(spellDiminishEnemiesCvarName, self.OnSpellDiminishEnemiesCVarChanged, self);
end

function CompactArenaFrameMixin:UpdateLayout()
	local arenaSize = GetArenaSize();
	self.minUnitFrames = arenaSize > 0 and arenaSize or 1;

	CompactPartyFrameMixin.UpdateLayout(self);

	local firstMemberUnitFrame = self.memberUnitFrames[1];
	local frameBorderOffset = self.borderFrame:IsShown() and 4 or 0;
	local unitFrameXOffset = -frameBorderOffset;

	-- Anchor title to top of first member unit frame
	local title = self.title;
	title:ClearAllPoints();
	title:SetPoint("BOTTOM", firstMemberUnitFrame, "TOP");

	local width, height = self:GetSize();


	-- Keep track of how many frames we're showing to calculate the height later
	local totalMemberFramesShown = 0;
	for i, memberUnitFrame in ipairs(self.memberUnitFrames) do
		if memberUnitFrame:IsShown() then
			totalMemberFramesShown = totalMemberFramesShown + 1;
		end

		local ccRemoverFrame = memberUnitFrame.CcRemoverFrame;
		local spellDiminishStatusTray = memberUnitFrame.SpellDiminishStatusTray;
		local debuffFrame = memberUnitFrame.DebuffFrame;
		local castingBarFrame = memberUnitFrame.CastingBarFrame;

		if ccRemoverFrame and debuffFrame and castingBarFrame and spellDiminishStatusTray then

			-- Adjust ccRemoverFrame anchor to account for the frame border
			local ccRemoverFrameXOffset = ccRemoverFrameInitialAnchor.xOffset + frameBorderOffset;
			ccRemoverFrame:ClearAllPoints();
			ccRemoverFrame:SetPoint(ccRemoverFrameInitialAnchor.point, memberUnitFrame, ccRemoverFrameInitialAnchor.relativePoint, ccRemoverFrameXOffset, ccRemoverFrameInitialAnchor.yOffset);

			-- AdjustTimeByDays debuff frame anchor to account for the frame border
			local debuffFrameXOffset = debuffFrameInitialAnchor.xOffset - frameBorderOffset;
			debuffFrame:ClearAllPoints();
			debuffFrame:SetPoint(debuffFrameInitialAnchor.point, memberUnitFrame, debuffFrameInitialAnchor.relativePoint, debuffFrameXOffset, debuffFrameInitialAnchor.yOffset);

			-- Adjust frame width and height to fit various frames
			-- Only need to adjust if this is the first frame since all subsequent frames will be the same size/layout
			local isFirstMemberFrame = i == 1;
			if isFirstMemberFrame then
				local ccRemoverWidth = ccRemoverFrame:GetWidth() + math.abs(ccRemoverFrameXOffset) - frameBorderOffset;
				local debuffFrameWidth = debuffFrame:GetWidth() + math.abs(debuffFrameXOffset) - frameBorderOffset;
				local castingBarXOffset = math.abs(select(4, castingBarFrame:GetPoint(1)));
				local castingBarFrameWidth = castingBarFrame:GetWidth() + castingBarFrame.BorderShield:GetWidth() + castingBarXOffset;

				width = width + ccRemoverWidth + debuffFrameWidth + castingBarFrameWidth;
				unitFrameXOffset = unitFrameXOffset - ccRemoverWidth;
			end
		end
	end

	-- Arena Frames are anchored under each other with an offset
	-- We take these offsets into account to calculate our final height
	if totalMemberFramesShown > 1 then
		-- Use the second frame for reference since the rest should be anchored with the same offset
		local _point, relativeTo, _relativePoint, _xOffset, yOffset = self.memberUnitFrames[2]:GetPoint(1);
		if yOffset then
			local arenaFrameVerticalOffsetsCombined = math.abs(yOffset) * (totalMemberFramesShown - 1);
			height = height + arenaFrameVerticalOffsetsCombined;
		end
	end
	-- Let's also add a little padding for the edit mode border
	local heightPadding = 7;
	height = height + heightPadding;

	firstMemberUnitFrame:ClearAllPoints();
	firstMemberUnitFrame:SetPoint("TOPRIGHT", self, "TOPRIGHT", unitFrameXOffset, -14);
	self:SetSize(width, height);

	UIParent_ManageFramePositions();
end

function CompactArenaFrameMixin:UpdateVisibility()
	self:SetShown(IsInArena() or EditModeManagerFrame:GetNumArenaFramesForcedShown() > 0);
end

function CompactArenaFrameMixin:OnSpellDiminishEnemiesCVarChanged()
	for _index, memberUnitFrame in ipairs(self.memberUnitFrames) do
		if memberUnitFrame.SpellDiminishStatusTray then
			memberUnitFrame.SpellDiminishStatusTray:UpdateShownState();
		end
	end
end

function CompactArenaFrameMixin:RefreshMembers()
	-- Add player units
	local numEditModeForcedShownArenaFrames = EditModeManagerFrame:GetNumArenaFramesForcedShown();
	for i, memberUnitFrame in ipairs(self.memberUnitFrames) do
		memberUnitFrame.unitIndex = i;
		memberUnitFrame.unitToken = GetUnitToken(memberUnitFrame.unitIndex);
		local usePlayerOverride = i <= numEditModeForcedShownArenaFrames and not ArenaUtil.UnitExists(memberUnitFrame.unitToken);
		memberUnitFrame.unitToken = usePlayerOverride and "player" or memberUnitFrame.unitToken;

		CompactUnitFrame_SetUnit(memberUnitFrame, memberUnitFrame.unitToken);
		CompactUnitFrame_SetUpFrame(memberUnitFrame, DefaultCompactUnitFrameSetup);
		CompactUnitFrame_SetUpdateAllEvent(memberUnitFrame, "ARENA_OPPONENT_UPDATE");

		if memberUnitFrame.CastingBarFrame then
			local showTradeSkillsNo, showShieldYes = false, true;
			memberUnitFrame.CastingBarFrame:SetUnit(memberUnitFrame.unitToken, showTradeSkillsNo, showShieldYes);
		end

		if memberUnitFrame.CcRemoverFrame then
			memberUnitFrame.CcRemoverFrame:SetUnit(memberUnitFrame.unitToken);
		end

		if memberUnitFrame.DebuffFrame then
			memberUnitFrame.DebuffFrame:SetUnit(memberUnitFrame.unitToken);
		end

		if memberUnitFrame.SpellDiminishStatusTray then
			local spellDiminishStatusTray = memberUnitFrame.SpellDiminishStatusTray;
			spellDiminishStatusTray:SetUnit(memberUnitFrame.unitToken);
		end

		local stealthedUnitFrame = self.stealthedUnitFrames and self.stealthedUnitFrames[i];
		if stealthedUnitFrame then
			stealthedUnitFrame:SetUnitFrame(memberUnitFrame);
		end
	end

	-- Add pet units if we're set to display pets
	-- Pets should always appear at the bottom under the real players
	-- Pets order should match the units order
	for i, petUnitFrame in ipairs(self.petUnitFrames) do
		local petUnitToken = CompactRaidFrameContainer.pvpDisplayPets and GetPetUnitToken(i) or nil;

		CompactUnitFrame_SetUpFrame(petUnitFrame, DefaultCompactMiniFrameSetup);
		CompactUnitFrame_SetUnit(petUnitFrame, petUnitToken);
		CompactUnitFrame_SetUpdateAllEvent(petUnitFrame, "ARENA_OPPONENT_UPDATE");
		CompactUnitFrame_SetUpdateAllEvent(petUnitFrame, "UNIT_PET");
	end

	self:UpdateLayout();
	self:UpdateVisibility();
	self.PreMatchFramesContainer:UpdateUnitFrames();
end

ArenaPreMatchFramesContainerMixin = {};

function ArenaPreMatchFramesContainerMixin:OnLoad()
	-- Create pre-match unit frame
	local memberUnitFrames = CompactArenaFrame.memberUnitFrames;
	for i, memberUnitFrame in ipairs(memberUnitFrames) do
		local preMatchFrame = CreateFrame("Frame", nil, self, "PreMatchArenaUnitFrameTemplate");
		self["PreMatchFrame"..i] = preMatchFrame;
		preMatchFrame:SetPoint("TOPLEFT", memberUnitFrame, "TOPLEFT");
		preMatchFrame:SetPoint("BOTTOMRIGHT", memberUnitFrame, "BOTTOMRIGHT");
	end

	CVarCallbackRegistry:RegisterCallback(useClassColorsCvarName, self.OnUseClassColorsChanged, self);

	self:UpdateUnitFrames();
end

function ArenaPreMatchFramesContainerMixin:OnUseClassColorsChanged()
	self:UpdateUnitFrames();
end

function ArenaPreMatchFramesContainerMixin:SetIsInEditMode(isInEditMode)
	self.isInEditMode = isInEditMode;
	self:UpdateShownState();
end

function ArenaPreMatchFramesContainerMixin:UpdateShownState()
	self:SetShown(IsInArena() and not IsMatchEngaged() and not C_PvP.IsMatchComplete() and not self.isInEditMode);
end

function ArenaPreMatchFramesContainerMixin:UpdateUnitFrames()
	-- update pre match unit frames
	for i, preMatchUnitFrame in ipairs(self.preMatchUnitFrames) do
		preMatchUnitFrame:Update(i);
	end

	self:UpdateShownState();
end

PreMatchArenaUnitFrameMixin = {};

function PreMatchArenaUnitFrameMixin:Update(index)
	local specID, gender = GetArenaOpponentSpec(index);
	if specID and specID > 0 then
		local _, specName, _, specIcon, role, class, className = GetSpecializationInfoByID(specID, gender);

		self.SpecNameText:SetText(specName);
		self.ClassNameText:SetText(className);

		self.SpecPortraitTexture:SetTexture(specIcon);
		SetRoleIconTexture(self.RoleIconTexture , role);
		SetFrameBarColor(self.BarTexture, class);

		self:Show();
	else
		self:Hide();
	end
end

ArenaUnitFrameCcRemoverMixin = {};

local ccRemoverAlwaysEvents =
{
	"ARENA_CROWD_CONTROL_SPELL_UPDATE"
};

local ccRemoverShownEvents =
{
	"ARENA_COOLDOWNS_UPDATE"
};

function ArenaUnitFrameCcRemoverMixin:OnLoad()
	local seconds = 60;
	self.Cooldown:SetCountdownAbbrevThreshold(seconds);
	self.Cooldown:SetCountdownFont("GameFontHighlightSmallOutline");
end

function ArenaUnitFrameCcRemoverMixin:OnEvent(event, ...)
	if event == "ARENA_COOLDOWNS_UPDATE" then
		self:UpdateCooldown();
	elseif event == "ARENA_CROWD_CONTROL_SPELL_UPDATE" then
		local unitToken, spellId = ...;
		self:SetSpellId(spellId);
		self:UpdateCooldown();
	end
end

function ArenaUnitFrameCcRemoverMixin:OnShow()
	if self.unitToken then
		FrameUtil.RegisterFrameForEvents(self, ccRemoverShownEvents);
	end

	self:UpdateCooldown();
end

function ArenaUnitFrameCcRemoverMixin:OnHide()
	FrameUtil.UnregisterFrameForEvents(self, ccRemoverShownEvents);
end

function ArenaUnitFrameCcRemoverMixin:UpdateCooldown()
	local spellId, startTimeMs, durationMs = C_PvP.GetArenaCrowdControlInfo(self.unitToken);
	local startTime = startTimeMs and (startTimeMs / 1000) or 0;
	local duration = durationMs and (durationMs / 1000) or 0;
	local enabled = spellId and duration > 0;
	local forceShowDrawEdge = true;
	CooldownFrame_Set(self.Cooldown, startTime, duration, enabled, forceShowDrawEdge);
end

function ArenaUnitFrameCcRemoverMixin:SetSpellId(spellId)
	local newSpellId = (spellId and spellId > 0) and spellId or nil;
	if self.spellId == newSpellId then
		return;
	end

	local texture = newSpellId and C_Spell.GetSpellTexture(newSpellId) or QUESTION_MARK_ICON;
	self.Icon:SetTexture(texture);
	self.spellId = newSpellId;
	self:UpdateShownState();
end

function ArenaUnitFrameCcRemoverMixin:SetUnit(unitToken)
	self.unitToken = unitToken;

	FrameUtil.UnregisterFrameForEvents(self, ccRemoverAlwaysEvents);
	FrameUtil.UnregisterFrameForEvents(self, ccRemoverShownEvents);

	if self.unitToken then
		FrameUtil.RegisterFrameForUnitEvents(self, ccRemoverAlwaysEvents, self.unitToken);
		if self:IsShown() then
			FrameUtil.RegisterFrameForEvents(self, ccRemoverShownEvents);
		end
	end

	if ArenaUtil.UnitExists(self.unitToken) then
		C_PvP.RequestCrowdControlSpell(self.unitToken);
	else
		self:SetSpellId(nil);
		self:UpdateCooldown();
	end
end

function ArenaUnitFrameCcRemoverMixin:SetIsInEditMode(isInEditMode)
	self.isInEditMode = isInEditMode;
	self:UpdateShownState();
end

function ArenaUnitFrameCcRemoverMixin:UpdateShownState()
	self:SetShown(self.isInEditMode or self.spellId);
end

ArenaUnitFrameDebuffMixin = {};

local arenaUnitFrameDebuffEvents = {
	"LOSS_OF_CONTROL_UPDATE",
	"LOSS_OF_CONTROL_ADDED",
};

function ArenaUnitFrameDebuffMixin:OnLoad()
	self.Cooldown:SetUseAuraDisplayTime(true);
	self.Cooldown:SetCountdownFont("GameFontHighlightOutline");
end

function ArenaUnitFrameDebuffMixin:OnEvent(event, ...)
	if event == "LOSS_OF_CONTROL_UPDATE" then
		self:Update();
	elseif event == "LOSS_OF_CONTROL_ADDED" then
		self:Update();
	end
end

function ArenaUnitFrameDebuffMixin:OnEnter()
	if not self.auraData or not self.auraData.auraInstanceID then
		return;
	end

	GameTooltip:SetOwner(self, "ANCHOR_LEFT", 0, 0);
	self:UpdateTooltip();
end

function ArenaUnitFrameDebuffMixin:OnLeave()
	if GameTooltip:IsOwned(self) then
		GameTooltip:Hide();
	end
end

function ArenaUnitFrameDebuffMixin:UpdateTooltip()
	if not GameTooltip:IsOwned(self) then
		return;
	end

	GameTooltip:SetUnitDebuffByAuraInstanceID(self.unitToken, self.auraData.auraInstanceID, nil);
end

function ArenaUnitFrameDebuffMixin:SetUnit(unitToken)
	if unitToken == self.unitToken then
		return;
	end

	FrameUtil.UnregisterFrameForEvents(self, arenaUnitFrameDebuffEvents);

	self.unitToken = unitToken;
	if self.unitToken then
		FrameUtil.RegisterFrameForUnitEvents(self, arenaUnitFrameDebuffEvents, self.unitToken);
	end

	self:Update();
end

-- TODO: I don't think this will work any more and it needs to be rewritten so that the loss of control stuff is managed in the secure env
-- There's a chance that this could still work in the interim...needs investigation.
function ArenaUnitFrameDebuffMixin:Update()
	local lossOfControlData;
	if ArenaUtil.UnitExists(self.unitToken) then
		for i = 1, C_LossOfControl.GetActiveLossOfControlDataCountByUnit(self.unitToken) do
			local data = C_LossOfControl.GetActiveLossOfControlDataByUnit(self.unitToken, i);
			if (lossOfControlData == nil) or (data and data.priority > lossOfControlData.priority) then
				lossOfControlData = data;
			end
		end
	end

	if lossOfControlData and lossOfControlData.auraInstanceID then
		self.auraData = C_UnitAuras.GetAuraDataByAuraInstanceID(self.unitToken, lossOfControlData.auraInstanceID);
	else
		self.auraData = nil;
	end

	local unitFrame = self:GetParent();
	C_UnitAuras.ClearBlockedAuras(self.unitToken);

	if self.auraData then
		C_UnitAuras.AddBlockedAura(self.unitToken, self.auraData.auraInstanceID);

		self.Icon:SetTexture(self.auraData.icon);

		local enabled = self.auraData.duration > 0;
		local forceShowDrawEdge = true;
		CooldownFrame_Set(self.Cooldown, self.auraData.expirationTime - self.auraData.duration, self.auraData.duration, enabled, forceShowDrawEdge);
	else
		self.Icon:SetTexture(QUESTION_MARK_ICON);
	end

	-- TODO: Previous code here was to call ComactUnitFrame_UpdateAuras which I think served the purpose of filtering out the loss-of-control debuff from the CUF display.
	local forceUpdatePrivateAuras = true;
	unitFrame:UpdatePrivateAuras(forceUpdatePrivateAuras);

	self:UpdateShownState();
	self:UpdateTooltip();
end

function ArenaUnitFrameDebuffMixin:SetIsInEditMode(isInEditMode)
	self.isInEditMode = isInEditMode;
	self:UpdateShownState();
end

function ArenaUnitFrameDebuffMixin:UpdateShownState()
	self:SetShown(self.auraData or self.isInEditMode)
end

StealthedArenaUnitFrameMixin = {};

function StealthedArenaUnitFrameMixin:SetUnitFrame(unitFrame)
	if self.unitFrame then
		CompactUnitFrame_UnsubscribeToVisibilityChanged(self.unitFrame, self);
	end

	self.unitFrame = unitFrame;

	if not self:HasValidUnitFrame() then
		self:Hide();
		return;
	end

	CompactUnitFrame_SubscribeToVisibilityChanged(self.unitFrame, self, self.UpdateShownState);

	local unitClassInfo = self:GetUnitClassInfo();
	SetRoleIconTexture(self.RoleIconTexture , unitClassInfo.role);
	SetFrameBarColor(self.BarTexture, unitClassInfo.class);
	self:UpdateName(unitClassInfo);
	self:UpdateShownState();
end

function StealthedArenaUnitFrameMixin:HasValidUnitFrame()
	return self.unitFrame and self.unitFrame.unitToken and self.unitFrame.unitIndex;
end

function StealthedArenaUnitFrameMixin:GetUnitClassInfo()
	local unitClassInfo = {
		role = nil;
		class = nil;
		specName = nil;
		className = nil,
	};
	if self.unitFrame and self.unitFrame.unitIndex then
		local specID, gender = GetArenaOpponentSpec(self.unitFrame.unitIndex);
		if specID and specID > 0 then
			local _;
			_, unitClassInfo.specName, _, _, unitClassInfo.role, unitClassInfo.class, unitClassInfo.className = GetSpecializationInfoByID(specID, gender);
		end
	end

	return unitClassInfo;
end

function StealthedArenaUnitFrameMixin:UpdateName(unitClassInfo)
	local name;
	if self.unitFrame and self.unitFrame.unitToken then
		name = GetUnitName(self.unitFrame.unitToken);
	end
	self.NameText:SetText(name or unitClassInfo.specName or unitClassInfo.className or "");
end

function StealthedArenaUnitFrameMixin:UpdateShownState()
	if not self:HasValidUnitFrame() or self.unitFrame:IsShown() then
		self:Hide();
		return;
	end

	local isInEditMode = EditModeManagerFrame:IsEditModeActive();
	local isInActiveArena = IsInArena() and (IsMatchEngaged() or C_PvP.IsMatchComplete());
	local shouldUnitExist = self.unitFrame.unitIndex <= GetArenaSize();

	self:SetShown(not isInEditMode and isInActiveArena and shouldUnitExist);
end
