local _, addon = ...
local API = addon.API;
local L = addon.L;
local GetDBBool = addon.GetDBBool;


local ActivityUtil = {};
addon.ActivityUtil = ActivityUtil;

ActivityUtil.hideCompleted = false;


local ipairs = ipairs;
local tsort = table.sort;
local format = string.format;


local IsQuestFlaggedCompleted = C_QuestLog.IsQuestFlaggedCompleted;
local IsQuestFlaggedCompletedOnAccount = C_QuestLog.IsQuestFlaggedCompletedOnAccount;
local GetQuestClassification = C_QuestInfoSystem.GetQuestClassification;
local IsOnQuest = C_QuestLog.IsOnQuest;
local GetQuestLineInfo = C_QuestLine.GetQuestLineInfo;
local GetItemCount = C_Item.GetItemCount;
local HaveQuestData = HaveQuestData;
local GetCurrentRenownLevel = C_MajorFactions.GetCurrentRenownLevel;
local IsWorldQuestActive = C_TaskQuest.IsActive;
local ReadyForTurnIn = API.IsQuestReadyForTurnIn;


local function ShownIfOnQuest(questID)
	return questID and IsOnQuest(questID)
end

local function ShownIfActive(questID)
	return questID and (IsOnQuest(questID) or IsWorldQuestActive(questID))
end

local function IsCategoryCollapsed(categoryID)
	return GetDBBool("LandingPage_Activity_Collapsed_"..categoryID)
end

local function SetCategoryCollapsed(categoryID, isCollapsed)
	return addon.SetDBValue("LandingPage_Activity_Collapsed_"..categoryID, isCollapsed, true)
end


local ActivityData = {};
local SortedActivity;
local MapQuestData;     --Show quests available on certain maps. The quest markers need to be visible on the world map

local DELVES_REP_TOOLTIP = L["Bountiful Delves Rep Tooltip"];


local DynamicQuestDataProvider = {};


local ConditionFuncs = {};
do
	function ConditionFuncs.OwnItem(itemInfo)
		--itemInfo can be item name
		if itemInfo then
			return GetItemCount(itemInfo) > 0
		end
		return false
	end
end


local ShownQuestClassification = {
	--Show these types of quests
	[Enum.QuestClassification.Recurring] = true,
	[Enum.QuestClassification.Meta] = true,
	[Enum.QuestClassification.Calling] = true,
};


local QuestIcon = {
	WEEKLY_QUEST = "Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/WeeklyQuestAvailable.png",
	PREY_ACTIVE = "Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressPrey.png",
	Boss = "Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/TrackerType-Boss.png",
};


local QuestIconAtlas = {
	[Enum.QuestClassification.Normal] = 	"QuestNormal",
	[Enum.QuestClassification.Questline] = 	"QuestNormal",
	[Enum.QuestClassification.Recurring] =	"quest-recurring-available",
	[Enum.QuestClassification.Meta] = 		"quest-wrapper-available",
	[Enum.QuestClassification.Calling] = 	"Quest-DailyCampaign-Available",
	[Enum.QuestClassification.Campaign] = 	"Quest-Campaign-Available",
	[Enum.QuestClassification.Legendary] =	"UI-QuestPoiLegendary-QuestBang",
	[Enum.QuestClassification.Important] =	"importantavailablequesticon",

	DELVES_BOUNTIFUL = "delves-bountiful",
	DAILY_QUEST = "quest-recurring-available",
};
ActivityUtil.QuestIconAtlas = QuestIconAtlas;


local InProgressQuestIconFile = {
	[Enum.QuestClassification.Normal] = 	"Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressRed.png",
	[Enum.QuestClassification.Questline] = 	"Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressBlue.png",
	[Enum.QuestClassification.Recurring] =	"Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressBlue.png",
	[Enum.QuestClassification.Meta] = 		"Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressBlue.png",
	[Enum.QuestClassification.WorldQuest] = "Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/InProgressBlue.png",

	[128] = "Interface/AddOns/Plumber/Art/ExpansionLandingPage/Icons/Checklist.png",
};


local Conditions = {};
do
	ActivityUtil.Conditions = Conditions;

	Conditions.ItemReadyToTurnInWhenLooted_ItemID = {
		ShouldShowActivity = ConditionFuncs.OwnItem,
		IsReadyForTurnIn = ConditionFuncs.OwnItem,
	};

	Conditions.ItemReadyToTurnInWhenLooted_ItemName = {
		ShouldShowActivity = ConditionFuncs.OwnItem,
		IsReadyForTurnIn = ConditionFuncs.OwnItem,
		useItemName = true,
	};

	Conditions.KareshWarrant = {
		ShouldShowActivity = function()
			return GetCurrentRenownLevel(2658) >= 3
		end,
	};

	Conditions.DelversBounty = {
		ShouldShowActivity = function()
			return GetCurrentRenownLevel(2722) >= 2
		end,
	};
end


local TooltipFuncs = {};
do
	ActivityUtil.TooltipFuncs = TooltipFuncs;

	local function ShouldShowAdvancedTooltip()
		return GetDBBool("LandingPage_AdvancedTooltip");
	end

	--Similar to Bullet list
	local function Tooltip_AddListNewLine(tooltip, text, r, g, b)
		tooltip:AddLine("|TInterface/AddOns/Plumber/Art/Tooltip/TabChar_Dash:0:0|t"..text, r, g, b);
	end
	local function Tooltip_AddListInLine(tooltip, text, r, g, b)
		tooltip:AddLine("|TInterface/AddOns/Plumber/Art/Tooltip/TabChar_Space:0:0|t"..text, r, g, b);
	end

	local function Tooltip_AddListQuest(tooltip, questID, questName)
		if IsQuestFlaggedCompleted(questID) then
			Tooltip_AddListInLine(tooltip, questName, 0.251, 0.753, 0.251)
		else
			Tooltip_AddListInLine(tooltip, questName, 0.5, 0.5, 0.5);
		end
	end


	function TooltipFuncs.DevouredEnergyPod(tooltip)
		--Devoured Energy-Pod (20)  Translocated Gorger
		--Add item count if mount not learnt
		if API.IsMountCollected(2602) then return true end;
		local quantityRequired = 20;
		tooltip:AddLine(" ");
		API.AddCraftingReagentToTooltip(tooltip, 246240, quantityRequired);
		return true
	end

	function TooltipFuncs.WeeklyCofferKey_Shared(tooltip, title, dataKey)
		local loaded = true;
		local keepUpdating = false;

		tooltip:AddLine(title, 1, 1, 1, true);

		local tbl = addon.WeeklyRewardsConstant;

		if ShouldShowAdvancedTooltip() then
			for _, itemID in ipairs(tbl[dataKey]) do
				local itemName = C_Item.GetItemNameByID(itemID);
				local sources = tbl.ChestSources[itemID];

				if itemName then
					tooltip:AddLine(" ");
					Tooltip_AddListNewLine(tooltip, itemName, 1, 0.82, 0);
				else
					loaded = false;
				end

				if sources then
					local quests = sources.quests or (sources.questMap and DynamicQuestDataProvider:GetQuestsByMap(sources.questMap));
					if quests then
						if sources.questMap then
							keepUpdating = true;
							for _, questInfo in ipairs(quests) do
								local questID = questInfo.questID;
								local rewards, missingData = API.GetQuestRewards(questID);
								if missingData then
									loaded = false;
								end
								if rewards then
									if rewards.items then
										for _, v in ipairs(rewards.items) do
											if v.id == itemID then
												local questName = API.GetQuestName(questID);
												if questName then
													Tooltip_AddListQuest(tooltip, questID, questName);
												else
													loaded = false;
												end
												break
											end
										end
									end
								end
							end
						else
							for _, questID in ipairs(quests) do
								local questName = API.GetQuestName(questID);
								if questName then
									Tooltip_AddListQuest(tooltip, questID, questName);
								else
									loaded = false;
								end
							end
						end
					end
				end
			end
		else
			tooltip:AddLine(" ");
			for _, itemID in ipairs(tbl.MajorChests) do
				local name = C_Item.GetItemNameByID(itemID);
				if name then
					tooltip:AddLine("- "..name, 1, 1, 1, false);
				else
					loaded = false;
				end
			end
		end

		return loaded, keepUpdating
	end

	function TooltipFuncs.WeeklyRestoredCofferKey(tooltip)
		return TooltipFuncs.WeeklyCofferKey_Shared(tooltip, L["Weekly Coffer Key Tooltip"], "MajorChests");
	end

	function TooltipFuncs.WeeklyCofferKeyShard(tooltip)
		return TooltipFuncs.WeeklyCofferKey_Shared(tooltip, L["Weekly Coffer Key Shards Tooltip"], "MinorChests");
	end

	function TooltipFuncs.WeeklyBonusRenown(tooltip, flagQuests)
		for _, v in ipairs(flagQuests) do
			local factionName = addon.FactionUtil:GetFactionName(v.factionID);
			if factionName then
				if C_QuestLog.IsQuestFlaggedCompletedOnAccount(v.questID) then
					tooltip:AddLine("- "..factionName, 0.251, 0.753, 0.251, false);
				else
					tooltip:AddLine("- "..factionName, 0.5, 0.5, 0.5, false);
				end
			end
		end

		tooltip:AddLine(" ");
		tooltip:AddLine(L["Bountiful Delves Rep Tooltip"], 1, 1, 1, true);
		tooltip:AddLine(" ");
		tooltip:AddLine(L["Warband Weekly Reward Tooltip"], 0, 0.8, 1, true);

		return true
	end
end


local function CreateChildrenFromQuestList(list)
	local tbl = {};
	for i, questID in ipairs(list) do
		tbl[i] = {questID = questID};
	end
	return tbl
end
ActivityUtil.CreateChildrenFromQuestList = CreateChildrenFromQuestList;


local SortFuncs = {};
do
	function SortFuncs.DataIndex(a, b)
		return a.dataIndex < b.dataIndex
	end

	function SortFuncs.IncompleteFirst(a, b)
		if a.completed ~= b.completed then
			return b.completed
		end

		if (a.isOnQuest ~= nil) and (b.isOnQuest ~= nil) and a.isOnQuest ~= b.isOnQuest then
			return b.isOnQuest
		end

		if (a.readyForTurnIn ~= nil) and (b.readyForTurnIn ~= nil) and a.readyForTurnIn ~= b.readyForTurnIn then
			return b.readyForTurnIn
		end

		if a.sortToTop ~= b.sortToTop then
			return a.sortToTop
		end

		return a.dataIndex < b.dataIndex
	end

	function SortFuncs.ClassificationThenQuestID(a, b)
		if a.questClassification and b.questClassification then
			if a.questClassification ~= b.questClassification then
				return a.questClassification < b.questClassification
			end
		else
			return a.questClassification ~= nil
		end

		if a.questID and b.questID then
			return a.questID > b.questID
		end

		return a.name < b.name
	end
end


local function InitQuestData(info)
	local questClassification = info.questClassification or GetQuestClassification(info.questID);

	if HaveQuestData(info.questID) then
		info.questClassification = questClassification;
	end

	info.isOnQuest = IsOnQuest(info.questID);

	if info.isBoss then
		info.icon = QuestIcon.Boss;
	end

	if info.isOnQuest then
		info.icon = questClassification and InProgressQuestIconFile[questClassification];
		info.readyForTurnIn = ReadyForTurnIn(info.questID);
	else
		info.readyForTurnIn = false;
	end

	if not info.atlas then
		info.atlas = questClassification and QuestIconAtlas[questClassification] or "QuestNormal";
	end
end


do  --DynamicQuestDataProvider  Dynamic Quests are acquired using Game API, instead of using a pre-determined table
	local MapMetaQuestLines = {
		[2339] = {  --Dornogal
			5572,   --Worldsoul: Weekly Meata
		},
	};

	local MapQuests = {
		[2339] = {  --Dornogal
			{name = "Sparks of War: Azj-Khahet", questID = 81796, shownIfOnQuest = true},
			{name = "Sparks of War: Isle of Dorn", questID = 81793, shownIfOnQuest = true},
			{name = "Sparks of War: The Ringing Deeps", questID = 81794, shownIfOnQuest = true},
			{name = "Sparks of War: Hallowfall", questID = 81795, shownIfOnQuest = true},
			{name = "Sparks of War: Undermine", questID = 86853, shownIfOnQuest = true},
		},

		[2393] = {  --Silvermoon
			--Weekly Meta (https://www.wowhead.com/quests/midnight/meta-quests#0+1+13;name=Midnight%3A)
			{name = "Midnight: Abundance", questID = 93890, shownIfOnQuest = true},
			{name = "Midnight: Arcantina", questID = 93767, shownIfOnQuest = true},
			{name = "Midnight: Battlegrounds", questID = 94457, shownIfOnQuest = true},
			{name = "Midnight: Delves", questID = 93909, shownIfOnQuest = true},
			{name = "Midnight: Dungeons", questID = 93911, shownIfOnQuest = true},
			{name = "Midnight: Housing", questID = 93769, shownIfOnQuest = true},
			{name = "Midnight: Legends of the Haranir", questID = 93891, shownIfOnQuest = true},
			{name = "Midnight: Prey", questID = 93910, shownIfOnQuest = true},
			{name = "Midnight: Raid", questID = 93912, shownIfOnQuest = true},
			{name = "Midnight: Ritual Sites", questID = 95843, shownIfOnQuest = true},
			{name = "Midnight: Saltheril's Soiree", questID = 93889, shownIfOnQuest = true},
			{name = "Midnight: Stormarion Assault", questID = 93892, shownIfOnQuest = true},
			{name = "Midnight: Void Assaults", questID = 95842, shownIfOnQuest = true},
			{name = "Midnight: World Boss", questID = 93913, shownIfOnQuest = true},
			{name = "Midnight: World Quests", questID = 93766, shownIfOnQuest = true},

			--Dungeon Quest
			{name = "Windrunner Spire", questID = 93751, shownIfOnQuest = true},
			{name = "Murder Row", questID = 93752, shownIfOnQuest = true},
			{name = "Magister's Terrace", questID = 93753, shownIfOnQuest = true},
			{name = "Maisara Caverns", questID = 93754, shownIfOnQuest = true},
			{name = "Den of Nalorakk", questID = 93755, shownIfOnQuest = true},
			{name = "The Blinding Vale", questID = 93756, shownIfOnQuest = true},
			{name = "Voidscar Arena", questID = 93757, shownIfOnQuest = true},
			{name = "Nexus-Point Xenas", questID = 93758, shownIfOnQuest = true},

			--PVP
			{name = "Sparks of War: Eversong Woods", questID = 93423, shownIfOnQuest = true},
			{name = "Sparks of War: Zul'Aman", questID = 93424, shownIfOnQuest = true},
			{name = "Sparks of War: Harandar", questID = 93425, shownIfOnQuest = true},
			{name = "Sparks of War: Voidstorm", questID = 93426, shownIfOnQuest = true},
		},
	};

	local DynamicQuestMaps = {
		--Automatically find repeatable quests from these maps
		--[uiMapID] = categoryID,
	};

	function DynamicQuestDataProvider:Reset()
		self.addedQuests = {};
		self.questsByMap = {};
	end

	function DynamicQuestDataProvider:AddQuestsFromTable(uiMapID, tbl)
		if not tbl then return end;

		local n = self.questsByMap[uiMapID] and #self.questsByMap[uiMapID] or 0;
		local mapID, questID;

		for _, v in ipairs(tbl) do
			questID = v.questID;
			if questID and not self.addedQuests[questID] then
				mapID = v.startMapID or v.mapID;
				if (not v.isHidden) and mapID and mapID == uiMapID then
					InitQuestData(v);
					if ShownQuestClassification[v.questClassification] then
						self.addedQuests[questID] = true;
						n = n + 1;
						if not self.questsByMap[uiMapID] then
							self.questsByMap[uiMapID] = {};
						end
						v.isDynamicQuest = true;
						self.questsByMap[uiMapID][n] = v;
					end
				end
			end
		end
	end

	function DynamicQuestDataProvider:IsQuestActive(questID)
		return self.addedQuests[questID]
	end

	function DynamicQuestDataProvider:AddQuestsFromMap(uiMapID, categoryID)
		C_QuestLine.RequestQuestLinesForMap(uiMapID);

		self.questsByMap[uiMapID] = nil;

		self:AddQuestsFromTable(uiMapID, C_QuestLine.GetAvailableQuestLines(uiMapID));
		self:AddQuestsFromTable(uiMapID, C_TaskQuest.GetQuestsOnMap(uiMapID));

		if MapMetaQuestLines[uiMapID] then
			for _, questLineID in ipairs(MapMetaQuestLines[uiMapID]) do
				self:AddQuestsFromQuestLine(questLineID, uiMapID);
			end
		end

		if MapQuests[uiMapID] then
			local n;
			local valid;

			if self.questsByMap[uiMapID] then
				n = #self.questsByMap[uiMapID];
			else
				self.questsByMap[uiMapID] = {};
				n = 0;
			end

			for _, entry in ipairs(MapQuests[uiMapID]) do
				valid = false;
				if entry.shownIfOnQuest then
					if ShownIfOnQuest(entry.questID) then
						valid = true;
					end
				else
					valid = true;
				end

				if valid then
					n = n + 1;
					InitQuestData(entry);
					self.questsByMap[uiMapID][n] = entry;
				end
			end
		end

		if self.questsByMap[uiMapID] then
			table.sort(self.questsByMap[uiMapID], SortFuncs.ClassificationThenQuestID);
			local mapName = C_Map.GetMapInfo(uiMapID).name;
			local category = {
				isHeader = true,
				name = mapName,
				entries = self.questsByMap[uiMapID],
				isDynamicQuest = true,
				questMapID = uiMapID,
				categoryID = categoryID,
				isCollapsed = IsCategoryCollapsed(categoryID),
			};
			if not MapQuestData then
				MapQuestData = {};
			end
			table.insert(MapQuestData, category);
		end
	end

	function DynamicQuestDataProvider:AddQuestsFromQuestLine(questLineID, uiMapID)
		local questIDs = C_QuestLine.GetQuestLineQuests(questLineID);   --Dornogal meta
		if questIDs then
			local n = self.questsByMap[uiMapID] and #self.questsByMap[uiMapID] or 0;
			for _, questID in ipairs(questIDs) do
				if not self.addedQuests[questID] then
					local questLineInfo = GetQuestLineInfo(questID);
					if questLineInfo and ShownIfOnQuest(questID) then
						--print(questLineInfo.questID, questLineInfo.questName, questLineInfo.isHidden)
						InitQuestData(questLineInfo);
						if ShownQuestClassification[questLineInfo.questClassification] then
							self.addedQuests[questID] = true;
							n = n + 1;
							if not self.questsByMap[uiMapID] then
								self.questsByMap[uiMapID] = {};
							end
							questLineInfo.isDynamicQuest = true;
							self.questsByMap[uiMapID][n] = questLineInfo;
						end
					end
				end
			end
		end
	end

	function DynamicQuestDataProvider:QueryQuests()
		for uiMapID, categoryID in pairs(DynamicQuestMaps) do
			self:AddQuestsFromMap(uiMapID, categoryID);
		end
	end

	function DynamicQuestDataProvider:GetQuestsByMap(uiMapID)
		return self.questsByMap[uiMapID]
	end


	addon.CallbackRegistry:Register("LandingPage.SetActivityQuestMaps", function(activityQuestMap)
		DynamicQuestMaps = activityQuestMap or {};
		DynamicQuestDataProvider:Reset();
	end);
end


function ActivityUtil.GetActivityData(dataIndex)
	if SortedActivity then
		return SortedActivity[dataIndex]
	end
end


local QuestNames = {};
local ItemNames = {};


--- GetActivityName
--- @param dataIndex number
--- @param questID any
--- @return string name
--- @return boolean isLocalized
function ActivityUtil.GetActivityName(dataIndex, questID)
	local v = SortedActivity[dataIndex];
	if v then
		if v.nameGetter then
			return v.nameGetter(), true
		end

		if v.localizedName and ((not questID) or (questID and v.questID and questID == v.questID)) then
			--Entry's questID may change, in this case don't use cache
			return v.localizedName, true
		end

		if v.questID or questID then
			questID = v.questID or questID;
			local name = QuestNames[questID] or API.GetQuestName(questID);
			if name and name ~= "" then
				if v.removeSharedPrefix then
					name = API.RemoveTextBeforeColon(name);
				end
				v.localizedName = name;
				return name, true
			end
		end

		if v.isHeader and (v.areaID or v.factionID) then
			if v.areaID then
				local zoneName = API.GetZoneName(v.areaID);
				return zoneName, true
			end
			local data = C_Reputation.GetFactionDataByID(v.factionID);
			if data and data.name then
				v.localizedName = data.name;
				return data.name, true
			end
		end

		if v.itemID then
			local name = ItemNames[v.itemID] or C_Item.GetItemNameByID(v.itemID);
			if name then
				v.localizedName = name;
				return name, true
			end
		end

		return v.name, false
	end

	return "", true
end


function ActivityUtil.StoreQuestActivityName(questID, localizedName)
	if questID and localizedName and localizedName ~= "" then
		QuestNames[questID] = localizedName;
	end
end

function ActivityUtil.StoreItemActivityName(itemID, localizedName)
	if itemID and localizedName and localizedName ~= "" then
		ItemNames[itemID] = localizedName;
	end
end

function ActivityUtil.ShouldShowActivity(data)
	if data.shownIfOnQuest then
		return ShownIfOnQuest(data.questID)
	end

	if data.shownIfActive then
		return ShownIfActive(data.questID)
	end

	return true
end


local function IndexData(activityData)
	local n = 0;
	for _, category in ipairs(activityData) do
		n = n + 1;
		category.dataIndex = n;
		for _, entry in ipairs(category.entries) do
			n = n + 1;
			entry.dataIndex = n;
		end
	end
	return n
end

local function FlattenData(activityData, n, outputTbl, numCompleted)
	if not activityData then return n, 0 end;

	local dataIndex = IndexData(activityData);

	local hideCompleted = ActivityUtil.hideCompleted;
	numCompleted = numCompleted or 0;

	local uniqueQuests = {};

	for _, category in ipairs(activityData) do
		local anyIncompleted;
		local numEntries = 0;
		local entries = {};
		local showActivity;
		local uiMapID;

		for _, entry in ipairs(category.entries) do
			showActivity = true;
			uiMapID = category.uiMapID;

			if entry.children then
				if not entry.icon then
					entry.icon = InProgressQuestIconFile[128];
				end
				local completed = true;
				local totalChildren = #entry.children;
				local numCompletedChildren = 0;
				for k, v in ipairs(entry.children) do
					local flagQuest = v.questID;
					if flagQuest then
						if (v.accountwide and IsQuestFlaggedCompletedOnAccount(flagQuest)) or (not v.accountwide and IsQuestFlaggedCompleted(flagQuest)) then
							numCompletedChildren = numCompletedChildren + 1;
						else
							completed = false;
						end
						uniqueQuests[flagQuest] = true;
					end
				end
				entry.completed = completed;
				if entry.label then
					entry.localizedName = format("%s/%s %s", numCompletedChildren, totalChildren, entry.label);
				end
			elseif entry.questPool then
				--Mutually Exclusive quests: Fortify Runestones
				if not entry.icon then
					entry.icon = QuestIcon.WEEKLY_QUEST;
				end
				local flagQuest;
				local completed = false;
				for k, v in ipairs(entry.questPool) do
					if not flagQuest then
						flagQuest = v.questID;
					end
					if IsQuestFlaggedCompleted(v.questID) then
						flagQuest = v.questID;
						completed = true;
						break
					end
				end
				entry.questID = flagQuest;
				InitQuestData(entry);
				entry.completed = completed;
			elseif entry.currencyID then
				if not entry.localizedName then
					local info = C_CurrencyInfo.GetCurrencyInfo(entry.currencyID);
					if info then
						entry.localizedName = info.name;
						entry.icon = info.iconFileID;
					else
						entry.localizedName = "Currency:"..entry.currencyID;
						entry.icon = 134400;
					end
				end
				entry.completed = API.IsCurrencyFullyEarned(entry.currencyID);
				showActivity = true;
			else
				local flagQuest = entry.flagQuest or entry.questID;
				if entry.questID then
					InitQuestData(entry);
				else
					entry.isOnQuest = false;
				end

				if flagQuest then
					if entry.accountwide then
						entry.completed = IsQuestFlaggedCompletedOnAccount(flagQuest);
					else
						entry.completed = IsQuestFlaggedCompleted(flagQuest);
					end
				else
					entry.completed = false;
				end
			end

			if entry.conditions then
				local arg = entry.conditions.useItemName and entry.localizedName or entry.itemID;
				if entry.conditions.ShouldShowActivity then
					showActivity = entry.conditions.ShouldShowActivity(arg);
				end
				if entry.conditions.IsActivityCompleted then
					entry.completed = entry.conditions.IsActivityCompleted(arg);
				end
			end

			if entry.shownIfOnQuest then
				if not entry.isOnQuest then
					showActivity = false;
				end
			end

			if entry.shownIfActive then
				if ShownIfActive(entry.questID) then
					showActivity = true;
					entry.uiMapID = C_TaskQuest.GetQuestZoneID(entry.questID);
				else
					if entry.showIfCompleted and entry.completed and (not hideCompleted) then
						showActivity = true;
					else
						showActivity = false;
					end
				end
			end

			if entry.shownIfOwned and entry.itemID then
				local count = C_Item.GetItemCount(entry.itemID);
				if count and count > 0 then
					showActivity = true;
				else
					showActivity = false;
				end
			end

			if (not entry.completed) and showActivity then
				anyIncompleted = true;
			end

			if entry.shouldShow then
				showActivity = entry.shouldShow();
			end

			if showActivity then
				if entry.completed then
					numCompleted = numCompleted + 1;
				end

				if entry.isDelveReputation then
					if not entry.atlas then
						entry.atlas = QuestIconAtlas.DELVES_BOUNTIFUL;
					end

					if not entry.tooltip then
						entry.tooltip = DELVES_REP_TOOLTIP;
					end
				end

				if entry.isWeeklyQuest then
					--if not entry.atlas then
					--    entry.atlas = QuestIconAtlas.WEEKLY_QUEST;
					--end
					if not entry.icon then
						entry.icon = QuestIcon.WEEKLY_QUEST;
					end
				elseif entry.isDailyQuest then
					if not entry.atlas then
						entry.atlas = QuestIconAtlas.DAILY_QUEST;
					end
				end

				if (not hideCompleted) or (entry.isHeader or not entry.completed) then
					if (not entry.questID) or (entry.questID and not uniqueQuests[entry.questID]) then
						if entry.questID then
							uniqueQuests[entry.questID] = true;
						end
						numEntries = numEntries + 1;
						entries[numEntries] = entry;
					end
				end
			end
		end

		if category.questLines then
			for _, questLineID in ipairs(category.questLines) do
				local questIDs = C_QuestLine.GetQuestLineQuests(questLineID);
				if questIDs then
					for _, questID in ipairs(questIDs) do
						if not uniqueQuests[questID] then
							uniqueQuests[questID] = true;
							if IsOnQuest(questID) then
								dataIndex = dataIndex + 1;
								local _entry = {questID = questID, name = "", uiMapID = uiMapID, dataIndex = dataIndex, completed = false};
								InitQuestData(_entry);
								numEntries = numEntries + 1;
								entries[numEntries] = _entry;
								anyIncompleted = true;
							end
						end
					end
				end
			end
		end

		if hideCompleted then
			if anyIncompleted then
				n = n + 1;
				outputTbl[n] = category;
				if numEntries > 0 then
					tsort(entries, SortFuncs.IncompleteFirst);
					for _, entry in ipairs(entries) do
						n = n + 1;
						outputTbl[n] = entry;
					end
				end
			end
		else
			if true then
				if numEntries > 0 then
					n = n + 1;
					outputTbl[n] = category;
					tsort(entries, SortFuncs.IncompleteFirst);
					for _, entry in ipairs(entries) do
						n = n + 1;
						outputTbl[n] = entry;
					end
				end
			end
		end
	end

	return n, numCompleted
end

function ActivityUtil.GetSortedActivity()
	--Wipe old data
	MapQuestData = nil;
	DynamicQuestDataProvider:Reset();
	DynamicQuestDataProvider:QueryQuests();


	local tbl = {};
	local n = 0;
	local numCompleted = 0;

	for _, category in ipairs(ActivityData) do
		category.isCollapsed = IsCategoryCollapsed(category.categoryID);
	end

	n, numCompleted = FlattenData(MapQuestData, n, tbl, numCompleted);
	n, numCompleted = FlattenData(ActivityData, n, tbl, numCompleted);

	for k, v in ipairs(tbl) do
		v.dataIndex = k;
	end

	SortedActivity = tbl;

	return tbl, numCompleted
end

function ActivityUtil.UpdateAndGetProgress(dataIndex)
	local entry = SortedActivity and SortedActivity[dataIndex];
	if entry then
		local flagQuest = entry.flagQuest or entry.questID;
		if flagQuest then
			if entry.accountwide then
				entry.completed = IsQuestFlaggedCompletedOnAccount(flagQuest);
			else
				entry.completed = IsQuestFlaggedCompleted(flagQuest);
			end
		else
			entry.completed = false;
		end
		return entry.completed
	end
end

function ActivityUtil.ToggleCollapsed(dataIndex)
	local v = SortedActivity and SortedActivity[dataIndex];
	if v and v.isHeader then
		v.isCollapsed = not v.isCollapsed;

		if v.categoryID then
			SetCategoryCollapsed(v.categoryID, v.isCollapsed);
		end

		--print(dataIndex, v.localizedName or v.name, v.isDynamicQuest, v.questMapID, v.isCollapsed);

		return v.isCollapsed
	end
end

function ActivityUtil.SetHideCompleted(state)
	ActivityUtil.hideCompleted = state;
end
addon.CallbackRegistry:RegisterSettingCallback("LandingPage_Activity_HideCompleted", ActivityUtil.SetHideCompleted);


addon.CallbackRegistry:Register("LandingPage.SetActivityData", function(data)
	if data then
		ActivityData = data;
	end
end);


--[[
function Debug_YeetQuests(uiMapID)
	uiMapID = uiMapID or C_Map.GetBestMapForUnit("player");

	local function PrintMetaQuests(quests)
		for index, questLineInfo in ipairs(quests) do
			if questLineInfo.isMeta or true then
				if not questLineInfo.questName then
					questLineInfo.questName = API.GetQuestName(questLineInfo.questID);
				end
				print(index, questLineInfo.questName, questLineInfo.questLineID)
			end
		end
	end

	PrintMetaQuests(C_QuestLine.GetAvailableQuestLines(uiMapID));
	PrintMetaQuests(C_TaskQuest.GetQuestsOnMap(uiMapID));
end

function Debug_YeetActiveQuestLineQuests()
	local questIDs = C_QuestLine.GetQuestLineQuests(5572);   --Dornogal meta
	for _, questID in ipairs(questIDs) do
		local questLineInfo = C_QuestLine.GetQuestLineInfo(questID);
		if questLineInfo then
			print(questLineInfo.questID, questLineInfo.questName, questLineInfo.isHidden)
		end
	end
end
--]]

--Debug Event Listener
--[[
do
	local EL = CreateFrame("Frame");

	local DynamicEvents = {
		"QUEST_LOG_UPDATE",
		"QUEST_REMOVED",
		"QUEST_ACCEPTED",
		"QUEST_TURNED_IN",
		"QUESTLINE_UPDATE",
		"TASK_PROGRESS_UPDATE",
	};
	API.RegisterFrameForEvents(EL, DynamicEvents);

	EL:SetScript("OnEvent", function(self, event, ...)
		print(event, ...)
	end)
end
--]]
