-------------------------------------------------------
----------Constants
-------------------------------------------------------
MAX_LFG_LIST_APPLICATIONS = 5
MAX_LFG_LIST_SEARCH_AUTOCOMPLETE_ENTRIES = 6
MAX_LFG_LIST_GROUP_DROPDOWN_ENTRIES = 10
LFG_LIST_DELISTED_FONT_COLOR = { r = 0.3, g = 0.3, b = 0.3 }
LFG_LIST_COMMENT_FONT_COLOR = { r = 0.6, g = 0.6, b = 0.6 }

ACTIVITY_RETURN_VALUES = {
	fullName = 1,
	shortName = 2,
	categoryID = 3,
	groupID = 4,
	itemLevel = 5,
	filters = 6,
	minLevel = 7,
	maxPlayers = 8,
	displayType = 9,
	orderIndex = 10,
	useHonorLevel = 11,
}

--Hard-coded values. Should probably make these part of the DB, but it gets a little more complicated with the per-expansion textures
LFG_LIST_CATEGORY_TEXTURES = {
	[1] = "questing",
	[2] = "dungeons",
	[3] = "raids", --Prefix for expansion
	[4] = "arenas",
	[5] = "scenarios",
	[6] = "custom", -- Prefix for "-pve" or "-pvp"
	[7] = "skirmishes",
	[8] = "battlegrounds",
	[9] = "ratedbgs",
	[10] = "ashran",
}

LFG_LIST_PER_EXPANSION_TEXTURES = {
	[0] = "classic",
	[1] = "bc",
	[2] = "wrath",
	[3] = "cataclysm",
	[4] = "mists",
	[5] = "warlords",
	[6] = "legion",
}

LFG_LIST_GROUP_DATA_ATLASES = {
	--Roles
	TANK = "groupfinder-icon-role-large-tank",
	HEALER = "groupfinder-icon-role-large-heal",
	DAMAGER = "groupfinder-icon-role-large-dps",
}

--Fill out classes
for i = 1, #CLASS_SORT_ORDER do
	LFG_LIST_GROUP_DATA_ATLASES[CLASS_SORT_ORDER[i]] = "groupfinder-icon-class-" .. string.lower(CLASS_SORT_ORDER[i])
end

LFG_LIST_GROUP_DATA_ROLE_ORDER = { "TANK", "HEALER", "DAMAGER" }
LFG_LIST_GROUP_DATA_CLASS_ORDER = CLASS_SORT_ORDER

StaticPopupDialogs["LFG_LIST_INVITING_CONVERT_TO_RAID"] = {
	text = LFG_LIST_CONVERT_TO_RAID_WARNING,
	button1 = INVITE,
	button2 = CANCEL,
	OnAccept = function(self, applicantID)
		ConvertToRaid()
		C_LFGList.InviteApplicant(applicantID)
	end,
	timeout = 0,
	whileDead = 1,
	hideOnEscape = 1,
}

-------------------------------------------------------
----------Base Frame
-------------------------------------------------------
LFG_LIST_EDIT_BOX_TAB_CATEGORIES = {}
function LFGListFrame_OnLoad(self)
	self:RegisterEvent("PARTY_LEADER_CHANGED")
	self:RegisterEvent("GROUP_ROSTER_UPDATE")
	self:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED")
	self:RegisterEvent("PLAYER_ROLES_ASSIGNED")
	self:RegisterEvent("LFG_LIST_AVAILABILITY_UPDATE")
	self:RegisterEvent("LFG_LIST_ACTIVE_ENTRY_UPDATE")
	self:RegisterEvent("LFG_LIST_ENTRY_CREATION_FAILED")
	self:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED")
	self:RegisterEvent("LFG_LIST_SEARCH_RESULT_UPDATED")
	self:RegisterEvent("LFG_LIST_SEARCH_FAILED")
	self:RegisterEvent("LFG_LIST_APPLICANT_LIST_UPDATED")
	self:RegisterEvent("LFG_LIST_APPLICANT_UPDATED")
	self:RegisterEvent("LFG_LIST_ENTRY_EXPIRED_TOO_MANY_PLAYERS")
	self:RegisterEvent("LFG_LIST_ENTRY_EXPIRED_TIMEOUT")
	self:RegisterEvent("LFG_LIST_APPLICATION_STATUS_UPDATED")
	self:RegisterEvent("VARIABLES_LOADED")
	self:RegisterEvent("ADDON_LOADED")
	self:RegisterEvent("UNIT_CONNECTION")
	for i = 1, #LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS do
		self:RegisterEvent(LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS[i])
	end
	LFGListFrame_SetBaseFilters(self, LE_LFG_LIST_FILTER_PVE)
	LFGListFrame_SetActivePanel(self, self.NothingAvailable)

	self.EventsInBackground = {
		LFG_LIST_SEARCH_FAILED = { self.SearchPanel },
	}
end

function LFGListFrame_OnEvent(self, event, ...)
	if event == "LFG_LIST_AVAILABILITY_UPDATE" then
		LFGListFrame_FixPanelValid(self)
	elseif event == "LFG_LIST_ACTIVE_ENTRY_UPDATE" then
		local createdNew = ...
		LFGListFrame_FixPanelValid(self) --If our current panel isn't valid, change it.
		if C_LFGList.GetActiveEntryInfo() then self.EntryCreation.WorkingCover:Hide() end
		if createdNew then PlaySound("PVPEnterQueue") end
	elseif event == "LFG_LIST_ENTRY_CREATION_FAILED" then
		self.EntryCreation.WorkingCover:Hide()
	elseif event == "LFG_LIST_APPLICANT_LIST_UPDATED" then
		local hasNewPending, hasNewPendingWithData = ...
		if hasNewPending and hasNewPendingWithData and LFGListUtil_IsEntryEmpowered() then
			local isLeader = UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME)
			local autoAccept = select(9, C_LFGList.GetActiveEntryInfo())
			local numPings = nil
			if not isLeader then numPings = 6 end
			--Non-leaders don't get another ping until they open the panel or we reset the count to 0
			if isLeader or not self.stopAssistPings then
				if autoAccept then
					--Check if we would be auto-inviting more people if we were in a raid
					if
						not IsInRaid(LE_PARTY_CATEGORY_HOME)
						and GetNumGroupMembers(LE_PARTY_CATEGORY_HOME)
								+ C_LFGList.GetNumInvitedApplicantMembers()
								+ C_LFGList.GetNumPendingApplicantMembers()
							> (MAX_PARTY_MEMBERS + 1)
					then
						if self.displayedAutoAcceptConvert then
							QueueStatusMinimapButton_SetGlowLock(
								QueueStatusMinimapButton,
								"lfglist-applicant",
								true,
								numPings
							)
							self.stopAssistPings = true
						else
							self.displayedAutoAcceptConvert = true
							StaticPopup_Show("LFG_LIST_AUTO_ACCEPT_CONVERT_TO_RAID")
						end
					end
				elseif not self:IsVisible() then
					QueueStatusMinimapButton_SetGlowLock(QueueStatusMinimapButton, "lfglist-applicant", true, numPings)
					self.stopAssistPings = true
				end
			end
		end
	elseif event == "LFG_LIST_APPLICANT_UPDATED" then
		local numApps, numActiveApps = C_LFGList.GetNumApplicants()
		if numActiveApps == 0 then
			QueueStatusMinimapButton_SetGlowLock(QueueStatusMinimapButton, "lfglist-applicant", false)
			self.stopAssistPings = false
		end
	elseif event == "LFG_LIST_ENTRY_EXPIRED_TOO_MANY_PLAYERS" then
		if UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
			StaticPopup_Show("LFG_LIST_ENTRY_EXPIRED_TOO_MANY_PLAYERS")
		end
	elseif event == "LFG_LIST_ENTRY_EXPIRED_TIMEOUT" then
		if UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
			StaticPopup_Show("LFG_LIST_ENTRY_EXPIRED_TIMEOUT")
		end
	elseif event == "LFG_LIST_APPLICATION_STATUS_UPDATED" then
		local id, newStatus, oldStatus = ...
		if newStatus == "declined" then
			local info = ChatTypeInfo["SYSTEM"]
			local id, activity, name = C_LFGList.GetSearchResultInfo(id)
			DEFAULT_CHAT_FRAME:AddMessage(string.format(LFG_LIST_APP_DECLINED_MESSAGE, name), info.r, info.g, info.b)
		elseif newStatus == "timedout" then
			local info = ChatTypeInfo["SYSTEM"]
			local id, activity, name = C_LFGList.GetSearchResultInfo(id)
			DEFAULT_CHAT_FRAME:AddMessage(string.format(LFG_LIST_APP_TIMED_OUT_MESSAGE, name), info.r, info.g, info.b)
		end
	elseif event == "VARIABLES_LOADED" or event == "ADDON_LOADED" then
		if not GetCVarBitfield("closedInfoFrames", LE_FRAME_TUTORIAL_LFG_LIST) and UnitLevel("player") >= 90 then
			PremadeGroupsPvETutorialAlert:Show()
			if PremadeGroupsPvPTutorialAlert then PremadeGroupsPvPTutorialAlert:Show() end
		end
	elseif event == "GROUP_ROSTER_UPDATE" then
		if not IsInGroup(LE_PARTY_CATEGORY_HOME) then self.displayedAutoAcceptConvert = false end
	end

	--Dispatch the event to our currently active panel
	local onEvent = self.activePanel and self.activePanel:GetScript("OnEvent")
	if onEvent then onEvent(self.activePanel, event, ...) end

	--Dispatch the event to any panels that want the event in the background
	local bg = self.EventsInBackground[event]
	if bg then
		for i = 1, #bg do
			if bg[i] ~= self.activePanel then bg[i]:GetScript("OnEvent")(bg[i], event, ...) end
		end
	end
end

function LFGListFrame_OnShow(self)
	LFGListFrame_FixPanelValid(self)
	C_LFGList.RequestAvailableActivities()
	self.stopAssistPings = false
	QueueStatusMinimapButton_SetGlowLock(QueueStatusMinimapButton, "lfglist-applicant", false)
	PlaySound("igCharacterInfoOpen")
end

function LFGListFrame_SetActivePanel(self, panel)
	if self.activePanel then self.activePanel:Hide() end
	self.activePanel = panel
	self.activePanel:Show()
end

function LFGListFrame_IsPanelValid(self, panel)
	local listed = C_LFGList.GetActiveEntryInfo()

	--If we're listed, make sure we're either viewing applicants or editing our group
	if
		listed
		and panel ~= self.ApplicationViewer
		and not (panel == self.EntryCreation and LFGListEntryCreation_IsEditMode(self.EntryCreation))
	then
		return false
	end

	--If we're not listed, we can't be viewing applicants or editing our group
	if
		not listed
		and (
			panel == self.ApplicationViewer
			or (panel == self.EntryCreation and LFGListEntryCreation_IsEditMode(self.EntryCreation))
		)
	then
		return false
	end

	--Make sure we aren't creating a new entry with different baseFilters
	if panel == self.EntryCreation then
		if
			not LFGListEntryCreation_IsEditMode(self.EntryCreation)
			and self.baseFilters ~= self.EntryCreation.baseFilters
		then
			return false
		end
	end

	--Make sure we aren't searching with different baseFilters
	if panel == self.SearchPanel then
		if self.baseFilters ~= self.SearchPanel.preferredFilters then return false end
	end

	--If we're a trial account, we can only see the NothingAvailable and ApplicationViewer
	if IsRestrictedAccount() then
		if panel ~= self.NothingAvailable and panel ~= self.ApplicationViewer then return false end
	end

	--If we don't have any available activities, say so
	if #C_LFGList.GetAvailableCategories(self.baseFilters) == 0 then
		if panel == self.CategorySelection then return false end
	else
		if panel == self.NothingAvailable and not IsRestrictedAccount() then return false end
	end

	return true
end

function LFGListFrame_GetBestPanel(self)
	local listed = C_LFGList.GetActiveEntryInfo()

	if listed then
		return self.ApplicationViewer
	elseif IsRestrictedAccount() then
		return self.NothingAvailable
	elseif #C_LFGList.GetAvailableCategories(self.baseFilters) == 0 then
		return self.NothingAvailable
	else
		return self.CategorySelection
	end
end

function LFGListFrame_FixPanelValid(self)
	if not LFGListFrame_IsPanelValid(self, self.activePanel) then
		LFGListFrame_SetActivePanel(self, LFGListFrame_GetBestPanel(self))
	end
end

function LFGListFrame_SetBaseFilters(self, filters)
	self.baseFilters = filters

	--If we need to change panels, do so
	LFGListFrame_FixPanelValid(self)

	--Update the current panel
	if self.activePanel and self.activePanel.updateAll then self.activePanel.updateAll(self.activePanel) end
end

-------------------------------------------------------
----------Nothing available frame
-------------------------------------------------------
function LFGListNothingAvailable_OnEvent(self, event, ...)
	--Note: events are dispatched from the base frame. Add RegisterEvent there.
	if event == "LFG_LIST_AVAILABILITY_UPDATE" then LFGListNothingAvailable_Update(self) end
end

function LFGListNothingAvailable_Update(self)
	if IsRestrictedAccount() then
		self.Label:SetText(ERR_RESTRICTED_ACCOUNT_LFG_LIST_TRIAL)
	elseif C_LFGList.HasActivityList() then
		self.Label:SetText(NO_LFG_LIST_AVAILABLE)
	else
		self.Label:SetText(LFG_LIST_LOADING)
	end
end

-------------------------------------------------------
----------Category selection
-------------------------------------------------------
function LFGListCategorySelection_OnLoad(self) LFGListCategorySelection_UpdateNavButtons(self) end

function LFGListCategorySelection_OnEvent(self, event, ...)
	--Note: events are dispatched from the base frame. Add RegisterEvent there.
	if event == "LFG_LIST_AVAILABILITY_UPDATE" then LFGListCategorySelection_UpdateCategoryButtons(self) end

	if tContains(LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS, event) then LFGListCategorySelection_UpdateNavButtons(self) end
end

function LFGListCategorySelection_OnShow(self)
	LFGListCategorySelection_UpdateCategoryButtons(self)
	LFGListCategorySelection_UpdateNavButtons(self)
end

function LFGListCategorySelection_UpdateCategoryButtons(self)
	local baseFilters = self:GetParent().baseFilters
	local categories = C_LFGList.GetAvailableCategories(baseFilters)

	local nextBtn = 1
	local hasSelected = false

	--Update category buttons
	for i = 1, #categories do
		local isSelected = false
		local categoryID = categories[i]
		local name, separateRecommended = C_LFGList.GetCategoryInfo(categoryID)

		if separateRecommended then
			nextBtn, isSelected =
				LFGListCategorySelection_AddButton(self, nextBtn, categoryID, LE_LFG_LIST_FILTER_RECOMMENDED)
			hasSelected = hasSelected or isSelected
			nextBtn, isSelected =
				LFGListCategorySelection_AddButton(self, nextBtn, categoryID, LE_LFG_LIST_FILTER_NOT_RECOMMENDED)
		else
			nextBtn, isSelected = LFGListCategorySelection_AddButton(self, nextBtn, categoryID, 0)
		end

		hasSelected = hasSelected or isSelected
	end

	--Hide any extra buttons
	for i = nextBtn, #self.CategoryButtons do
		self.CategoryButtons[i]:Hide()
	end

	--If the selected item isn't in the list, deselect it
	if self.selectedCategory and not hasSelected then LFGListCategorySelection_SelectCategory(self, nil, nil) end
end

function LFGListCategorySelection_AddButton(self, btnIndex, categoryID, filters)
	--Check that we have activities with this filter
	local baseFilters = self:GetParent().baseFilters
	local allFilters = bit.bor(baseFilters, filters)

	if filters ~= 0 and #C_LFGList.GetAvailableActivities(categoryID, nil, allFilters) == 0 then
		return btnIndex, false
	end

	local name, separateRecommended = C_LFGList.GetCategoryInfo(categoryID)

	local button = self.CategoryButtons[btnIndex]
	if not button then
		self.CategoryButtons[btnIndex] = CreateFrame("BUTTON", nil, self, "LFGListCategoryTemplate")
		self.CategoryButtons[btnIndex]:SetPoint("TOP", self.CategoryButtons[btnIndex - 1], "BOTTOM", 0, -3)
		button = self.CategoryButtons[btnIndex]
	end

	button:SetText(LFGListUtil_GetDecoratedCategoryName(name, filters, true))
	button.categoryID = categoryID
	button.filters = filters

	local atlasName = nil
	if bit.band(allFilters, LE_LFG_LIST_FILTER_RECOMMENDED) ~= 0 then
		atlasName = "groupfinder-button-"
			.. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "raids")
			.. "-"
			.. LFG_LIST_PER_EXPANSION_TEXTURES[LFGListUtil_GetCurrentExpansion()]
	elseif bit.band(allFilters, LE_LFG_LIST_FILTER_NOT_RECOMMENDED) ~= 0 then
		atlasName = "groupfinder-button-"
			.. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "raids")
			.. "-"
			.. LFG_LIST_PER_EXPANSION_TEXTURES[math.max(0, LFGListUtil_GetCurrentExpansion() - 1)]
	else
		atlasName = "groupfinder-button-" .. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "questing")
	end

	local suffix = ""
	if bit.band(allFilters, LE_LFG_LIST_FILTER_PVE) ~= 0 then
		suffix = "-pve"
	elseif bit.band(allFilters, LE_LFG_LIST_FILTER_PVP) ~= 0 then
		suffix = "-pvp"
	end

	--Try with the suffix and then without it
	if not button.Icon:SetAtlas(atlasName .. suffix) then button.Icon:SetAtlas(atlasName) end

	local selected = self.selectedCategory == categoryID and self.selectedFilters == filters
	button.SelectedTexture:SetShown(selected)
	button:Show()

	return btnIndex + 1, selected
end

function LFGListCategorySelection_SelectCategory(self, categoryID, filters)
	self.selectedCategory = categoryID
	self.selectedFilters = filters
	LFGListCategorySelection_UpdateCategoryButtons(self)
	LFGListCategorySelection_UpdateNavButtons(self)
end

function LFGListCategorySelection_UpdateNavButtons(self)
	local findEnabled, startEnabled = true, true
	self.FindGroupButton.tooltip = nil
	self.StartGroupButton.tooltip = nil

	--Check if the user needs to select a category
	if not self.selectedCategory then
		findEnabled = false
		self.FindGroupButton.tooltip = LFG_LIST_SELECT_A_CATEGORY
		startEnabled = false
		self.StartGroupButton.tooltip = LFG_LIST_SELECT_A_CATEGORY
	end

	--Check if the user can't start a group due to not being a leader
	if IsInGroup(LE_PARTY_CATEGORY_HOME) and not UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
		startEnabled = false
		self.StartGroupButton.tooltip = LFG_LIST_NOT_LEADER
	end

	--Check if the player is currently in some incompatible queue
	local messageStart = LFGListUtil_GetActiveQueueMessage(false)
	if messageStart then
		startEnabled = false
		self.StartGroupButton.tooltip = messageStart
	end

	self.FindGroupButton:SetEnabled(findEnabled)
	self.StartGroupButton:SetEnabled(startEnabled)
end

function LFGListCategorySelectionStartGroupButton_OnClick(self)
	local panel = self:GetParent()
	if not panel.selectedCategory then return end

	PlaySound("igMainMenuOptionCheckBoxOn")

	local baseFilters = panel:GetParent().baseFilters

	local entryCreation = panel:GetParent().EntryCreation

	LFGListEntryCreation_Show(entryCreation, baseFilters, panel.selectedCategory, panel.selectedFilters)
end

function LFGListCategorySelectionFindGroupButton_OnClick(self)
	local panel = self:GetParent()
	if not panel.selectedCategory then return end

	PlaySound("igMainMenuOptionCheckBoxOn")

	local baseFilters = panel:GetParent().baseFilters

	local searchPanel = panel:GetParent().SearchPanel
	LFGListSearchPanel_Clear(searchPanel)
	LFGListSearchPanel_SetCategory(searchPanel, panel.selectedCategory, panel.selectedFilters, baseFilters)
	LFGListSearchPanel_DoSearch(searchPanel)
	LFGListFrame_SetActivePanel(panel:GetParent(), searchPanel)
end

--The individual category buttons
function LFGListCategorySelectionButton_OnClick(self)
	local panel = self:GetParent()
	PlaySound("igMainMenuOptionCheckBoxOn")
	LFGListCategorySelection_SelectCategory(panel, self.categoryID, self.filters)
end

-------------------------------------------------------
----------List Entry Creation
-------------------------------------------------------
function LFGListEntryCreation_OnLoad(self)
	self.Name.Instructions:SetText(LFG_LIST_ENTER_NAME)
	self.Description.EditBox:SetScript("OnEnterPressed", nop)
	LFGListUtil_SetUpDropDown(
		self,
		self.CategoryDropDown,
		LFGListEntryCreation_PopulateCategories,
		LFGListEntryCreation_OnCategorySelected
	)
	LFGListUtil_SetUpDropDown(
		self,
		self.GroupDropDown,
		LFGListEntryCreation_PopulateGroups,
		LFGListEntryCreation_OnGroupSelected
	)
	LFGListUtil_SetUpDropDown(
		self,
		self.ActivityDropDown,
		LFGListEntryCreation_PopulateActivities,
		LFGListEntryCreation_OnActivitySelected
	)
	LFGListEntryCreation_SetBaseFilters(self, 0)
end

function LFGListEntryCreation_OnEvent(self, event, ...)
	if event == "GROUP_ROSTER_UPDATE" then
		LFGListEntryCreation_UpdateValidState(self)
	elseif tContains(LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS, event) then
		LFGListEntryCreation_UpdateValidState(self)
	end
end

function LFGListEntryCreation_OnShow(self) LFGListEntryCreation_UpdateValidState(self) end

function LFGListEntryCreation_Show(self, baseFilters, selectedCategory, selectedFilters)
	--If this was what the player selected last time, just leave it filled out with the same info.
	--Also don't save it for categories that try to set it to the current area.
	local _, _, _, preferCurrentArea = C_LFGList.GetCategoryInfo(selectedCategory)
	local keepOldData = not preferCurrentArea
		and self.selectedCategory == selectedCategory
		and baseFilters == self.baseFilters
		and self.selectedFilters == selectedFilters
	LFGListEntryCreation_SetBaseFilters(self, baseFilters)
	if not keepOldData then
		LFGListEntryCreation_Clear(self)
		LFGListEntryCreation_Select(self, selectedFilters, selectedCategory)
	end
	LFGListEntryCreation_SetEditMode(self, false)

	LFGListEntryCreation_UpdateValidState(self)

	LFGListFrame_SetActivePanel(self:GetParent(), self)
	self.Name:SetFocus()
end

function LFGListEntryCreation_Clear(self)
	--Clear selections
	self.selectedCategory = nil
	self.selectedGroup = nil
	self.selectedActivity = nil
	self.selectedFilters = nil

	--Reset widgets
	self.Name:SetText("")
	self.ItemLevel.CheckButton:SetChecked(false)
	self.ItemLevel.EditBox:SetText("")
	self.HonorLevel.CheckButton:SetChecked(false)
	self.HonorLevel.EditBox:SetText("")
	self.VoiceChat.CheckButton:SetChecked(false)
	self.VoiceChat.EditBox:SetText("")
	self.Description.EditBox:SetText("")

	self.ActivityFinder:Hide()
end

function LFGListEntryCreation_ClearFocus(self)
	self.Name:ClearFocus()
	self.ItemLevel.EditBox:ClearFocus()
	self.HonorLevel.EditBox:ClearFocus()
	self.VoiceChat.EditBox:ClearFocus()
	self.Description.EditBox:ClearFocus()
end

--This function accepts any or all of categoryID, groupId, and activityID
function LFGListEntryCreation_Select(self, filters, categoryID, groupID, activityID)
	filters, categoryID, groupID, activityID =
		LFGListUtil_AugmentWithBest(bit.bor(self.baseFilters, filters or 0), categoryID, groupID, activityID)
	self.selectedCategory = categoryID
	self.selectedGroup = groupID
	self.selectedActivity = activityID
	self.selectedFilters = filters

	--Update the category dropdown
	local categoryName, _, autoChoose = C_LFGList.GetCategoryInfo(categoryID)
	UIDropDownMenu_SetText(self.CategoryDropDown, LFGListUtil_GetDecoratedCategoryName(categoryName, filters, false))

	--Update the activity dropdown
	local _, shortName, _, _, iLevel, _, _, _, _, _, useHonorLevel = C_LFGList.GetActivityInfo(activityID)
	UIDropDownMenu_SetText(self.ActivityDropDown, shortName)

	--Update the group dropdown. If the group dropdown is showing an activity, hide the activity dropdown
	local groupName = C_LFGList.GetActivityGroupInfo(groupID)
	UIDropDownMenu_SetText(self.GroupDropDown, groupName or shortName)
	self.ActivityDropDown:SetShown(groupName and not autoChoose)
	self.GroupDropDown:SetShown(not autoChoose)

	--Update the recommended item level box
	if iLevel ~= 0 then
		self.ItemLevel.EditBox.Instructions:SetFormattedText(LFG_LIST_RECOMMENDED_ILVL, iLevel)
	else
		self.ItemLevel.EditBox.Instructions:SetText(LFG_LIST_ITEM_LEVEL_INSTR_SHORT)
	end

	if useHonorLevel then
		self.HonorLevel:Show()
		self.VoiceChat:SetPoint("TOPLEFT", self.HonorLevel, "BOTTOMLEFT", 0, -5)
	else
		self.HonorLevel:Hide()
		self.VoiceChat:SetPoint("TOPLEFT", self.ItemLevel, "BOTTOMLEFT", 0, -5)
	end

	LFGListRequirement_Validate(self.ItemLevel, self.ItemLevel.EditBox:GetText())
	if useHonorLevel then LFGListRequirement_Validate(self.HonorLevel, self.HonorLevel.EditBox:GetText()) end
	LFGListEntryCreation_UpdateValidState(self)
end

function LFGListEntryCreation_PopulateCategories(self, dropDown, info)
	local categories = C_LFGList.GetAvailableCategories(self.baseFilters)
	for i = 1, #categories do
		local categoryID = categories[i]
		local name, separateRecommended = C_LFGList.GetCategoryInfo(categoryID)
		if separateRecommended then
			LFGListEntryCreation_AddCategoryEntry(self, info, categoryID, name, LE_LFG_LIST_FILTER_RECOMMENDED)
			LFGListEntryCreation_AddCategoryEntry(self, info, categoryID, name, LE_LFG_LIST_FILTER_NOT_RECOMMENDED)
		else
			LFGListEntryCreation_AddCategoryEntry(self, info, categoryID, name, 0)
		end
	end
end

function LFGListEntryCreation_AddCategoryEntry(self, info, categoryID, name, filters)
	if filters ~= 0 and #C_LFGList.GetAvailableActivities(categoryID, nil, filters) == 0 then return end

	info.text = LFGListUtil_GetDecoratedCategoryName(name, filters, false)
	info.value = categoryID
	info.arg1 = filters
	info.checked = (self.selectedCategory == categoryID and self.selectedFilters == filters)
	info.isRadio = true
	UIDropDownMenu_AddButton(info)
end

function LFGListEntryCreation_OnCategorySelected(self, categoryID, filters)
	LFGListEntryCreation_Select(self, filters, categoryID, nil, nil)
end

function LFGListEntryCreation_PopulateGroups(self, dropDown, info)
	if not self.selectedCategory then
		--We don't have a category, so we can't fill out groups.
		return
	end

	local useMore = false

	--Start out displaying everything
	local groups =
		C_LFGList.GetAvailableActivityGroups(self.selectedCategory, bit.bor(self.baseFilters, self.selectedFilters))
	local activities =
		C_LFGList.GetAvailableActivities(self.selectedCategory, 0, bit.bor(self.baseFilters, self.selectedFilters))
	if self.selectedFilters == 0 then
		--We don't bother filtering if we have less than 5 items anyway
		if #groups + #activities > 5 then
			--Try just displaying the recommended
			local filters = bit.bor(self.selectedFilters, self.baseFilters, LE_LFG_LIST_FILTER_RECOMMENDED)
			local recGroups = C_LFGList.GetAvailableActivityGroups(self.selectedCategory, filters)
			local recActivities = C_LFGList.GetAvailableActivities(self.selectedCategory, 0, filters)

			--If we have some recommended, just display those
			if #recGroups + #recActivities > 0 then
				--If we still have just as many, we don't need to display more
				useMore = #recGroups ~= #groups or #recActivities ~= #activities
				groups = recGroups
				activities = recActivities
			end
		end
	end

	local groupOrder = groups[1] and select(2, C_LFGList.GetActivityGroupInfo(groups[1]))
	local activityOrder = activities[1] and select(10, C_LFGList.GetActivityInfo(activities[1]))

	local groupIndex, activityIndex = 1, 1

	--Start merging
	for i = 1, MAX_LFG_LIST_GROUP_DROPDOWN_ENTRIES do
		if not groupOrder and not activityOrder then break end

		if activityOrder and (not groupOrder or activityOrder < groupOrder) then
			local activityID = activities[activityIndex]
			local name = select(ACTIVITY_RETURN_VALUES.shortName, C_LFGList.GetActivityInfo(activityID))

			info.text = name
			info.value = activityID
			info.arg1 = "activity"
			info.checked = (self.selectedActivity == activityID)
			info.isRadio = true
			UIDropDownMenu_AddButton(info)

			activityIndex = activityIndex + 1
			activityOrder = activities[activityIndex]
				and select(10, C_LFGList.GetActivityInfo(activities[activityIndex]))
		else
			local groupID = groups[groupIndex]
			local name = C_LFGList.GetActivityGroupInfo(groupID)

			info.text = name
			info.value = groupID
			info.arg1 = "group"
			info.checked = (self.selectedGroup == groupID)
			info.isRadio = true
			UIDropDownMenu_AddButton(info)

			groupIndex = groupIndex + 1
			groupOrder = groups[groupIndex] and select(2, C_LFGList.GetActivityGroupInfo(groups[groupIndex]))
		end
	end

	if #activities + #groups > MAX_LFG_LIST_GROUP_DROPDOWN_ENTRIES then useMore = true end

	if useMore then
		info.text = LFG_LIST_MORE
		info.value = nil
		info.arg1 = "more"
		info.notCheckable = true
		info.checked = false
		info.isRadio = false
		UIDropDownMenu_AddButton(info)
	end
end

function LFGListEntryCreation_OnGroupSelected(self, id, buttonType)
	if buttonType == "activity" then
		LFGListEntryCreation_Select(self, nil, nil, nil, id)
	elseif buttonType == "group" then
		LFGListEntryCreation_Select(self, self.selectedFilters, self.selectedCategory, id, nil)
	elseif buttonType == "more" then
		LFGListEntryCreationActivityFinder_Show(
			self.ActivityFinder,
			self.selectedCategory,
			nil,
			bit.bor(self.baseFilters, self.selectedFilters)
		)
	end
end

function LFGListEntryCreation_PopulateActivities(self, dropDown, info)
	local useMore = self.selectedFilters == 0

	local filters = bit.bor(self.baseFilters, self.selectedFilters)

	--Start out displaying everything
	local activities = C_LFGList.GetAvailableActivities(self.selectedCategory, self.selectedGroup, filters)

	--If we're displaying more than 5, see if we can just display recommended
	if useMore then
		if #activities > 5 then
			filters = bit.bor(filters, LE_LFG_LIST_FILTER_RECOMMENDED)
			local recActivities = C_LFGList.GetAvailableActivities(self.selectedCategory, self.selectedGroup, filters)

			useMore = #recActivities ~= #activities
			if #recActivities > 0 then
				activities = recActivities
			else
				--Just display up to 5 non-recommended activities
				for i = #activities, 5, -1 do
					activities[i] = nil
				end
			end
		else
			useMore = false
		end
	end

	for i = 1, #activities do
		local activityID = activities[i]
		local shortName = select(ACTIVITY_RETURN_VALUES.shortName, C_LFGList.GetActivityInfo(activityID))

		info.text = shortName
		info.value = activityID
		info.arg1 = "activity"
		info.checked = (self.selectedActivity == activityID)
		info.isRadio = true
		UIDropDownMenu_AddButton(info)
	end

	if useMore then
		info.text = LFG_LIST_MORE
		info.value = nil
		info.arg1 = "more"
		info.notCheckable = true
		info.checked = false
		info.isRadio = false
		UIDropDownMenu_AddButton(info)
	end
end

function LFGListEntryCreation_OnActivitySelected(self, activityID, buttonType)
	if buttonType == "activity" then
		LFGListEntryCreation_Select(self, nil, nil, nil, activityID)
	elseif buttonType == "more" then
		LFGListEntryCreationActivityFinder_Show(
			self.ActivityFinder,
			self.selectedCategory,
			self.selectedGroup,
			bit.bor(self.baseFilters, self.selectedFilters)
		)
	end
end

function LFGListEntryCreation_GetSanitizedName(self) return string.match(self.Name:GetText(), "^%s*(.-)%s*$") end

function LFGListEntryCreation_ListGroup(self)
	local name = LFGListEntryCreation_GetSanitizedName(self)
	local iLevel = tonumber(self.ItemLevel.EditBox:GetText()) or 0
	local honorLevel = tonumber(self.HonorLevel.EditBox:GetText()) or 0

	if LFGListEntryCreation_IsEditMode(self) then
		local autoAccept = select(9, C_LFGList.GetActiveEntryInfo())
		C_LFGList.UpdateListing(
			self.selectedActivity,
			name,
			iLevel,
			honorLevel,
			self.VoiceChat.EditBox:GetText(),
			self.Description.EditBox:GetText(),
			autoAccept
		)
		LFGListFrame_SetActivePanel(self:GetParent(), self:GetParent().ApplicationViewer)
	else
		if
			C_LFGList.CreateListing(
				self.selectedActivity,
				name,
				iLevel,
				honorLevel,
				self.VoiceChat.EditBox:GetText(),
				self.Description.EditBox:GetText(),
				false
			)
		then
			self.WorkingCover:Show()
			LFGListEntryCreation_ClearFocus(self)
		end
	end
end

function LFGListEntryCreation_UpdateValidState(self)
	local errorText
	local maxPlayers, _, _, useHonorLevel =
		select(ACTIVITY_RETURN_VALUES.maxPlayers, C_LFGList.GetActivityInfo(self.selectedActivity))
	if maxPlayers > 0 and GetNumGroupMembers(LE_PARTY_CATEGORY_HOME) >= maxPlayers then
		errorText = string.format(LFG_LIST_TOO_MANY_FOR_ACTIVITY, maxPlayers)
	elseif LFGListEntryCreation_GetSanitizedName(self) == "" then
		errorText = LFG_LIST_MUST_HAVE_NAME
	elseif self.ItemLevel.warningText then
		errorText = self.ItemLevel.warningText
	elseif useHonorLevel and self.HonorLevel.warningText then
		errorText = self.HonorLevel.warningText
	else
		errorText = LFGListUtil_GetActiveQueueMessage(false)
	end

	self.ListGroupButton:SetEnabled(not errorText)
	self.ListGroupButton.errorText = errorText
end

function LFGListEntryCreation_SetBaseFilters(self, baseFilters) self.baseFilters = baseFilters end

function LFGListEntryCreation_SetEditMode(self, editMode)
	self.editMode = editMode
	if editMode then
		local active, activityID, ilvl, honorLevel, name, comment, voiceChat = C_LFGList.GetActiveEntryInfo()
		assert(active)

		--Update the dropdowns
		LFGListEntryCreation_Select(self, nil, nil, nil, activityID)
		UIDropDownMenu_DisableDropDown(self.CategoryDropDown)
		UIDropDownMenu_DisableDropDown(self.GroupDropDown)
		UIDropDownMenu_DisableDropDown(self.ActivityDropDown)

		--Update edit boxes
		self.Name:SetText(name)
		self.ItemLevel.EditBox:SetText(ilvl ~= 0 and ilvl or "")
		self.HonorLevel.EditBox:SetText(honorLevel ~= 0 and honorLevel or "")
		self.VoiceChat.EditBox:SetText(voiceChat)
		self.Description.EditBox:SetText(comment)

		self.ListGroupButton:SetText(DONE_EDITING)
	else
		UIDropDownMenu_EnableDropDown(self.CategoryDropDown)
		UIDropDownMenu_EnableDropDown(self.GroupDropDown)
		UIDropDownMenu_EnableDropDown(self.ActivityDropDown)
		self.ListGroupButton:SetText(LIST_GROUP)
	end
end

function LFGListEntryCreation_IsEditMode(self) return self.editMode end

function LFGListEntryCreationCancelButton_OnClick(self)
	local panel = self:GetParent()
	PlaySound("igMainMenuOptionCheckBoxOn")
	if LFGListEntryCreation_IsEditMode(panel) then
		LFGListFrame_SetActivePanel(panel:GetParent(), panel:GetParent().ApplicationViewer)
	else
		LFGListFrame_SetActivePanel(panel:GetParent(), panel:GetParent().CategorySelection)
	end
end

function LFGListEntryCreationListGroupButton_OnClick(self)
	PlaySound("igMainMenuOptionCheckBoxOn")
	LFGListEntryCreation_ListGroup(self:GetParent())
end

function LFGListEntryCreationActivityFinder_OnLoad(self)
	self.Dialog.ScrollFrame.update = function() LFGListEntryCreationActivityFinder_Update(self) end
	self.Dialog.ScrollFrame.scrollBar.doNotHide = true
	HybridScrollFrame_CreateButtons(self.Dialog.ScrollFrame, "LFGListEntryCreationActivityListTemplate")

	self.matchingActivities = {}
end

function LFGListEntryCreationActivityFinder_Show(self, categoryID, groupID, filters)
	self.Dialog.EntryBox:SetText("")
	self.categoryID = categoryID
	self.groupID = groupID
	self.filters = filters
	self.selectedActivity = nil
	LFGListEntryCreationActivityFinder_UpdateMatching(self)
	self:Show()
	self.Dialog.EntryBox:SetFocus()
end

function LFGListEntryCreationActivityFinder_UpdateMatching(self)
	self.matchingActivities =
		C_LFGList.GetAvailableActivities(self.categoryID, self.groupID, self.filters, self.Dialog.EntryBox:GetText())
	LFGListUtil_SortActivitiesByRelevancy(self.matchingActivities)
	if not self.selectedActivity or not tContains(self.matchingActivities, self.selectedActivity) then
		self.selectedActivity = self.matchingActivities[1]
	end
	LFGListEntryCreationActivityFinder_Update(self)
end

function LFGListEntryCreationActivityFinder_Update(self)
	local actitivities = self.matchingActivities

	local offset = HybridScrollFrame_GetOffset(self.Dialog.ScrollFrame)

	for i = 1, #self.Dialog.ScrollFrame.buttons do
		local button = self.Dialog.ScrollFrame.buttons[i]
		local idx = i + offset
		local id = actitivities[idx]
		if id then
			button:SetText((C_LFGList.GetActivityInfo(id)))
			button.activityID = id
			button.Selected:SetShown(self.selectedActivity == id)
			if self.selectedActivity == id then
				button:LockHighlight()
			else
				button:UnlockHighlight()
			end
			button:Show()
		else
			button:Hide()
		end
	end
	HybridScrollFrame_Update(
		self.Dialog.ScrollFrame,
		self.Dialog.ScrollFrame.buttons[1]:GetHeight() * #actitivities,
		self.Dialog.ScrollFrame:GetHeight()
	)
end

function LFGListEntryCreationActivityFinder_Accept(self)
	if self.selectedActivity then
		LFGListEntryCreation_Select(self:GetParent(), nil, nil, nil, self.selectedActivity)
	end
	self:Hide()
end

function LFGListEntryCreationActivityFinder_Cancel(self) self:Hide() end

function LFGListEntryCreationActivityFinder_Select(self, activityID)
	self.selectedActivity = activityID
	LFGListEntryCreationActivityFinder_Update(self)
end

-------------------------------------------------------
----------Application Viewing
-------------------------------------------------------
function LFGListApplicationViewer_OnLoad(self)
	self.ScrollFrame.update = function() LFGListApplicationViewer_UpdateResults(self) end
	self.ScrollFrame.dynamic = function(offset) return LFGListApplicationViewer_GetScrollOffset(self, offset) end
	self.ScrollFrame.scrollBar.doNotHide = true
	self.NameColumnHeader:Disable()
	self.RoleColumnHeader:Disable()
	self.ItemLevelColumnHeader:Disable()
	HybridScrollFrame_CreateButtons(self.ScrollFrame, "LFGListApplicantTemplate")
end

function LFGListApplicationViewer_OnEvent(self, event, ...)
	if event == "LFG_LIST_ACTIVE_ENTRY_UPDATE" then
		LFGListApplicationViewer_UpdateInfo(self)
	elseif event == "PARTY_LEADER_CHANGED" then
		LFGListApplicationViewer_UpdateAvailability(self)
		LFGListApplicationViewer_UpdateInfo(self)
	elseif event == "LFG_LIST_APPLICANT_LIST_UPDATED" then
		LFGListApplicationViewer_UpdateResultList(self)
		LFGListApplicationViewer_UpdateResults(self)
	elseif event == "LFG_LIST_APPLICANT_UPDATED" then
		--If we can't make changes, we just remove people immediately
		local id = ...
		if not LFGListUtil_IsEntryEmpowered() then C_LFGList.RemoveApplicant(id) end

		--Update whether we can invite people
		LFGListApplicationViewer_UpdateInviteState(self)
	elseif event == "GROUP_ROSTER_UPDATE" then
		LFGListApplicationViewer_UpdateAvailability(self)
		LFGListApplicationViewer_UpdateGroupData(self)
		LFGListApplicationViewer_UpdateInviteState(self)
		LFGListApplicationViewer_UpdateInfo(self)
	elseif event == "PLAYER_ROLES_ASSIGNED" then
		LFGListApplicationViewer_UpdateGroupData(self)
	end
end

function LFGListApplicationViewer_OnShow(self)
	C_LFGList.RefreshApplicants()
	LFGListApplicationViewer_UpdateResultList(self)
	LFGListApplicationViewer_UpdateResults(self)
	LFGListApplicationViewer_UpdateInfo(self)
	LFGListApplicationViewer_UpdateAvailability(self)
	LFGListApplicationViewer_UpdateGroupData(self)
end

function LFGListApplicationViewer_UpdateGroupData(self)
	local active, activityID = C_LFGList.GetActiveEntryInfo()
	if not active then return end

	local data = GetGroupMemberCounts()
	data.DAMAGER = data.DAMAGER + data.NOROLE --People without a role count as damage
	data.NOROLE = 0
	LFGListGroupDataDisplay_Update(self.DataDisplay, activityID, data)
end

function LFGListApplicationViewer_UpdateInfo(self)
	local active, activityID, ilvl, honorLevel, name, comment, voiceChat, duration, autoAccept =
		C_LFGList.GetActiveEntryInfo()
	local fullName, shortName, categoryID, groupID, iLevel, filters, minLevel, maxPlayers, displayType =
		C_LFGList.GetActivityInfo(activityID)
	local _, separateRecommended = C_LFGList.GetCategoryInfo(categoryID)
	assert(active)
	self.EntryName:SetWidth(0)
	self.EntryName:SetText(name)
	self.DescriptionFrame.activityName = C_LFGList.GetActivityInfo(activityID)
	self.DescriptionFrame.comment = comment
	if comment == "" then
		self.DescriptionFrame.Text:SetText(self.DescriptionFrame.activityName)
	else
		self.DescriptionFrame.Text:SetFormattedText(
			"%s |cff888888- %s|r",
			self.DescriptionFrame.activityName,
			self.DescriptionFrame.comment
		)
	end

	local hasRestrictions = false
	if ilvl == 0 then
		self.ItemLevel:SetText("")
	else
		self.ItemLevel:SetFormattedText(LFG_LIST_ITEM_LEVEL_CURRENT, ilvl)
	end

	if voiceChat == "" then
		self.VoiceChatFrame.tooltip = nil
		self.VoiceChatFrame:Hide()
	else
		self.VoiceChatFrame.tooltip = voiceChat
		self.VoiceChatFrame:Show()
	end

	if self.EntryName:GetWidth() > 290 then self.EntryName:SetWidth(290) end

	--Set the background
	local atlasName = nil
	if separateRecommended and bit.band(filters, LE_LFG_LIST_FILTER_RECOMMENDED) ~= 0 then
		atlasName = "groupfinder-background-"
			.. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "raids")
			.. "-"
			.. LFG_LIST_PER_EXPANSION_TEXTURES[LFGListUtil_GetCurrentExpansion()]
	elseif separateRecommended and bit.band(filters, LE_LFG_LIST_FILTER_NOT_RECOMMENDED) ~= 0 then
		atlasName = "groupfinder-background-"
			.. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "raids")
			.. "-"
			.. LFG_LIST_PER_EXPANSION_TEXTURES[math.max(0, LFGListUtil_GetCurrentExpansion() - 1)]
	else
		atlasName = "groupfinder-background-" .. (LFG_LIST_CATEGORY_TEXTURES[categoryID] or "questing")
	end

	local suffix = ""
	if bit.band(filters, LE_LFG_LIST_FILTER_PVE) ~= 0 then
		suffix = "-pve"
	elseif bit.band(filters, LE_LFG_LIST_FILTER_PVP) ~= 0 then
		suffix = "-pvp"
	end

	--Try with the suffix and then without it
	if not self.InfoBackground:SetAtlas(atlasName .. suffix) then self.InfoBackground:SetAtlas(atlasName) end

	--Update the AutoAccept button
	self.AutoAcceptButton:SetChecked(autoAccept)
	if UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
		self.AutoAcceptButton:Show()
		self.AutoAcceptButton:Enable()
		self.AutoAcceptButton.Label:SetFontObject(GameFontHighlightSmall)
	elseif UnitIsGroupAssistant("player", LE_PARTY_CATEGORY_HOME) then
		self.AutoAcceptButton:Show()
		self.AutoAcceptButton:Disable()
		self.AutoAcceptButton.Label:SetFontObject(GameFontDisableSmall)
	else
		self.AutoAcceptButton:SetShown(autoAccept)
		self.AutoAcceptButton:Disable()
		self.AutoAcceptButton.Label:SetFontObject(GameFontDisableSmall)
	end
end

function LFGListApplicationViewer_UpdateAvailability(self)
	if UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
		self.RemoveEntryButton:Show()
		self.EditButton:Show()
	else
		self.RemoveEntryButton:Hide()
		self.EditButton:Hide()
	end

	if IsRestrictedAccount() then
		self.EditButton:Disable()
		self.EditButton.tooltip = ERR_RESTRICTED_ACCOUNT_LFG_LIST_TRIAL
	else
		self.EditButton:Enable()
		self.EditButton.tooltip = nil
	end

	local empowered = LFGListUtil_IsEntryEmpowered()
	self.UnempoweredCover:SetShown(not empowered)
	self.ScrollFrame.NoApplicants:SetShown(empowered and (not self.applicants or #self.applicants == 0))
end

function LFGListApplicationViewer_UpdateResultList(self)
	self.applicants = C_LFGList.GetApplicants()

	--Filter applicants. Don't worry about order.
	LFGListUtil_FilterApplicants(self.applicants)

	--Sort applicants
	LFGListUtil_SortApplicants(self.applicants)

	--Cache off the group sizes for the scroll frame and the total height
	local totalHeight = 0
	self.applicantSizes = {}
	for i = 1, #self.applicants do
		local _, _, _, numMembers = C_LFGList.GetApplicantInfo(self.applicants[i])
		self.applicantSizes[i] = numMembers
		totalHeight = totalHeight + LFGListApplicationViewerUtil_GetButtonHeight(numMembers)
	end
	self.totalApplicantHeight = totalHeight

	LFGListApplicationViewer_UpdateAvailability(self)
end

function LFGListApplicationViewer_UpdateInviteState(self)
	local active, activityID, ilvl, honorLevel, name, comment, voiceChat = C_LFGList.GetActiveEntryInfo()
	if not active then return end

	local numAllowed = select(ACTIVITY_RETURN_VALUES.maxPlayers, C_LFGList.GetActivityInfo(activityID))
	if numAllowed == 0 then numAllowed = MAX_RAID_MEMBERS end

	local currentCount = GetNumGroupMembers(LE_PARTY_CATEGORY_HOME)
	local numInvited = C_LFGList.GetNumInvitedApplicantMembers()

	local buttons = self.ScrollFrame.buttons
	for i = 1, #buttons do
		local button = buttons[i]
		if button.applicantID then
			if button.numMembers + currentCount > numAllowed then
				button.InviteButton:Disable()
				button.InviteButton.tooltip = LFG_LIST_GROUP_TOO_FULL
			elseif button.numMembers + currentCount + numInvited > numAllowed then
				button.InviteButton:Disable()
				button.InviteButton.tooltip = LFG_LIST_INVITED_APP_FILLS_GROUP
			else
				button.InviteButton:Enable()
				button.InviteButton.tooltip = nil
			end

			--If our mouse is already over the button, update the tooltip
			if button.InviteButton:IsMouseOver() then
				if button.InviteButton.tooltip then
					button.InviteButton:GetScript("OnEnter")(button.InviteButton)
				else
					GameTooltip:Hide()
				end
			end
		end
	end
end

function LFGListApplicationViewer_UpdateResults(self)
	local offset = HybridScrollFrame_GetOffset(self.ScrollFrame)
	local buttons = self.ScrollFrame.buttons

	--If the mouse is over something in this frame, update it
	local mouseover = GetMouseFocus()
	local mouseoverParent = mouseover and mouseover:GetParent()
	local parentParent = mouseoverParent and mouseoverParent:GetParent()
	if mouseoverParent == self.ScrollFrame or parentParent == self.ScrollFrame then
		--Just hide the tooltip. We should show it again inside the update function.
		GameTooltip:Hide()
	end

	for i = 1, #buttons do
		local button = buttons[i]
		local idx = i + offset
		local id = self.applicants[idx]

		if id then
			button.applicantID = id
			LFGListApplicationViewer_UpdateApplicant(button, id)
			button.Background:SetAlpha(idx % 2 == 0 and 0.1 or 0.05)
			button:Show()
		else
			button.applicantID = nil
			button:Hide()
		end
	end
	HybridScrollFrame_Update(self.ScrollFrame, self.totalApplicantHeight, self.ScrollFrame:GetHeight())
	LFGListApplicationViewer_UpdateInviteState(self)
end

function LFGListApplicationViewer_UpdateApplicant(button, id)
	local id, status, pendingStatus, numMembers, isNew = C_LFGList.GetApplicantInfo(id)
	button:SetHeight(LFGListApplicationViewerUtil_GetButtonHeight(numMembers))

	--Update individual members
	for i = 1, numMembers do
		local member = button.Members[i]
		if not member then
			member = CreateFrame("BUTTON", nil, button, "LFGListApplicantMemberTemplate")
			member:SetPoint("TOPLEFT", button.Members[i - 1], "BOTTOMLEFT", 0, 0)
			button.Members[i] = member
		end
		LFGListApplicationViewer_UpdateApplicantMember(member, id, i, status, pendingStatus)
		member:Show()
	end

	--Hide extra member buttons
	for i = numMembers + 1, #button.Members do
		button.Members[i]:Hide()
	end

	--Update the Invite and Decline buttons based on group size
	if numMembers > 1 then
		button.DeclineButton:SetHeight(36)
		button.InviteButton:SetHeight(36)
		button.InviteButton:SetFormattedText(LFG_LIST_INVITE_GROUP, numMembers)
	else
		button.DeclineButton:SetHeight(22)
		button.InviteButton:SetHeight(22)
		button.InviteButton:SetText(INVITE)
	end

	if pendingStatus or status == "applied" then
		button.Status:Hide()
	elseif status == "invited" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_INVITED)
		button.Status:SetTextColor(GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
	elseif status == "failed" or status == "cancelled" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_CANCELLED)
		button.Status:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
	elseif status == "declined" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_DECLINED)
		button.Status:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
	elseif status == "timedout" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_TIMED_OUT)
		button.Status:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
	elseif status == "inviteaccepted" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_INVITE_ACCEPTED)
		button.Status:SetTextColor(GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
	elseif status == "invitedeclined" then
		button.Status:Show()
		button.Status:SetText(LFG_LIST_APP_INVITE_DECLINED)
		button.Status:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
	end

	button.numMembers = numMembers
	button.InviteButton:SetShown(not pendingStatus and status == "applied" and LFGListUtil_IsEntryEmpowered())
	button.DeclineButton:SetShown(not pendingStatus and status ~= "invited" and LFGListUtil_IsEntryEmpowered())
	button.DeclineButton.isAck = (status ~= "applied" and status ~= "invited")
	button.Spinner:SetShown(pendingStatus)
end

function LFGListApplicationViewer_UpdateApplicantMember(member, appID, memberIdx, status, pendingStatus)
	local grayedOut = not pendingStatus
		and (
			status == "failed"
			or status == "cancelled"
			or status == "declined"
			or status == "invitedeclined"
			or status == "timedout"
		)
	local noTouchy = (status == "invited" or status == "inviteaccepted" or status == "invitedeclined")

	local name, class, localizedClass, level, itemLevel, honorLevel, tank, healer, damage, assignedRole, relationship =
		C_LFGList.GetApplicantMemberInfo(appID, memberIdx)

	member.memberIdx = memberIdx

	member.Name:SetWidth(0)
	if name then
		local displayName = Ambiguate(name, "short")
		if memberIdx > 1 then
			member.Name:SetText("  " .. displayName)
		else
			member.Name:SetText(displayName)
		end

		local classTextColor = grayedOut and GRAY_FONT_COLOR or RAID_CLASS_COLORS[class]
		member.Name:SetTextColor(classTextColor.r, classTextColor.g, classTextColor.b)
	else
		--We might still be requesting the name and class from the server.
		member.Name:SetText("")
	end

	member.FriendIcon:SetShown(relationship)
	member.FriendIcon.relationship = relationship
	member.FriendIcon.Icon:SetDesaturated(grayedOut)
	member.FriendIcon:SetAlpha(grayedOut and 0.5 or 1.0)

	--Adjust name width depending on whether we have the friend icon
	local nameLength = 100
	if relationship then nameLength = nameLength - 22 end
	if member.Name:GetWidth() > nameLength then member.Name:SetWidth(nameLength) end

	--Update the roles.
	if grayedOut then
		member.RoleIcon1:Hide()
		member.RoleIcon2:Hide()
	else
		local role1 = tank and "TANK" or (healer and "HEALER" or (damage and "DAMAGER"))
		local role2 = (tank and healer and "HEALER") or ((tank or healer) and damage and "DAMAGER")
		member.RoleIcon1:GetNormalTexture():SetAtlas(LFG_LIST_GROUP_DATA_ATLASES[role1])
		member.RoleIcon1:GetHighlightTexture():SetAtlas(LFG_LIST_GROUP_DATA_ATLASES[role1])
		if role2 then
			member.RoleIcon2:GetNormalTexture():SetAtlas(LFG_LIST_GROUP_DATA_ATLASES[role2])
			member.RoleIcon2:GetHighlightTexture():SetAtlas(LFG_LIST_GROUP_DATA_ATLASES[role2])
		end
		member.RoleIcon1:SetEnabled(not noTouchy and role1 ~= assignedRole)
		member.RoleIcon1:SetAlpha(role1 == assignedRole and 1 or 0.3)
		member.RoleIcon1:Show()
		member.RoleIcon2:SetEnabled(not noTouchy and role2 ~= assignedRole)
		member.RoleIcon2:SetAlpha(role2 == assignedRole and 1 or 0.3)
		member.RoleIcon2:SetShown(role2)
		member.RoleIcon1.role = role1
		member.RoleIcon2.role = role2
	end

	member.ItemLevel:SetShown(not grayedOut)
	member.ItemLevel:SetText(math.floor(itemLevel))

	local mouseFocus = GetMouseFocus()
	if mouseFocus == member then
		LFGListApplicantMember_OnEnter(member)
	elseif mouseFocus == member.FriendIcon then
		member.FriendIcon:GetScript("OnEnter")(member.FriendIcon)
	end
end

function LFGListApplicationViewer_GetScrollOffset(self, offset)
	local acum = 0
	for i = 1, #self.applicantSizes do
		local height = LFGListApplicationViewerUtil_GetButtonHeight(self.applicantSizes[i])
		acum = acum + height
		if acum > offset then return i - 1, height + offset - acum end
	end

	--We're scrolled completely off the bottom
	return #self.applicantSizes, 0
end

function LFGListApplicationViewerUtil_GetButtonHeight(numApplicants) return 20 * numApplicants + 6 end

function LFGListApplicationViewerEditButton_OnClick(self)
	PlaySound("igMainMenuOptionCheckBoxOn")

	local panel = self:GetParent()
	local entryCreation = panel:GetParent().EntryCreation
	LFGListEntryCreation_SetEditMode(entryCreation, true)
	LFGListFrame_SetActivePanel(panel:GetParent(), entryCreation)
end

--Applicant members
function LFGListApplicantMember_OnEnter(self)
	local applicantID = self:GetParent().applicantID
	local memberIdx = self.memberIdx

	local active, activityID = C_LFGList.GetActiveEntryInfo()
	if not active then return end

	local useHonorLevel = select(11, C_LFGList.GetActivityInfo(activityID))
	local id, status, pendingStatus, numMembers, isNew, comment = C_LFGList.GetApplicantInfo(applicantID)
	local name, class, localizedClass, level, itemLevel, honorLevel, tank, healer, damage, assignedRole =
		C_LFGList.GetApplicantMemberInfo(applicantID, memberIdx)

	GameTooltip:SetOwner(self, "ANCHOR_NONE")
	GameTooltip:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 105, 0)
	if name then
		local classTextColor = RAID_CLASS_COLORS[class]
		GameTooltip:SetText(name, classTextColor.r, classTextColor.g, classTextColor.b)
		GameTooltip:AddLine(string.format(UNIT_TYPE_LEVEL_TEMPLATE, level, localizedClass), 1, 1, 1)
	else
		GameTooltip:SetText(" ") --Just make it empty until we get the name update
	end
	GameTooltip:AddLine(string.format(LFG_LIST_ITEM_LEVEL_CURRENT, itemLevel), 1, 1, 1)
	if useHonorLevel then GameTooltip:AddLine(string.format(LFG_LIST_HONOR_LEVEL_CURRENT_PVP, honorLevel), 1, 1, 1) end
	if comment and comment ~= "" then
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(
			string.format(LFG_LIST_COMMENT_FORMAT, comment),
			LFG_LIST_COMMENT_FONT_COLOR.r,
			LFG_LIST_COMMENT_FONT_COLOR.g,
			LFG_LIST_COMMENT_FONT_COLOR.b,
			true
		)
	end

	--Add statistics
	local stats = C_LFGList.GetApplicantMemberStats(applicantID, memberIdx)
	local lastTitle = nil

	--Tank proving ground
	if stats[23690] and stats[23690] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_TANK_GOLD, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23687] and stats[23687] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_TANK_SILVER, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23684] and stats[23684] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_TANK_BRONZE, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	end

	--Healer proving ground
	if stats[23691] and stats[23691] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_HEALER_GOLD, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23688] and stats[23688] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_HEALER_SILVER, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23685] and stats[23685] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_HEALER_BRONZE, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	end

	--Damage proving ground
	if stats[23689] and stats[23689] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_DAMAGER_GOLD, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23686] and stats[23686] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_DAMAGER_SILVER, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	elseif stats[23683] and stats[23683] > 0 then
		LFGListUtil_AppendStatistic(LFG_LIST_PROVING_DAMAGER_BRONZE, nil, LFG_LIST_PROVING_GROUND_TITLE, lastTitle)
		lastTitle = LFG_LIST_PROVING_GROUND_TITLE
	end

	GameTooltip:Show()
end

-------------------------------------------------------
----------Searching
-------------------------------------------------------
function LFGListSearchPanel_OnLoad(self)
	self.SearchBox.Instructions:SetText(FILTER)
	self.ScrollFrame.update = function() LFGListSearchPanel_UpdateResults(self) end
	self.ScrollFrame.scrollBar.doNotHide = true
	HybridScrollFrame_CreateButtons(self.ScrollFrame, "LFGListSearchEntryTemplate")
	self.SearchBox.clearButton:SetScript("OnClick", function(btn)
		SearchBoxTemplateClearButton_OnClick(btn)
		LFGListSearchPanel_DoSearch(self)
	end)
end

function LFGListSearchPanel_OnEvent(self, event, ...)
	--Note: events are dispatched from the base frame. Add RegisterEvent there.
	if event == "LFG_LIST_SEARCH_RESULTS_RECEIVED" then
		StaticPopupSpecial_Hide(LFGListApplicationDialog)
		self.searching = false
		self.searchFailed = false
		LFGListSearchPanel_UpdateResultList(self)
		LFGListSearchPanel_UpdateResults(self)
	elseif event == "LFG_LIST_SEARCH_FAILED" then
		self.searching = false
		self.searchFailed = true
		LFGListSearchPanel_UpdateResultList(self)
		LFGListSearchPanel_UpdateResults(self)
	elseif event == "LFG_LIST_SEARCH_RESULT_UPDATED" then
		local id = ...
		if self.selectedResult == id then
			LFGListSearchPanel_ValidateSelected(self)
			if self.selectedResult ~= id then LFGListSearchPanel_UpdateResults(self) end
		end
		LFGListSearchPanel_UpdateButtonStatus(self)
	elseif event == "LFG_LIST_SEARCH_RESULT_UPDATED" then
		LFGListSearchPanel_UpdateButtonStatus(self)
	elseif event == "PARTY_LEADER_CHANGED" then
		LFGListSearchPanel_UpdateButtonStatus(self)
	elseif event == "GROUP_ROSTER_UPDATE" then
		LFGListSearchPanel_UpdateButtonStatus(self)
	elseif event == "PLAYER_SPECIALIZATION_CHANGED" then
		local unit = ...
		if unit == "player" then LFGListSearchPanel_UpdateButtonStatus(self) end
	elseif event == "UNIT_CONNECTION" then
		LFGListSearchPanel_UpdateButtonStatus(self)
	end

	if tContains(LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS, event) then LFGListSearchPanel_UpdateButtonStatus(self) end
end

function LFGListSearchPanel_OnShow(self)
	LFGListSearchPanel_UpdateResultList(self)
	LFGListSearchPanel_UpdateResults(self)
	--LFGListSearchPanel_UpdateButtonStatus(self); --Called by UpdateResults

	local availableLanguages = C_LFGList.GetAvailableLanguageSearchFilter()
	local defaultLanguages = C_LFGList.GetDefaultLanguageSearchFilter()

	local canChangeLanguages = false
	for i = 1, #availableLanguages do
		if not defaultLanguages[availableLanguages[i]] then
			canChangeLanguages = true
			break
		end
	end

	if canChangeLanguages then
		self.SearchBox:SetWidth(228)
		self.FilterButton:Show()
	else
		self.SearchBox:SetWidth(319)
		self.FilterButton:Hide()
	end
end

function LFGListSearchPanel_Clear(self)
	C_LFGList.ClearSearchResults()
	self.SearchBox:SetText("")
	self.selectedResult = nil
	LFGListSearchPanel_UpdateResultList(self)
	LFGListSearchPanel_UpdateResults(self)
end

function LFGListSearchPanel_SetCategory(self, categoryID, filters, preferredFilters)
	self.categoryID = categoryID
	self.filters = filters
	self.preferredFilters = preferredFilters

	local name = LFGListUtil_GetDecoratedCategoryName(C_LFGList.GetCategoryInfo(categoryID), filters, false)
	self.CategoryName:SetText(name)
end

function LFGListSearchPanel_DoSearch(self)
	local searchText = self.SearchBox:GetText()
	local languages = C_LFGList.GetLanguageSearchFilter()
	C_LFGList.Search(self.categoryID, searchText, self.filters, self.preferredFilters, languages)
	self.searching = true
	self.searchFailed = false
	self.selectedResult = nil
	LFGListSearchPanel_UpdateResultList(self)
	LFGListSearchPanel_UpdateResults(self)
end

function LFGListSearchPanel_CreateGroupInstead(self)
	LFGListEntryCreation_Show(self:GetParent().EntryCreation, self.preferredFilters, self.categoryID, self.filters)
end

function LFGListSearchPanel_UpdateResultList(self)
	self.totalResults, self.results = C_LFGList.GetSearchResults()
	self.applications = C_LFGList.GetApplications()
	LFGListUtil_SortSearchResults(self.results)
end

function LFGListSearchPanel_ValidateSelected(self)
	if self.selectedResult and not LFGListSearchPanelUtil_CanSelectResult(self.selectedResult) then
		self.selectedResult = nil
	end
end

function LFGListSearchPanelUtil_CanSelectResult(resultID)
	local _, appStatus, pendingStatus, appDuration = C_LFGList.GetApplicationInfo(resultID)
	local id, activityID, name, comment, voiceChat, iLvl, honorLevel, age, numBNetFriends, numCharFriends, numGuildMates, isDelisted =
		C_LFGList.GetSearchResultInfo(resultID)
	if appStatus ~= "none" or pendingStatus or isDelisted then return false end
	return true
end

function LFGListSearchPanel_UpdateResults(self)
	local offset = HybridScrollFrame_GetOffset(self.ScrollFrame)
	local buttons = self.ScrollFrame.buttons

	--If we have an application selected, deselect it.
	LFGListSearchPanel_ValidateSelected(self)

	if self.searching then
		self.SearchingSpinner:Show()
		self.ScrollFrame.NoResultsFound:Hide()
		self.ScrollFrame.StartGroupButton:Hide()
		for i = 1, #buttons do
			buttons[i]:Hide()
		end
	else
		self.SearchingSpinner:Hide()
		local results = self.results
		local apps = self.applications

		for i = 1, #buttons do
			local button = buttons[i]
			local idx = i + offset
			local result = (idx <= #apps) and apps[idx] or results[idx - #apps]

			if result then
				button.resultID = result
				LFGListSearchEntry_Update(button)
				button:Show()
			else
				button.resultID = nil
				button:Hide()
			end
		end

		local totalHeight = buttons[1]:GetHeight() * (#results + #apps)

		--Reanchor the errors to not overlap applications
		if totalHeight < self.ScrollFrame:GetHeight() then
			self.ScrollFrame.NoResultsFound:SetPoint("TOP", self.ScrollFrame, "TOP", 0, -totalHeight - 27)
		end
		self.ScrollFrame.NoResultsFound:SetShown(self.totalResults == 0)
		self.ScrollFrame.StartGroupButton:SetShown(self.totalResults == 0 and not self.searchFailed)
		self.ScrollFrame.NoResultsFound:SetText(
			self.searchFailed and LFG_LIST_SEARCH_FAILED or LFG_LIST_NO_RESULTS_FOUND
		)

		HybridScrollFrame_Update(self.ScrollFrame, totalHeight, self.ScrollFrame:GetHeight())
	end
	LFGListSearchPanel_UpdateButtonStatus(self)
end

function LFGListSearchPanel_SelectResult(self, resultID)
	self.selectedResult = resultID
	LFGListSearchPanel_UpdateResults(self)
end

function LFGListSearchPanel_UpdateButtonStatus(self)
	--Update the SignUpButton
	local resultID = self.selectedResult
	local numApplications, numActiveApplications = C_LFGList.GetNumApplications()
	local messageApply = LFGListUtil_GetActiveQueueMessage(true)
	local availTank, availHealer, availDPS = C_LFGList.GetAvailableRoles()
	if messageApply then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = messageApply
	elseif not LFGListUtil_IsAppEmpowered() then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_APP_UNEMPOWERED
	elseif IsInGroup(LE_PARTY_CATEGORY_HOME) and C_LFGList.IsCurrentlyApplying() then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_APP_CURRENTLY_APPLYING
	elseif numActiveApplications >= MAX_LFG_LIST_APPLICATIONS then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = string.format(LFG_LIST_HIT_MAX_APPLICATIONS, MAX_LFG_LIST_APPLICATIONS)
	elseif GetNumGroupMembers(LE_PARTY_CATEGORY_HOME) > MAX_PARTY_MEMBERS + 1 then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_MAX_MEMBERS
	elseif not (availTank or availHealer or availDPS) then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_MUST_CHOOSE_SPEC
	elseif GroupHasOfflineMember(LE_PARTY_CATEGORY_HOME) then
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_OFFLINE_MEMBER
	elseif resultID then
		self.SignUpButton:Enable()
		self.SignUpButton.tooltip = nil
	else
		self.SignUpButton:Disable()
		self.SignUpButton.tooltip = LFG_LIST_SELECT_A_SEARCH_RESULT
	end

	--Update the StartGroupButton
	if IsInGroup(LE_PARTY_CATEGORY_HOME) and not UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) then
		self.ScrollFrame.StartGroupButton:Disable()
		self.ScrollFrame.StartGroupButton.tooltip = LFG_LIST_NOT_LEADER
	else
		local messageStart = LFGListUtil_GetActiveQueueMessage(false)
		if messageStart then
			self.ScrollFrame.StartGroupButton:Disable()
			self.ScrollFrame.StartGroupButton.tooltip = messageStart
		else
			self.ScrollFrame.StartGroupButton:Enable()
			self.ScrollFrame.StartGroupButton.tooltip = nil
		end
	end
end

function LFGListSearchPanel_SignUp(self) LFGListApplicationDialog_Show(LFGListApplicationDialog, self.selectedResult) end

function LFGListSearchPanelSearchBox_OnEnterPressed(self)
	local parent = self:GetParent()
	if parent.AutoCompleteFrame:IsShown() and parent.AutoCompleteFrame.selected then
		self:SetText((C_LFGList.GetActivityInfo(parent.AutoCompleteFrame.selected)))
	end

	LFGListSearchPanel_DoSearch(self:GetParent())
	self:ClearFocus()
end

function LFGListSearchPanelSearchBox_OnTabPressed(self)
	if IsShiftKeyDown() then
		LFGListSearchPanel_AutoCompleteAdvance(self:GetParent(), -1)
	else
		LFGListSearchPanel_AutoCompleteAdvance(self:GetParent(), 1)
	end
end

function LFGListSearchPanelSearchBox_OnArrowPressed(self, key)
	if key == "UP" then
		LFGListSearchPanel_AutoCompleteAdvance(self:GetParent(), -1)
	elseif key == "DOWN" then
		LFGListSearchPanel_AutoCompleteAdvance(self:GetParent(), 1)
	end
end

function LFGListSearchPanelSearchBox_OnTextChanged(self)
	SearchBoxTemplate_OnTextChanged(self)
	LFGListSearchPanel_UpdateAutoComplete(self:GetParent())
end

function LFGListSearchAutoCompleteButton_OnClick(self)
	local panel = self:GetParent():GetParent()
	PlaySound("igMainMenuOptionCheckBoxOn")
	panel.SearchBox:SetText((C_LFGList.GetActivityInfo(self.activityID)))
	LFGListSearchPanel_DoSearch(panel)
	panel.SearchBox:ClearFocus()
end

function LFGListSearchPanel_AutoCompleteAdvance(self, offset)
	local selected = self.AutoCompleteFrame.selected

	--Find the index of the current selection and how many results we have displayed
	local idx = nil
	local numDisplayed = 0
	for i = 1, #self.AutoCompleteFrame.Results do
		local btn = self.AutoCompleteFrame.Results[i]
		if btn:IsShown() and btn.activityID then
			numDisplayed = i
			if btn.activityID == selected then idx = i end
		else
			break
		end
	end

	local newIndex = nil
	if not idx then
		--We had nothing selected, advance from the front or back
		if offset > 0 then
			newIndex = offset
		else
			newIndex = numDisplayed + 1 + offset
		end
	else
		--Advance from our old location
		newIndex = ((idx - 1 + offset + numDisplayed) % numDisplayed) + 1
	end

	self.AutoCompleteFrame.selected = self.AutoCompleteFrame.Results[newIndex].activityID
	LFGListSearchPanel_UpdateAutoComplete(self)
end

function LFGListSearchPanel_UpdateAutoComplete(self)
	local text = self.SearchBox:GetText()
	if text == "" or not self.SearchBox:HasFocus() then
		self.AutoCompleteFrame:Hide()
		self.AutoCompleteFrame.selected = nil
		return
	end

	--Choose the autocomplete results
	local matchingActivities = C_LFGList.GetAvailableActivities(self.categoryID, nil, self.filters, text)
	LFGListUtil_SortActivitiesByRelevancy(matchingActivities)

	local numResults = math.min(#matchingActivities, MAX_LFG_LIST_SEARCH_AUTOCOMPLETE_ENTRIES)

	if numResults == 0 then
		self.AutoCompleteFrame:Hide()
		self.AutoCompleteFrame.selected = nil
		return
	end

	--Update the buttons
	local foundSelected = false
	for i = 1, numResults do
		local id = matchingActivities[i]

		local button = self.AutoCompleteFrame.Results[i]
		if not button then
			button = CreateFrame("BUTTON", nil, self.AutoCompleteFrame, "LFGListSearchAutoCompleteButtonTemplate")
			button:SetPoint("TOPLEFT", self.AutoCompleteFrame.Results[i - 1], "BOTTOMLEFT", 0, 0)
			button:SetPoint("TOPRIGHT", self.AutoCompleteFrame.Results[i - 1], "BOTTOMRIGHT", 0, 0)
			self.AutoCompleteFrame.Results[i] = button
		end

		if i == numResults and numResults < #matchingActivities then
			--This is just a "x more" button
			button:SetFormattedText(LFG_LIST_AND_MORE, #matchingActivities - numResults + 1)
			button:Disable()
			button.Selected:Hide()
			button.activityID = nil
		else
			--This is an actual activity
			button:SetText((C_LFGList.GetActivityInfo(id)))
			button:Enable()
			button.activityID = id

			if id == self.AutoCompleteFrame.selected then
				button.Selected:Show()
				foundSelected = true
			else
				button.Selected:Hide()
			end
		end
		button:Show()
	end

	if not foundSelected then self.selected = nil end

	--Hide unused buttons
	for i = numResults + 1, #self.AutoCompleteFrame.Results do
		self.AutoCompleteFrame.Results[i]:Hide()
	end

	--Update the frames height and show it
	self.AutoCompleteFrame:SetHeight(numResults * self.AutoCompleteFrame.Results[1]:GetHeight() + 8)
	self.AutoCompleteFrame:Show()
end

function LFGListSearchEntry_OnLoad(self)
	self:RegisterEvent("LFG_LIST_SEARCH_RESULT_UPDATED")
	self:RegisterEvent("LFG_ROLE_CHECK_UPDATE")
	self:RegisterForClicks("LeftButtonUp", "RightButtonUp")
end

function LFGListSearchEntry_Update(self)
	local resultID = self.resultID
	local _, appStatus, pendingStatus, appDuration = C_LFGList.GetApplicationInfo(resultID)
	local isApplication = (appStatus ~= "none" or pendingStatus)
	local isAppFinished = LFGListUtil_IsStatusInactive(appStatus) or LFGListUtil_IsStatusInactive(pendingStatus)

	--Update visibility based on whether we're an application or not
	self.isApplication = isApplication
	self.ApplicationBG:SetShown(isApplication and not isAppFinished)
	self.ResultBG:SetShown(not isApplication or isAppFinished)
	self.DataDisplay:SetShown(not isApplication)
	self.CancelButton:SetShown(isApplication and pendingStatus ~= "applied")
	self.CancelButton:SetEnabled(LFGListUtil_IsAppEmpowered())
	self.CancelButton.Icon:SetDesaturated(not LFGListUtil_IsAppEmpowered())
	self.CancelButton.tooltip = (not LFGListUtil_IsAppEmpowered()) and LFG_LIST_APP_UNEMPOWERED
	self.Spinner:SetShown(pendingStatus == "applied")

	if pendingStatus == "applied" and C_LFGList.GetRoleCheckInfo() then
		self.PendingLabel:SetText(LFG_LIST_ROLE_CHECK)
		self.PendingLabel:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif pendingStatus == "cancelled" or appStatus == "cancelled" or appStatus == "failed" then
		self.PendingLabel:SetText(LFG_LIST_APP_CANCELLED)
		self.PendingLabel:SetTextColor(RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif appStatus == "declined" then
		self.PendingLabel:SetText(LFG_LIST_APP_DECLINED)
		self.PendingLabel:SetTextColor(RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif appStatus == "timedout" then
		self.PendingLabel:SetText(LFG_LIST_APP_TIMED_OUT)
		self.PendingLabel:SetTextColor(RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif appStatus == "invited" then
		self.PendingLabel:SetText(LFG_LIST_APP_INVITED)
		self.PendingLabel:SetTextColor(GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif appStatus == "inviteaccepted" then
		self.PendingLabel:SetText(LFG_LIST_APP_INVITE_ACCEPTED)
		self.PendingLabel:SetTextColor(GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif appStatus == "invitedeclined" then
		self.PendingLabel:SetText(LFG_LIST_APP_INVITE_DECLINED)
		self.PendingLabel:SetTextColor(RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	elseif isApplication and pendingStatus ~= "applied" then
		self.PendingLabel:SetText(LFG_LIST_PENDING)
		self.PendingLabel:SetTextColor(GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
		self.PendingLabel:Show()
		self.ExpirationTime:Show()
		self.CancelButton:Show()
	else
		self.PendingLabel:Hide()
		self.ExpirationTime:Hide()
		self.CancelButton:Hide()
	end

	--Center justify if we're on more than one line
	if self.PendingLabel:GetHeight() > 15 then
		self.PendingLabel:SetJustifyH("CENTER")
	else
		self.PendingLabel:SetJustifyH("RIGHT")
	end

	--Change the anchor of the label depending on whether we have the expiration time
	if self.ExpirationTime:IsShown() then
		self.PendingLabel:SetPoint("RIGHT", self.ExpirationTime, "LEFT", -3, 0)
	else
		self.PendingLabel:SetPoint("RIGHT", self.ExpirationTime, "RIGHT", -3, 0)
	end

	self.expiration = GetTime() + appDuration

	local panel = self:GetParent():GetParent():GetParent()

	local id, activityID, name, comment, voiceChat, iLvl, honorLevel, age, numBNetFriends, numCharFriends, numGuildMates, isDelisted =
		C_LFGList.GetSearchResultInfo(resultID)
	local activityName = C_LFGList.GetActivityInfo(activityID)

	self.resultID = resultID
	self.Selected:SetShown(panel.selectedResult == resultID and not isApplication and not isDelisted)
	self.Highlight:SetShown(panel.selectedResult ~= resultID and not isApplication and not isDelisted)
	local nameColor = NORMAL_FONT_COLOR
	local activityColor = GRAY_FONT_COLOR
	if isDelisted or isAppFinished then
		nameColor = LFG_LIST_DELISTED_FONT_COLOR
		activityColor = LFG_LIST_DELISTED_FONT_COLOR
	elseif numBNetFriends > 0 or numCharFriends > 0 or numGuildMates > 0 then
		nameColor = BATTLENET_FONT_COLOR
	end
	self.Name:SetWidth(0)
	self.Name:SetText(name)
	self.Name:SetTextColor(nameColor.r, nameColor.g, nameColor.b)
	self.ActivityName:SetText(activityName)
	self.ActivityName:SetTextColor(activityColor.r, activityColor.g, activityColor.b)
	self.VoiceChat:SetShown(voiceChat ~= "")
	self.VoiceChat.tooltip = voiceChat

	local displayData = C_LFGList.GetSearchResultMemberCounts(resultID)
	LFGListGroupDataDisplay_Update(self.DataDisplay, activityID, displayData, isDelisted)

	local nameWidth = isApplication and 165 or 176
	if voiceChat ~= "" then nameWidth = nameWidth - 22 end
	if self.Name:GetWidth() > nameWidth then self.Name:SetWidth(nameWidth) end
	self.ActivityName:SetWidth(nameWidth)

	local mouseFocus = GetMouseFocus()
	if mouseFocus == self then LFGListSearchEntry_OnEnter(self) end
	if mouseFocus == self.VoiceChat then mouseFocus:GetScript("OnEnter")(mouseFocus) end

	if isApplication then
		self:SetScript("OnUpdate", LFGListSearchEntry_UpdateExpiration)
		LFGListSearchEntry_UpdateExpiration(self)
	else
		self:SetScript("OnUpdate", nil)
	end
end

function LFGListSearchEntry_UpdateExpiration(self)
	local duration = 0
	local now = GetTime()
	if self.expiration and self.expiration > now then duration = self.expiration - now end

	local minutes = math.floor(duration / 60)
	local seconds = duration % 60
	self.ExpirationTime:SetFormattedText("%d:%.2d", minutes, seconds)
end

function LFGListSearchEntry_OnEvent(self, event, ...)
	if event == "LFG_LIST_SEARCH_RESULT_UPDATED" then
		local id = ...
		if id == self.resultID then LFGListSearchEntry_Update(self) end
	elseif event == "LFG_ROLE_CHECK_UPDATE" then
		if self.resultID then LFGListSearchEntry_Update(self) end
	end
end

function LFGListSearchEntry_OnClick(self, button)
	local scrollFrame = self:GetParent():GetParent()
	if button == "RightButton" then
		PlaySound("igMainMenuOptionCheckBoxOn")
		EasyMenu(LFGListUtil_GetSearchEntryMenu(self.resultID), LFGListFrameDropDown, self, 0, -2, "MENU")
	elseif
		scrollFrame:GetParent().selectedResult ~= self.resultID
		and LFGListSearchPanelUtil_CanSelectResult(self.resultID)
	then
		PlaySound("igMainMenuOptionCheckBoxOn")
		LFGListSearchPanel_SelectResult(scrollFrame:GetParent(), self.resultID)
	end
end

function LFGListSearchEntry_OnEnter(self)
	local resultID = self.resultID
	local id, activityID, name, comment, voiceChat, iLvl, honorLevel, age, numBNetFriends, numCharFriends, numGuildMates, isDelisted, leaderName, numMembers =
		C_LFGList.GetSearchResultInfo(resultID)
	local activityName, shortName, categoryID, groupID, minItemLevel, filters, minLevel, maxPlayers, displayType, _, useHonorLevel =
		C_LFGList.GetActivityInfo(activityID)
	local memberCounts = C_LFGList.GetSearchResultMemberCounts(resultID)
	GameTooltip:SetOwner(self, "ANCHOR_RIGHT", 25, 0)
	GameTooltip:SetText(name, 1, 1, 1, true)
	GameTooltip:AddLine(activityName)
	if comment ~= "" then
		GameTooltip:AddLine(
			string.format(LFG_LIST_COMMENT_FORMAT, comment),
			LFG_LIST_COMMENT_FONT_COLOR.r,
			LFG_LIST_COMMENT_FONT_COLOR.g,
			LFG_LIST_COMMENT_FONT_COLOR.b,
			true
		)
	end
	GameTooltip:AddLine(" ")
	if iLvl > 0 then GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_ILVL, iLvl)) end
	if useHonorLevel and honorLevel > 0 then
		GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_HONOR_LEVEL, honorLevel))
	end
	if voiceChat ~= "" then
		GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_VOICE_CHAT, voiceChat), nil, nil, nil, true)
	end
	if iLvl > 0 or (useHonorLevel and honorLevel > 0) or voiceChat ~= "" then GameTooltip:AddLine(" ") end

	if leaderName then GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_LEADER, leaderName)) end
	if age > 0 then
		GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_AGE, SecondsToTime(age, false, false, 1, false)))
	end

	if leaderName or age > 0 then GameTooltip:AddLine(" ") end

	if displayType == LE_LFG_LIST_DISPLAY_TYPE_CLASS_ENUMERATE then
		GameTooltip:AddLine(string.format(LFG_LIST_TOOLTIP_MEMBERS_SIMPLE, numMembers))
		for i = 1, numMembers do
			local role, class, classLocalized = C_LFGList.GetSearchResultMemberInfo(resultID, i)
			local classColor = RAID_CLASS_COLORS[class] or NORMAL_FONT_COLOR
			GameTooltip:AddLine(
				string.format(LFG_LIST_TOOLTIP_CLASS_ROLE, classLocalized, _G[role]),
				classColor.r,
				classColor.g,
				classColor.b
			)
		end
	else
		GameTooltip:AddLine(
			string.format(
				LFG_LIST_TOOLTIP_MEMBERS,
				numMembers,
				memberCounts.TANK,
				memberCounts.HEALER,
				memberCounts.DAMAGER
			)
		)
	end

	if numBNetFriends + numCharFriends + numGuildMates > 0 then
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(LFG_LIST_TOOLTIP_FRIENDS_IN_GROUP)
		GameTooltip:AddLine(LFGListSearchEntryUtil_GetFriendList(resultID), 1, 1, 1, true)
	end

	local completedEncounters = C_LFGList.GetSearchResultEncounterInfo(resultID)
	if completedEncounters and #completedEncounters > 0 then
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(LFG_LIST_BOSSES_DEFEATED)
		for i = 1, #completedEncounters do
			GameTooltip:AddLine(completedEncounters[i], RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		end
	end

	if isDelisted then
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(LFG_LIST_ENTRY_DELISTED, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, true)
	end

	GameTooltip:Show()
end

function LFGListSearchEntryUtil_GetFriendList(resultID)
	local list = ""
	local bNetFriends, charFriends, guildMates = C_LFGList.GetSearchResultFriends(resultID)
	local displayedFirst = false

	--BNet friends
	for i = 1, #bNetFriends do
		if displayedFirst then
			list = list .. PLAYER_LIST_DELIMITER
		else
			displayedFirst = true
		end
		list = list .. FRIENDS_BNET_NAME_COLOR_CODE .. bNetFriends[i] .. FONT_COLOR_CODE_CLOSE
	end

	--Character friends
	for i = 1, #charFriends do
		if displayedFirst then
			list = list .. PLAYER_LIST_DELIMITER
		else
			displayedFirst = true
		end
		list = list .. FRIENDS_WOW_NAME_COLOR_CODE .. charFriends[i] .. FONT_COLOR_CODE_CLOSE
	end

	--Guild mates
	for i = 1, #guildMates do
		if displayedFirst then
			list = list .. PLAYER_LIST_DELIMITER
		else
			displayedFirst = true
		end
		list = list .. RGBTableToColorCode(ChatTypeInfo.GUILD) .. guildMates[i] .. FONT_COLOR_CODE_CLOSE
	end
	return list
end

-------------------------------------------------------
----------Application dialog functions
-------------------------------------------------------
function LFGListApplicationDialog_OnLoad(self)
	self:RegisterEvent("LFG_ROLE_UPDATE")
	self.Description.EditBox:SetScript("OnEnterPressed", nop)
	self.hideOnEscape = true
end

function LFGListApplicationDialog_OnEvent(self, event)
	if event == "LFG_ROLE_UPDATE" then LFGListApplicationDialog_UpdateRoles(self) end
end

function LFGListApplicationDialog_Show(self, resultID)
	self.resultID = resultID
	self.Description.EditBox:SetText("")
	LFGListApplicationDialog_UpdateRoles(self)
	StaticPopupSpecial_Show(self)
end

function LFGListApplicationDialog_UpdateRoles(self)
	local availTank, availHealer, availDPS = C_LFGList.GetAvailableRoles()

	local avail1, avail2
	if availTank then avail1 = self.TankButton end
	if availHealer then
		if avail1 then
			avail2 = self.HealerButton
		else
			avail1 = self.HealerButton
		end
	end
	if availDPS then
		if avail1 then
			avail2 = self.DamagerButton
		else
			avail1 = self.DamagerButton
		end
	end

	self.TankButton:SetShown(availTank)
	self.HealerButton:SetShown(availHealer)
	self.DamagerButton:SetShown(availDPS)

	if avail2 then
		avail1:ClearAllPoints()
		avail1:SetPoint("TOPRIGHT", self, "TOP", -5, -35)
		avail2:ClearAllPoints()
		avail2:SetPoint("TOPLEFT", self, "TOP", 5, -35)
	elseif avail1 then
		avail1:ClearAllPoints()
		avail1:SetPoint("TOP", self, "TOP", 0, -35)
	end

	local _, tank, healer, dps = GetLFGRoles()
	self.TankButton.CheckButton:SetChecked(tank)
	self.HealerButton.CheckButton:SetChecked(healer)
	self.DamagerButton.CheckButton:SetChecked(dps)

	LFGListApplicationDialog_UpdateValidState(self)
end

function LFGListApplicationDialog_UpdateValidState(self)
	if
		(self.TankButton:IsShown() and self.TankButton.CheckButton:GetChecked())
		or (self.HealerButton:IsShown() and self.HealerButton.CheckButton:GetChecked())
		or (self.DamagerButton:IsShown() and self.DamagerButton.CheckButton:GetChecked())
	then
		self.SignUpButton:Enable()
		self.SignUpButton.errorText = nil
	else
		self.SignUpButton:Disable()
		self.SignUpButton.errorText = LFG_LIST_MUST_SELECT_ROLE
	end
end

function LFGListRoleButtonCheckButton_OnClick(self)
	if self:GetChecked() then
		PlaySound("igMainMenuOptionCheckBoxOn")
	else
		PlaySound("igMainMenuOptionCheckBoxOff")
	end

	local dialog = self:GetParent():GetParent()
	local leader, tank, healer, dps = GetLFGRoles()
	SetLFGRoles(
		leader,
		dialog.TankButton.CheckButton:GetChecked(),
		dialog.HealerButton.CheckButton:GetChecked(),
		dialog.DamagerButton.CheckButton:GetChecked()
	)
end

-------------------------------------------------------
----------Invite dialog functions
-------------------------------------------------------
function LFGListInviteDialog_OnLoad(self)
	self:RegisterEvent("LFG_LIST_SEARCH_RESULTS_RECEIVED")
	self:RegisterEvent("LFG_LIST_SEARCH_RESULT_UPDATED")
	self:RegisterEvent("LFG_LIST_JOINED_GROUP")
	self:RegisterEvent("PARTY_LEADER_CHANGED")
	self:RegisterEvent("UNIT_CONNECTION")
end

function LFGListInviteDialog_OnEvent(self, event, ...)
	if event == "LFG_LIST_SEARCH_RESULTS_RECEIVED" then
		LFGListInviteDialog_CheckPending(self)
	elseif event == "LFG_LIST_SEARCH_RESULT_UPDATED" then
		local id = ...
		local _, status, pendingStatus = C_LFGList.GetApplicationInfo(id)

		local empowered = LFGListUtil_IsAppEmpowered()
		if self.resultID == id and not self.informational and (status ~= "invited" or not empowered) then
			--Check if we need to hide the panel
			StaticPopupSpecial_Hide(self)
			LFGListInviteDialog_CheckPending(self)
		elseif status == "invited" and not pendingStatus then
			--Check if we need to show this result
			LFGListInviteDialog_CheckPending(self)
		end
	elseif event == "PARTY_LEADER_CHANGED" then
		--Check if we need to hide the current panel
		if not LFGListUtil_IsAppEmpowered() and self:IsShown() and not self.informational then
			StaticPopupSpecial_Hide(self)
		end

		--Check if we need to show any panels
		LFGListInviteDialog_CheckPending(self)
	elseif event == "LFG_LIST_JOINED_GROUP" then
		if not LFGListUtil_IsAppEmpowered() then
			--Show the informational dialog, regardless of whether we already had something up
			local id = ...
			StaticPopupSpecial_Hide(self)
			LFGListInviteDialog_Show(self, id)
		end
	elseif event == "UNIT_CONNECTION" then
		LFGListInviteDialog_UpdateOfflineNotice(self)
	end
end

function LFGListInviteDialog_CheckPending(self)
	--If we're already showing one, don't replace it
	if self:IsShown() then return end

	--If we're not empowered to make changes to applications, don't pop up anything.
	if not LFGListUtil_IsAppEmpowered() then return end

	local apps = C_LFGList.GetApplications()
	for i = 1, #apps do
		local id, status, pendingStatus = C_LFGList.GetApplicationInfo(apps[i])
		if status == "invited" and not pendingStatus then
			LFGListInviteDialog_Show(self, apps[i])
			return
		end
	end
end

function LFGListInviteDialog_Show(self, resultID)
	local id, activityID, name, comment, voiceChat, iLvl, honorLevel, age, numBNetFriends, numCharFriends, numGuildMates, isDelisted =
		C_LFGList.GetSearchResultInfo(resultID)
	local activityName = C_LFGList.GetActivityInfo(activityID)
	local _, status, _, _, role = C_LFGList.GetApplicationInfo(resultID)

	local informational = (status ~= "invited")
	assert(not informational or status == "inviteaccepted")

	self.resultID = resultID
	self.GroupName:SetText(name)
	self.ActivityName:SetText(activityName)
	self.Role:SetText(_G[role])
	self.RoleIcon:SetTexCoord(GetTexCoordsForRole(role))
	self.Label:SetText(informational and LFG_LIST_JOINED_GROUP_NOTICE or LFG_LIST_INVITED_TO_GROUP)

	self.informational = informational
	self.AcceptButton:SetShown(not informational)
	self.DeclineButton:SetShown(not informational)
	self.AcknowledgeButton:SetShown(informational)

	if not informational and GroupHasOfflineMember(LE_PARTY_CATEGORY_HOME) then
		self:SetHeight(250)
		self.OfflineNotice:Show()
		LFGListInviteDialog_UpdateOfflineNotice(self)
	else
		self:SetHeight(210)
		self.OfflineNotice:Hide()
	end

	StaticPopupSpecial_Show(self)

	PlaySound("ReadyCheck")
	FlashClientIcon()
end

function LFGListInviteDialog_UpdateOfflineNotice(self)
	if GroupHasOfflineMember(LE_PARTY_CATEGORY_HOME) then
		self.OfflineNotice:SetText(LFG_LIST_OFFLINE_MEMBER_NOTICE)
		self.OfflineNotice:SetFontObject(GameFontRed)
	else
		self.OfflineNotice:SetText(LFG_LIST_OFFLINE_MEMBER_NOTICE_GONE)
		self.OfflineNotice:SetFontObject(GameFontGreen)
	end
end

function LFGListInviteDialog_Accept(self)
	C_LFGList.AcceptInvite(self.resultID)
	StaticPopupSpecial_Hide(self)
	LFGListInviteDialog_CheckPending(self)
end

function LFGListInviteDialog_Decline(self)
	C_LFGList.DeclineInvite(self.resultID)
	StaticPopupSpecial_Hide(self)
	LFGListInviteDialog_CheckPending(self)
end

function LFGListInviteDialog_Acknowledge(self)
	StaticPopupSpecial_Hide(self)
	LFGListInviteDialog_CheckPending(self)
end

-------------------------------------------------------
----------Group Data Display functions
-------------------------------------------------------
function LFGListGroupDataDisplay_Update(self, activityID, displayData, disabled)
	local fullName, shortName, categoryID, groupID, iLevel, filters, minLevel, maxPlayers, displayType =
		C_LFGList.GetActivityInfo(activityID)
	if displayType == LE_LFG_LIST_DISPLAY_TYPE_ROLE_COUNT then
		self.RoleCount:Show()
		self.Enumerate:Hide()
		self.PlayerCount:Hide()
		LFGListGroupDataDisplayRoleCount_Update(self.RoleCount, displayData, disabled)
	elseif displayType == LE_LFG_LIST_DISPLAY_TYPE_ROLE_ENUMERATE then
		self.RoleCount:Hide()
		self.Enumerate:Show()
		self.PlayerCount:Hide()
		LFGListGroupDataDisplayEnumerate_Update(
			self.Enumerate,
			maxPlayers,
			displayData,
			disabled,
			LFG_LIST_GROUP_DATA_ROLE_ORDER
		)
	elseif displayType == LE_LFG_LIST_DISPLAY_TYPE_CLASS_ENUMERATE then
		self.RoleCount:Hide()
		self.Enumerate:Show()
		self.PlayerCount:Hide()
		LFGListGroupDataDisplayEnumerate_Update(
			self.Enumerate,
			maxPlayers,
			displayData,
			disabled,
			LFG_LIST_GROUP_DATA_CLASS_ORDER
		)
	elseif displayType == LE_LFG_LIST_DISPLAY_TYPE_PLAYER_COUNT then
		self.RoleCount:Hide()
		self.Enumerate:Hide()
		self.PlayerCount:Show()
		LFGListGroupDataDisplayPlayerCount_Update(self.PlayerCount, displayData, disabled)
	elseif displayType == LE_LFG_LIST_DISPLAY_TYPE_HIDE_ALL then
		self.RoleCount:Hide()
		self.Enumerate:Hide()
		self.PlayerCount:Hide()
	else
		GMError("Unknown display type")
		self.RoleCount:Hide()
		self.Enumerate:Hide()
		self.PlayerCount:Hide()
	end
end

function LFGListGroupDataDisplayRoleCount_Update(self, displayData, disabled)
	self.TankCount:SetText(displayData.TANK)
	self.HealerCount:SetText(displayData.HEALER)
	self.DamagerCount:SetText(displayData.DAMAGER)

	--Update for the disabled state
	local r = disabled and LFG_LIST_DELISTED_FONT_COLOR.r or HIGHLIGHT_FONT_COLOR.r
	local g = disabled and LFG_LIST_DELISTED_FONT_COLOR.g or HIGHLIGHT_FONT_COLOR.g
	local b = disabled and LFG_LIST_DELISTED_FONT_COLOR.b or HIGHLIGHT_FONT_COLOR.b
	self.TankCount:SetTextColor(r, g, b)
	self.HealerCount:SetTextColor(r, g, b)
	self.DamagerCount:SetTextColor(r, g, b)
	self.TankIcon:SetDesaturated(disabled)
	self.HealerIcon:SetDesaturated(disabled)
	self.DamagerIcon:SetDesaturated(disabled)
	self.TankIcon:SetAlpha(disabled and 0.5 or 0.70)
	self.HealerIcon:SetAlpha(disabled and 0.5 or 0.70)
	self.DamagerIcon:SetAlpha(disabled and 0.5 or 0.70)
end

function LFGListGroupDataDisplayEnumerate_Update(self, numPlayers, displayData, disabled, iconOrder)
	--Show/hide the required icons
	for i = 1, #self.Icons do
		if i > numPlayers then
			self.Icons[i]:Hide()
		else
			self.Icons[i]:Show()
			self.Icons[i]:SetDesaturated(disabled)
			self.Icons[i]:SetAlpha(disabled and 0.5 or 1.0)
		end
	end

	--Note that icons are numbered from right to left
	local iconIndex = numPlayers
	for i = 1, #iconOrder do
		for j = 1, displayData[iconOrder[i]] do
			self.Icons[iconIndex]:SetAtlas(LFG_LIST_GROUP_DATA_ATLASES[iconOrder[i]], false)
			iconIndex = iconIndex - 1
			if iconIndex < 1 then return end
		end
	end

	for i = 1, iconIndex do
		self.Icons[i]:SetAtlas("groupfinder-icon-emptyslot", false)
	end
end

function LFGListGroupDataDisplayPlayerCount_Update(self, displayData, disabled)
	local numPlayers = displayData.TANK + displayData.HEALER + displayData.DAMAGER + displayData.NOROLE

	local color = disabled and LFG_LIST_DELISTED_FONT_COLOR or HIGHLIGHT_FONT_COLOR
	self.Count:SetText(numPlayers)
	self.Count:SetTextColor(color.r, color.g, color.b)
	self.Icon:SetDesaturated(disabled)
	self.Icon:SetAlpha(disabled and 0.5 or 1)
end

-------------------------------------------------------
----------Edit Box functions
-------------------------------------------------------
function LFGListEditBox_AddToTabCategory(self, tabCategory)
	self.tabCategory = tabCategory
	local cat = LFG_LIST_EDIT_BOX_TAB_CATEGORIES[tabCategory]
	if not cat then
		cat = {}
		LFG_LIST_EDIT_BOX_TAB_CATEGORIES[tabCategory] = cat
	end
	self.tabCategoryIndex = #cat + 1
	cat[self.tabCategoryIndex] = self
end

function LFGListEditBox_OnTabPressed(self)
	if self.tabCategory then
		local offset = IsShiftKeyDown() and -1 or 1
		local cat = LFG_LIST_EDIT_BOX_TAB_CATEGORIES[self.tabCategory]
		if cat then
			--It's times like this when I wish Lua was 0-based...
			cat[((self.tabCategoryIndex - 1 + offset + #cat) % #cat) + 1]:SetFocus()
		end
	end
end

-------------------------------------------------------
----------Requirement functions
-------------------------------------------------------
function LFGListRequirement_Validate(self, text)
	if self.validateFunc then
		self.warningText = self:validateFunc(text)
		self.WarningFrame:SetShown(self.warningText)
		self.CheckButton:SetShown(not self.warningText)
	end
	LFGListEntryCreation_UpdateValidState(self:GetParent())
end

-------------------------------------------------------
----------Utility functions
-------------------------------------------------------
function LFGListUtil_AugmentWithBest(filters, categoryID, groupID, activityID)
	local myNumMembers = math.max(GetNumGroupMembers(LE_PARTY_CATEGORY_HOME), 1)
	local myItemLevel = GetAverageItemLevel()
	if not activityID then
		--Find the best activity by iLevel and recommended flag
		local activities = C_LFGList.GetAvailableActivities(categoryID, groupID, filters)
		local bestItemLevel, bestRecommended, bestCurrentArea, bestMinLevel, bestMaxPlayers
		for i = 1, #activities do
			local fullName, shortName, categoryID, groupID, iLevel, filters, minLevel, maxPlayers, displayType, orderIndex, useHonorLevel =
				C_LFGList.GetActivityInfo(activities[i])
			local isRecommended = bit.band(filters, LE_LFG_LIST_FILTER_RECOMMENDED) ~= 0
			local currentArea = C_LFGList.GetActivityInfoExpensive(activities[i])

			local usedItemLevel = myItemLevel
			local isBetter = false
			if not activityID then
				isBetter = true
			elseif currentArea ~= bestCurrentArea then
				isBetter = currentArea
			elseif bestRecommended ~= isRecommended then
				isBetter = isRecommended
			elseif bestMinLevel ~= minLevel then
				isBetter = minLevel > bestMinLevel
			elseif iLevel ~= bestItemLevel then
				isBetter = (iLevel > bestItemLevel and iLevel <= usedItemLevel)
					or (iLevel <= usedItemLevel and bestItemLevel > usedItemLevel)
					or (iLevel < bestItemLevel and iLevel > usedItemLevel)
			elseif (myNumMembers < maxPlayers) ~= (myNumMembers < bestMaxPlayers) then
				isBetter = myNumMembers < maxPlayers
			end

			if isBetter then
				activityID = activities[i]
				bestItemLevel = iLevel
				bestRecommended = isRecommended
				bestCurrentArea = currentArea
				bestMinLevel = minLevel
				bestMaxPlayers = maxPlayers
			end
		end
	end

	assert(activityID)

	--Update the categoryID and groupID with what we get from the activity
	categoryID, groupID, _, filters = select(ACTIVITY_RETURN_VALUES.categoryID, C_LFGList.GetActivityInfo(activityID))

	--Update the filters if needed
	local _, separateRecommended = C_LFGList.GetCategoryInfo(categoryID)
	if separateRecommended then
		if bit.band(filters, LE_LFG_LIST_FILTER_RECOMMENDED) == 0 then
			filters = LE_LFG_LIST_FILTER_NOT_RECOMMENDED
		else
			filters = LE_LFG_LIST_FILTER_RECOMMENDED
		end
	else
		filters = 0
	end

	return filters, categoryID, groupID, activityID
end

function LFGListUtil_SetUpDropDown(context, dropdown, populateFunc, onClickFunc)
	local onClick = function(self, ...) onClickFunc(context, self.value, ...) end
	local initialize = function(self)
		local info = UIDropDownMenu_CreateInfo()
		info.func = onClick
		populateFunc(context, dropdown, info)
	end
	dropdown:SetScript("OnShow", function(self)
		UIDropDownMenu_SetWidth(self, dropdown:GetWidth() - 50)
		UIDropDownMenu_Initialize(self, initialize)
	end)
	UIDropDownMenu_JustifyText(dropdown, "LEFT")
	UIDropDownMenu_SetAnchor(dropdown, -20, 7, "TOPRIGHT", dropdown, "BOTTOMRIGHT")
end

function LFGListUtil_ValidateLevelReq(self, text)
	local myItemLevel = GetAverageItemLevel()
	if text ~= "" and tonumber(text) > myItemLevel then return LFG_LIST_ILVL_ABOVE_YOURS end
end

function LFGListUtil_ValidateHonorLevelReq(self, text)
	local myHonorLevel = UnitHonorLevel("player")
	if text ~= "" and tonumber(text) > myHonorLevel then return LFG_LIST_HONOR_LEVEL_ABOVE_YOURS end
end

function LFGListUtil_GetCurrentExpansion()
	for i = 0, #MAX_PLAYER_LEVEL_TABLE do
		if UnitLevel("player") <= MAX_PLAYER_LEVEL_TABLE[i] then return i end
	end

	--We're higher than the highest level. Weird.
	return #MAX_PLAYER_LEVEL_TABLE
end

function LFGListUtil_GetDecoratedCategoryName(categoryName, filter, useColors)
	if filter == 0 then return categoryName end

	local colorStart = ""
	local colorEnd = ""
	if useColors then
		colorStart = "|cffffffff"
		colorEnd = "|r"
	end

	local extraName = ""
	if filter == LE_LFG_LIST_FILTER_NOT_RECOMMENDED then
		extraName = LFG_LIST_LEGACY
	elseif filter == LE_LFG_LIST_FILTER_RECOMMENDED then
		local exp = LFGListUtil_GetCurrentExpansion()
		extraName = _G["EXPANSION_NAME" .. exp]
	end

	return string.format(LFG_LIST_CATEGORY_FORMAT, categoryName, colorStart, extraName, colorEnd)
end

function LFGListUtil_SortSearchResultsCB(id1, id2)
	local id1, activityID1, name1, comment1, voiceChat1, iLvl1, honorLevel1, age1, numBNetFriends1, numCharFriends1, numGuildMates1, isDelisted1 =
		C_LFGList.GetSearchResultInfo(id1)
	local id2, activityID2, name2, comment2, voiceChat2, iLvl2, honorLevel2, age2, numBNetFriends2, numCharFriends2, numGuildMates2, isDelisted2 =
		C_LFGList.GetSearchResultInfo(id2)

	--If one has more friends, do that one first
	if numBNetFriends1 ~= numBNetFriends2 then return numBNetFriends1 > numBNetFriends2 end

	if numCharFriends1 ~= numCharFriends2 then return numCharFriends1 > numCharFriends2 end

	if numGuildMates1 ~= numGuildMates2 then return numGuildMates1 > numGuildMates2 end

	--If we aren't sorting by anything else, just go by ID
	return id1 < id2
end

function LFGListUtil_SortSearchResults(results) table.sort(results, LFGListUtil_SortSearchResultsCB) end

function LFGListUtil_FilterApplicants(applicants)
	--[[for i=#applicants, 1, -1 do
		local id, status, pendingStatus, numMembers, isNew = C_LFGList.GetApplicantInfo(applicants[i]);
		if ( status ~= "applied" and status ~= "invited" ) then
			--Remove this applicant. Don't worry about order.
			applicants[i] = applicants[#applicants];
			applicants[#applicants] = nil;
		end
	end--]]
end

function LFGListUtil_SortApplicantsCB(id1, id2)
	local _, _, _, _, isNew1, _, orderID1 = C_LFGList.GetApplicantInfo(id1)
	local _, _, _, _, isNew2, _, orderID2 = C_LFGList.GetApplicantInfo(id2)

	--New items go to the bottom
	if isNew1 ~= isNew2 then return isNew2 end

	return orderID1 < orderID2
end

function LFGListUtil_SortApplicants(applicants) table.sort(applicants, LFGListUtil_SortApplicantsCB) end

function LFGListUtil_IsAppEmpowered()
	return not IsInGroup(LE_PARTY_CATEGORY_HOME) or UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME)
end

function LFGListUtil_IsEntryEmpowered()
	return UnitIsGroupLeader("player", LE_PARTY_CATEGORY_HOME) or UnitIsGroupAssistant("player", LE_PARTY_CATEGORY_HOME)
end

function LFGListUtil_AppendStatistic(label, value, title, lastTitle)
	if title ~= lastTitle then
		GameTooltip:AddLine(" ")
		GameTooltip:AddLine(title, 1, 1, 1)
	end

	GameTooltip:AddLine(string.format(label, value))
end

local LFG_LIST_SEARCH_ENTRY_MENU = {
	{
		text = nil, --Group name goes here
		isTitle = true,
		notCheckable = true,
	},
	{
		text = WHISPER_LEADER,
		func = function(_, name) ChatFrame_SendTell(name) end,
		notCheckable = true,
		arg1 = nil, --Leader name goes here
		disabled = nil, --Disabled if we don't have a leader name yet or you haven't applied
		tooltipWhileDisabled = 1,
		tooltipOnButton = 1,
		tooltipTitle = nil, --The title to display on mouseover
		tooltipText = nil, --The text to display on mouseover
	},
	{
		text = LFG_LIST_REPORT_GROUP_FOR,
		hasArrow = true,
		notCheckable = true,
		menuList = {
			{
				text = LFG_LIST_BAD_NAME,
				func = function(_, id) C_LFGList.ReportSearchResult(id, "lfglistname") end,
				arg1 = nil, --Search result ID goes here
				notCheckable = true,
			},
			{
				text = LFG_LIST_BAD_DESCRIPTION,
				func = function(_, id) C_LFGList.ReportSearchResult(id, "lfglistcomment") end,
				arg1 = nil, --Search reuslt ID goes here
				notCheckable = true,
				disabled = nil, --Disabled if the description is just an empty string
			},
			{
				text = LFG_LIST_BAD_VOICE_CHAT_COMMENT,
				func = function(_, id) C_LFGList.ReportSearchResult(id, "lfglistvoicechat") end,
				arg1 = nil, --Search reuslt ID goes here
				notCheckable = true,
				disabled = nil, --Disabled if the description is just an empty string
			},
			{
				text = LFG_LIST_BAD_LEADER_NAME,
				func = function(_, id) C_LFGList.ReportSearchResult(id, "badplayername") end,
				arg1 = nil, --Search reuslt ID goes here
				notCheckable = true,
				disabled = nil, --Disabled if we don't have a name for the leader
			},
		},
	},
	{
		text = CANCEL,
		notCheckable = true,
	},
}

function LFGListUtil_GetSearchEntryMenu(resultID)
	local id, activityID, name, comment, voiceChat, iLvl, honorLevel, age, numBNetFriends, numCharFriends, numGuildMates, isDelisted, leaderName =
		C_LFGList.GetSearchResultInfo(resultID)
	local _, appStatus, pendingStatus, appDuration = C_LFGList.GetApplicationInfo(resultID)
	LFG_LIST_SEARCH_ENTRY_MENU[1].text = name
	LFG_LIST_SEARCH_ENTRY_MENU[2].arg1 = leaderName
	local applied = (appStatus == "applied" or appStatus == "invited")
	LFG_LIST_SEARCH_ENTRY_MENU[2].disabled = not leaderName or not applied
	LFG_LIST_SEARCH_ENTRY_MENU[2].tooltipTitle = (not applied) and WHISPER
	LFG_LIST_SEARCH_ENTRY_MENU[2].tooltipText = (not applied) and LFG_LIST_MUST_SIGN_UP_TO_WHISPER
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[1].arg1 = resultID
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[2].arg1 = resultID
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[2].disabled = (comment == "")
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[3].arg1 = resultID
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[3].disabled = (voiceChat == "")
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[4].arg1 = resultID
	LFG_LIST_SEARCH_ENTRY_MENU[3].menuList[4].disabled = not leaderName
	return LFG_LIST_SEARCH_ENTRY_MENU
end

local LFG_LIST_APPLICANT_MEMBER_MENU = {
	{
		text = nil, --Player name goes here
		isTitle = true,
		notCheckable = true,
	},
	{
		text = WHISPER,
		func = function(_, name) ChatFrame_SendTell(name) end,
		notCheckable = true,
		arg1 = nil, --Player name goes here
		disabled = nil, --Disabled if we don't have a name yet
	},
	{
		text = LFG_LIST_REPORT_FOR,
		hasArrow = true,
		notCheckable = true,
		menuList = {
			{
				text = LFG_LIST_BAD_PLAYER_NAME,
				notCheckable = true,
				func = function(_, id, memberIdx) C_LFGList.ReportApplicant(id, "badplayername", memberIdx) end,
				arg1 = nil, --Applicant ID goes here
				arg2 = nil, --Applicant Member index goes here
			},
			{
				text = LFG_LIST_BAD_DESCRIPTION,
				notCheckable = true,
				func = function(_, id) C_LFGList.ReportApplicant(id, "lfglistappcomment") end,
				arg1 = nil, --Applicant ID goes here
			},
		},
	},
	{
		text = IGNORE_PLAYER,
		notCheckable = true,
		func = function(_, name, applicantID)
			AddIgnore(name)
			C_LFGList.DeclineApplicant(applicantID)
		end,
		arg1 = nil, --Player name goes here
		arg2 = nil, --Applicant ID goes here
		disabled = nil, --Disabled if we don't have a name yet
	},
	{
		text = CANCEL,
		notCheckable = true,
	},
}

function LFGListUtil_GetApplicantMemberMenu(applicantID, memberIdx)
	local name, class, localizedClass, level, itemLevel, honorLevel, tank, healer, damage, assignedRole =
		C_LFGList.GetApplicantMemberInfo(applicantID, memberIdx)
	local id, status, pendingStatus, numMembers, isNew, comment = C_LFGList.GetApplicantInfo(applicantID)
	LFG_LIST_APPLICANT_MEMBER_MENU[1].text = name or " "
	LFG_LIST_APPLICANT_MEMBER_MENU[2].arg1 = name
	LFG_LIST_APPLICANT_MEMBER_MENU[2].disabled = not name or (status ~= "applied" and status ~= "invited")
	LFG_LIST_APPLICANT_MEMBER_MENU[3].menuList[1].arg1 = applicantID
	LFG_LIST_APPLICANT_MEMBER_MENU[3].menuList[1].arg2 = memberIdx
	LFG_LIST_APPLICANT_MEMBER_MENU[3].menuList[2].arg1 = applicantID
	LFG_LIST_APPLICANT_MEMBER_MENU[3].menuList[2].disabled = (comment == "")
	LFG_LIST_APPLICANT_MEMBER_MENU[4].arg1 = name
	LFG_LIST_APPLICANT_MEMBER_MENU[4].arg2 = applicantID
	LFG_LIST_APPLICANT_MEMBER_MENU[4].disabled = not name
	return LFG_LIST_APPLICANT_MEMBER_MENU
end

function LFGListUtil_InitializeLangaugeFilter(dropdown)
	local info = UIDropDownMenu_CreateInfo()
	local languages = C_LFGList.GetAvailableLanguageSearchFilter()
	local enabled = C_LFGList.GetLanguageSearchFilter()
	local defaults = C_LFGList.GetDefaultLanguageSearchFilter()
	local entry = UIDropDownMenu_CreateInfo()
	for i = 1, #languages do
		local lang = languages[i]
		entry.text = _G["LFG_LIST_LANGUAGE_" .. string.upper(lang)]
		entry.checked = enabled[lang] or defaults[lang]
		entry.disabled = defaults[lang]
		entry.isNotRadio = true
		entry.keepShownOnClick = true
		entry.func = function(self, _, _, checked)
			enabled[lang] = checked
			C_LFGList.SaveLanguageSearchFilter(enabled)
		end
		UIDropDownMenu_AddButton(entry)
	end
end

function LFGListUtil_OpenBestWindow(toggle)
	local func = toggle and PVEFrame_ToggleFrame or PVEFrame_ShowFrame
	local active, activityID, ilvl, honorLevel, name, comment, voiceChat = C_LFGList.GetActiveEntryInfo()
	if active then
		--Open to the window of our active activity
		local fullName, shortName, categoryID, groupID, iLevel, filters = C_LFGList.GetActivityInfo(activityID)

		if bit.band(filters, LE_LFG_LIST_FILTER_PVE) ~= 0 then
			func("GroupFinderFrame", "LFGListPVEStub")
		else
			func("PVPUIFrame", "LFGListPVPStub")
		end
	else
		--Open to the last window we had open
		if bit.band(LFGListFrame.baseFilters, LE_LFG_LIST_FILTER_PVE) ~= 0 then
			func("GroupFinderFrame", "LFGListPVEStub")
		else
			func("PVPUIFrame", "LFGListPVPStub")
		end
	end
end

function LFGListUtil_SortActivitiesByRelevancyCB(id1, id2)
	local fullName1, _, _, _, iLevel1, _, minLevel1 = C_LFGList.GetActivityInfo(id1)
	local fullName2, _, _, _, iLevel2, _, minLevel2 = C_LFGList.GetActivityInfo(id2)

	if minLevel1 ~= minLevel2 then
		return minLevel1 > minLevel2
	elseif iLevel1 ~= iLevel2 then
		local myILevel = GetAverageItemLevel()

		if (iLevel1 <= myILevel) ~= (iLevel2 <= myILevel) then
			--If one is below our item level and the other above, choose the one we meet
			return iLevel1 < myILevel
		else
			--If both are above or both are below, choose the one closest to our iLevel
			return math.abs(iLevel1 - myILevel) < math.abs(iLevel2 - myILevel)
		end
	else
		return strcmputf8i(fullName1, fullName2) < 0
	end
end

function LFGListUtil_SortActivitiesByRelevancy(activities)
	table.sort(activities, LFGListUtil_SortActivitiesByRelevancyCB)
end

LFG_LIST_ACTIVE_QUEUE_MESSAGE_EVENTS = {
	"LFG_LIST_ACTIVE_ENTRY_UPDATE",
	"LFG_LIST_SEARCH_RESULT_UPDATED",
	"PVP_ROLE_CHECK_UPDATED",
	"UPDATE_BATTLEFIELD_STATUS",
	"LFG_UPDATE",
	"LFG_ROLE_CHECK_UPDATE",
	"LFG_PROPOSAL_UPDATE",
	"LFG_PROPOSAL_FAILED",
	"LFG_PROPOSAL_SUCCEEDED",
	"LFG_PROPOSAL_SHOW",
	"LFG_QUEUE_STATUS_UPDATE",
}

function LFGListUtil_GetActiveQueueMessage(isApplication)
	--Check for applications if we're trying to list
	if not isApplication and select(2, C_LFGList.GetNumApplications()) > 0 then
		return CANNOT_DO_THIS_WITH_LFGLIST_APP
	end

	--Check for listings if we have an application
	if isApplication and C_LFGList.GetActiveEntryInfo() then return CANNOT_DO_THIS_WHILE_LFGLIST_LISTED end

	--Check all LFG categories
	for category = 1, NUM_LE_LFG_CATEGORYS do
		local mode = GetLFGMode(category)
		if mode then
			if mode == "lfgparty" then
				return CANNOT_DO_THIS_IN_LFG_PARTY
			elseif mode == "rolecheck" or (mode and not isApplication) then
				return CANNOT_DO_THIS_IN_PVE_QUEUE
			end
		end
	end

	--Check PvP role check
	local inProgress, _, _, _, _, isBattleground = GetLFGRoleUpdate()
	if inProgress then
		return isBattleground and CANNOT_DO_THIS_WHILE_PVP_QUEUING or CANNOT_DO_THIS_WHILE_PVE_QUEUING
	end

	for i = 1, GetMaxBattlefieldID() do
		local status, mapName, teamSize, registeredMatch, suspend = GetBattlefieldStatus(i)
		if status and status ~= "none" then return CANNOT_DO_THIS_IN_BATTLEGROUND end
	end
end

local LFG_LIST_INACTIVE_STATUSES = {
	cancelled = true,
	failed = true,
	declined = true,
	timedout = true,
	invitedeclined = true,
}

function LFGListUtil_IsStatusInactive(status) return LFG_LIST_INACTIVE_STATUSES[status] end

function LFGListUtil_SetAutoAccept(autoAccept)
	local active, activityID, iLevel, honorLevel, name, comment, voiceChat, expiration, oldAutoAccept =
		C_LFGList.GetActiveEntryInfo()
	if not active then
		--If we're not listed, we can't change the value.
		return
	end

	C_LFGList.UpdateListing(activityID, name, iLevel, honorLevel, voiceChat, comment, autoAccept)
end
