-- App locals
local _, app = ...;
local L = app.L;
local GetItemInfo = app.WOWAPI.GetItemInfo;

-- Global locals
local debugprofilestop, next, pcall, select, tinsert, tonumber, type,math_min
	= debugprofilestop, next, pcall, select, tinsert, tonumber, type,math.min

-- Module locals
local auctionData, priceA, priceB, oldLegacyFilter = {};
local GoldCap = 99999999999;
local function SortByPrice(a,b)
	priceA = a.price or 0;
	priceB = b.price or 0;
	if priceA == priceB then
		return a.itemID < b.itemID;
	else
		return priceA < priceB;
	end
end

-- API Differences
local CanFullScan, ReceiveAuctions, RunFullScan, OnClickForAuctionItem;
if C_AuctionHouse then
	local C_AuctionHouse_ReplicateItems, C_AuctionHouse_GetNumReplicateItems, C_AuctionHouse_GetReplicateItemInfo
		= C_AuctionHouse.ReplicateItems, C_AuctionHouse.GetNumReplicateItems, C_AuctionHouse.GetReplicateItemInfo

	-- Only allow a scan once every 15 minutes.
	local cooldown = 0;
	CanFullScan = function()
		return cooldown - time() < 0;
	end
	--[[
	-- CRIEVE NOTE: Currently Classic has no need for the data, we could store it I guess, but we don't use it.
	function ProcessAuctions(auctions)
		for i,auction in ipairs(auctions) do
			-- https://warcraft.wiki.gg/wiki/API_C_AuctionHouse.GetReplicateItemInfo
			local itemID = auction[17];
			auctionData[itemID] =
				itemLink = C_AuctionHouse_GetReplicateItemLink(0),
				count = auction[3],
				price = auction[10]
			};
		end
	end
	ReceiveAuctions = function(callback)
		-- Gather the Auctions
		local numItems = C_AuctionHouse_GetNumReplicateItems();
		if numItems > 0 then
			local auctions = {};
			local continuables = {};
			for i=0,numItems-1 do
				auctions[i] = {C_AuctionHouse_GetReplicateItemInfo(i)};
				if not auctions[i][18] then
					local item = Item:CreateFromItemID(auctions[i][17]);
					continuables[item] = true;
					item:ContinueOnItemLoad(function()
						auctions[i] = {C_AuctionHouse_GetReplicateItemInfo(i)};
						continuables[item] = nil;
						if not next(continuables) then
							ProcessAuctions(auctions);
							callback(#auctions);
						end
					end);
				end
			end
		end
	end
	]]--
	ReceiveAuctions = function(callback)
		-- Gather the Auctions
		-- TODO: this is completely wrong for commodity items
		local numItems = C_AuctionHouse_GetNumReplicateItems();
		if numItems > 0 then
			app.PrintDebug("Received data for", numItems, "auctions");
			local auction;
			for i=0,numItems-1 do
				auction = {C_AuctionHouse_GetReplicateItemInfo(i)};
				local itemID = auction[17]
				local lowestPrice = auctionData[itemID] or GoldCap
				-- if itemID == app.DEBUGITEMID then
				-- 	app.PrintTable(auction)
				-- end
				auctionData[itemID] = math_min(lowestPrice, auction[10] or auction[11] or auction[8] or GoldCap)
			end
			callback(numItems);
		end
	end
	RunFullScan = function(self)
		-- Register for the event and send the query.
		pcall(self.RegisterEvent, self, "REPLICATE_ITEM_LIST_UPDATE");
		C_AuctionHouse_ReplicateItems();
		cooldown = time() + 900;
	end
	OnClickForAuctionItem = function(self, button)
		local reference = self.ref;

		-- Attempt to search manually with the link.
		local link = reference.link or reference.silentLink;
		if link then
			AuctionHouseFrame.SearchBar.SearchBox:SetText(GetItemInfo(link))
			AuctionHouseFrame.SearchBar:StartSearch();
		end
	end
else
	local CanSendAuctionQuery, GetNumAuctionItems, GetAuctionItemInfo
		= CanSendAuctionQuery, GetNumAuctionItems, GetAuctionItemInfo;
	CanFullScan = function()
		return select(2, CanSendAuctionQuery());
	end
	ReceiveAuctions = function(callback)
		local numItems = GetNumAuctionItems("list");
		if numItems > 0 then
			local index = 1;
			local auction;
			repeat
				-- Process the Auction
				auction = {GetAuctionItemInfo("list", index)};
				if auction[16] == 0 then	-- saleStatus
					local itemID = auction[17];	-- itemId
					if itemID and itemID > 0 then
						auctionData[itemID] = auction[10] or auction[11] or auction[8] or 0;	-- buyoutPrice or bidAmount or minBid
					end
				end
				index = index + 1;
			until index > numItems;
			callback(numItems);
		end
	end
	RunFullScan = function(self)
		-- Register for the event and send the query.
		pcall(self.RegisterEvent, self, "AUCTION_ITEM_LIST_UPDATE");
		QueryAuctionItems("", nil, nil, 0, nil, nil, true, false, nil);
		-- QueryAuctionItems(name, minLevel, maxLevel, page, isUsable, qualityIndex, getAll, exactMatch, filterData);
	end
	OnClickForAuctionItem = function(self, button)
		-- Do nothing, yet. Ideally we would attempt to buy the cheapest auction and update the price on the item.
	end
end

-- Implementation
local LastCmd;
local MaximumPrice = GoldCap;
local function ParseCommand(self, cmd, skipUpdate)
	if not cmd or cmd == "" then
		cmd = "cap";
	else
		cmd = cmd:lower();
	end
	LastCmd = cmd;

	local price;
	if cmd == "cap" or cmd == "goldcap" or cmd == "default" then
		price = GoldCap;
	elseif cmd == "all" then
		price = GetMoney();
	elseif cmd == "warband" and app.GameBuildVersion >= 110000 then
		price = C_Bank.FetchDepositedMoney(Enum.BankType.Account) + GetMoney();
	else
		price = tonumber(cmd);
		if type(price) == "number" then
			price = price * 10000;
		else
			price = 0;
		end
	end
	if not price or price <= 0 or price > GoldCap then
		price = GoldCap;
	end

	if MaximumPrice ~= price then
		MaximumPrice = price;
		if not skipUpdate then
			self:Update(true);
		end
	end
end

do	-- Auction Item Type
	local KEY = "itemID"
	-- Only update an Auction Item if it's within the MaximumPrice
	local function OnUpdateAuctionItem(t, parent, UpdateGroup)
		local price = t.price
		if price <= MaximumPrice then return end

		return true
	end
	local function SummaryForAuctionItem(t)
		return t.price and GetCoinTextureString(t.price) or ""
	end
	-- Wraps the given Type Object as an Auction Item, allowing altered functionality representing this being an Auction result
	local CreateAuctionItem = app.CreateClass("AuctionItem", KEY, {
		IsClassIsolated = true,
		ImportFrom = "Item",
		ImportFields = { "link", "AsyncRefreshFunc" },
		OnClick = function() return OnClickForAuctionItem end,
		summaryText = function(t)
			local summary = app.GetProgressTextForRow(t).." "..SummaryForAuctionItem(t)
			t.summaryText = summary
			return summary
		end,
		-- only update if it meets the MaxPrice threshold
		OnUpdate = function(t) return OnUpdateAuctionItem end,
		-- ignore some other fields that don't make sense for auctions
		g = app.EmptyFunction,
		costCollectibles = app.EmptyFunction,
		collectibleAsCost = app.EmptyFunction,
		costsCount = app.EmptyFunction,
	})
	app.CreateAuctionItem = function(t, price)
		local c = app.WrapObject(CreateAuctionItem(t[KEY]), t)
		c.price = price or 0
		return c
	end
end

app:CreateWindow("Auctions", {
	Commands = { "attauctions" },
	TooltipAnchor = "ANCHOR_RIGHT",
	IgnoreQuestUpdates = true,
	Preload = true,
	OnInit = function(self, handlers)
		local function ProcessAuctions()
			pcall(self.UnregisterEvent, self, "AUCTION_ITEM_LIST_UPDATE");
			pcall(self.UnregisterEvent, self, "REPLICATE_ITEM_LIST_UPDATE");
			local beginTime = debugprofilestop();
			ReceiveAuctions(function(count)
				app.print(format("Scanned %d auctions in %d milliseconds", count, debugprofilestop()-beginTime));

				-- Write back the valid auction data to saved variables.
				AllTheThingsAuctionData = auctionData;
				wipe(self.data.g);
				self:Update(true);
			end);
		end
		handlers.AUCTION_ITEM_LIST_UPDATE = ProcessAuctions;
		handlers.REPLICATE_ITEM_LIST_UPDATE = ProcessAuctions;
		handlers.ADDON_LOADED = function(self, addonName)
			if addonName == "Blizzard_AuctionUI" or addonName == "Blizzard_AuctionHouseUI" then
				self:SetParent(AuctionHouseFrame or AuctionFrame);
				self:UnregisterEvent("ADDON_LOADED");
				self:UpdatePosition();
			end
		end
		self:RegisterEvent("ADDON_LOADED");
		self.UpdatePosition = function(self)
			local auctionFrame = AuctionHouseFrame or AuctionFrame;
			if auctionFrame and not auctionFrame.__ATTSETUP then
				local origHide, origShow = auctionFrame.Hide, auctionFrame.Show;
				auctionFrame.__ATTSETUP = true;
				auctionFrame.Hide = function(...)
					origHide(...);
					self:UpdatePosition();
				end
				auctionFrame.Show = function(...)
					origShow(...);
					self:UpdatePosition();
				end
				if SideDressUpFrame and not SideDressUpFrame.__ATTSETUP then
					local origHide, origShow = SideDressUpFrame.Hide, SideDressUpFrame.Show;
					SideDressUpFrame.__ATTSETUP = true;
					SideDressUpFrame.Hide = function(...)
						origHide(...);
						self:UpdatePosition();
					end
					SideDressUpFrame.Show = function(...)
						origShow(...);
						self:UpdatePosition();
					end
				end
			end
			if auctionFrame and auctionFrame:IsShown() then
				local width = self:GetWidth();
				self:ClearAllPoints();
				if AuctionHouseFrame then
					self:SetPoint("TOP", auctionFrame, "TOP", 0, 0);
					self:SetPoint("BOTTOM", auctionFrame, "BOTTOM", 0, 0);
				else
					self:SetPoint("TOP", auctionFrame, "TOP", 0, -12);
					self:SetPoint("BOTTOM", auctionFrame, "BOTTOM", 0, 10);
				end
				if SideDressUpFrame then
					if SideDressUpFrame:IsShown() then
						self:SetPoint("LEFT", SideDressUpFrame, "RIGHT", 0, 0);
					else
						self:SetPoint("LEFT", auctionFrame, "RIGHT", 0, 0);
					end
				end
				self:SetWidth(width);
				if app.Settings:GetTooltipSetting("Auto:AuctionList") and app.IsClassic then
					self:Show();
				elseif AuctionHouseFrameTabSideBar and app.IsRetail then
					AuctionHouseFrameTabSideBar:ClearAllPoints()
					AuctionHouseFrameTabSideBar:SetPoint("TOPLEFT", AuctionHouseFrame, "TOPRIGHT")
					AuctionHouseFrameTabSideBar:SetPoint("BOTTOMLEFT", AuctionHouseFrame, "BOTTOMRIGHT")
				end

				if app.IsRetail then
					app.Settings:SetTooltipSetting("Auto:AuctionList", false)
					self:SetResizable(false)
					self:SetWidth(350)
					self.CloseButton:Disable()	-- Hiding would be better, but it reasserts itself too often for that
					self:Hide()

					if not AuctionHouseFrameTabSideBar then	-- This runs in other addons as well, to create the shared parent frame
						AuctionHouseFrameTabSideBar = CreateFrame("Frame", nil, AuctionHouseFrame, "")
						AuctionHouseFrameTabSideBar:SetWidth(1)
						AuctionHouseFrameTabSideBar:SetPoint("TOPLEFT", AuctionHouseFrame, "TOPRIGHT")
						AuctionHouseFrameTabSideBar:SetPoint("BOTTOMLEFT", AuctionHouseFrame, "BOTTOMRIGHT")
						AuctionHouseFrameTabSideBar.Tabs = {}
						AuctionHouseFrameTabSideBar.selTab = 0
					end

					if not app.AuctionHouseTab then
						app.AuctionHouseTab = CreateFrame("Frame", nil, AuctionHouseFrameTabSideBar, "AllTheThings_Tab")
						app.AuctionHouseTab:SetPoint("TOPLEFT", AuctionHouseFrameTabSideBar, "TOPRIGHT", -2, -52)
						AuctionHouseFrameTabSideBar.Tabs[#AuctionHouseFrameTabSideBar.Tabs + 1] = app.AuctionHouseTab
					end

					local function toggleAHTab()
						local newState = not self:IsShown()
						app.AuctionHouseTab:SetChecked(newState)
						app.AuctionHouseTab.Icon:SetTexture("Interface\\Addons\\AllTheThings\\assets\\logo_32x32")
						app.AuctionHouseTab.Icon:SetSize(24, 24)

						AuctionHouseFrameTabSideBar:ClearAllPoints()
						if newState then
							for i = 1, #AuctionHouseFrameTabSideBar.Tabs do
								if AuctionHouseFrameTabSideBar.selTab == i and i ~= 1 then
									AuctionHouseFrameTabSideBar.Tabs[i]:GetScript("OnMouseUp")(AuctionHouseFrameTabSideBar.Tabs[i])
								end
							end
							AuctionHouseFrameTabSideBar.selTab = 1
							self:SetVisible(true)

							AuctionHouseFrameTabSideBar:SetPoint("TOPLEFT", self, "TOPRIGHT")
							AuctionHouseFrameTabSideBar:SetPoint("BOTTOMLEFT", self, "BOTTOMRIGHT")
						else
							AuctionHouseFrameTabSideBar.selTab = 0
							self:SetVisible(false)

							AuctionHouseFrameTabSideBar:SetPoint("TOPLEFT", AuctionHouseFrame, "TOPRIGHT")
							AuctionHouseFrameTabSideBar:SetPoint("BOTTOMLEFT", AuctionHouseFrame, "BOTTOMRIGHT")
						end
					end
					app.AuctionHouseTab:SetCustomOnMouseUpHandler(toggleAHTab)
					app.AuctionHouseTab:SetChecked(false)
					app.AuctionHouseTab.Icon:SetTexture("Interface\\Addons\\AllTheThings\\assets\\logo_32x32")
					app.AuctionHouseTab.Icon:SetSize(24, 24)

					if not app.CreatedTabAuction then
						AuctionHouseFrame:HookScript("OnHide", function()
							-- AuctionHouseFrameTabSideBar:ClearAllPoints()
							-- AuctionHouseFrameTabSideBar:SetPoint("TOPLEFT", AuctionHouseFrame, "TOPRIGHT")
							-- AuctionHouseFrameTabSideBar:SetPoint("BOTTOMLEFT", AuctionHouseFrame, "BOTTOMRIGHT")
							AuctionHouseFrameTabSideBar.selTab = 0
							app.AuctionHouseTab:SetChecked(false)
							app.AuctionHouseTab.Icon:SetTexture("Interface\\Addons\\AllTheThings\\assets\\logo_32x32")
							app.AuctionHouseTab.Icon:SetSize(24, 24)
							self:Hide()
						end)

						app.CreatedTabAuction = true
					end
				end
			else
				self:Hide();
			end
		end
		self:SetMovable(false);
		self:SetData(app.CreateRawText("Auction Module", {
			icon = 133784,
			description = "This is a debug window for all of the auction data that was returned. Turn on 'Account Mode' to show items usable on any character on your account!",
			SortType = "name",
			visible = true,
			expanded = true,
			back = 1,
			indent = 0,
			g = { },
			metas = { },
			options = {
				setmetatable({
					clickText = "Click to run a Full Scan",
					clickDescription = "Click this button to perform a full scan of the auction house. This information will appear within this window and clear out the existing data.",
					scanningText = "Full Scan on Cooldown",
					scanningDescription = "Please wait while we wait for the server to respond.",
					SortPriority = 1,
					icon = 132089,
					OnClick = function(row, button)
						return row.ref:StartScan(row);
					end,
					StartScan = function(ref, row)
						if ref.scanning then
							app.print("Scan already in progress. Please wait!");
							return;
						end
						if AucAdvanced and AucAdvanced.API then AucAdvanced.API.CompatibilityMode(1, ""); end

						if CanFullScan() then
							-- Clear out the old scan data.
							app.print("Full Scan Initiated... Please Wait!");
							wipe(self.data.g);
							wipe(auctionData);
							self:Update(true);

							-- Register for the event and send the query.
							RunFullScan(self);

							-- Update the Scan Button State
							row:StartATTCoroutine("UpdateScanButton", function()
								repeat
									if CanFullScan() then
										if ref.scanning then
											ref.scanning = nil;
											self:Refresh();
										end
										return true;
									else
										if not ref.scanning then
											ref.scanning = true;
											self:Refresh();
										end
										for i=0,60,1 do coroutine.yield(); end
									end
								until not ref.scanning;
							end);
						else
							app.print("Full Scan is still on Cooldown. Try again later.");
						end
					end,
					OnSetVisibility = app.ReturnTrue,
				}, { __index = function(t, key)
					if key == "info" then
						if CanFullScan() then
							return app.CreateRawText(t.clickText, {
								description = t.clickDescription,
								trackable = nil,
								saved = nil,
							});
						else
							return app.CreateRawText(t.scanningText, {
								description = t.scanningDescription,
								trackable = true,
								saved = false,
							});
						end
					end
					return t.info[key];
				end}),
				app.CreateRawText("Clear Auction Data", {
					description = "Click this button to clear all of the cached auction data.",
					SortPriority = 1.1,
					icon = 132089,
					OnClick = function(row, button)
						wipe(self.data.g);
						wipe(auctionData);
						self:Update(true);
						return true;
					end,
					OnUpdate = function(data)
						-- Determine if anything is cached in the Auction Data.
						data.visible = next(auctionData) ~= nil
						return true;
					end,
				}),
				app.CreateRawText("Toggle Debug Mode", {
					icon = 134932,
					description = "Click this button to toggle debug mode to show everything regardless of filters!",
					SortPriority = 1.2,
					OnClick = function()
						app.Settings:ToggleDebugMode();
					end,
					OnUpdate = function(data)
						data.visible = true;
						if app.MODE_DEBUG then
							-- Novaplane made me do it
							data.trackable = true;
							data.saved = true;
						else
							data.trackable = nil;
							data.saved = nil;
						end
						return true;
					end,
				}),
				app.CreateRawText("Toggle Account Mode", {
					icon = 133733,
					description = "Turn this setting on if you want to track all of the Things for all of your characters regardless of class and race filters.\n\nUnobtainable filters still apply.",
					SortPriority = 1.3,
					OnClick = function()
						app.Settings:ToggleAccountMode();
					end,
					OnUpdate = function(data)
						if app.MODE_DEBUG then
							data.visible = false;
						else
							data.visible = true;
							if app.MODE_ACCOUNT then
								data.trackable = true;
								data.saved = true;
							else
								data.trackable = nil;
								data.saved = nil;
							end
						end
						return true;
					end,
				}),
				app.CreateRawText("Toggle Faction Mode", {
					icon = 134932,
					description = "Click this button to toggle faction mode to show everything for your faction!",
					SortPriority = 1.4,
					OnClick = function()
						app.Settings:ToggleFactionMode();
					end,
					OnUpdate = function(data)
						if app.MODE_DEBUG or not app.MODE_ACCOUNT then
							data.visible = false;
						else
							data.visible = true;
							if app.Settings:Get("FactionMode") then
								data.trackable = true;
								data.saved = true;
							else
								data.trackable = nil;
								data.saved = nil;
							end
						end
						return true;
					end,
				}),
				app.CreateRawText("Maximum Price", {
					icon = 133784,
					description = "Press this button to change the maximum price of auctions displayed.\n\nChanging this value will filter out items that exceed this amount.",
					visible = true,
					SortPriority = 1.5,
					OnClick = function(row, button)
						app:ShowPopupDialogWithEditBox("Please enter a new maximum price\n(all / cap / warband) are also accepted values", tostring(MaximumPrice * 0.0001), function(cmd)
							ParseCommand(self, cmd);
						end);
						return true;
					end,
					OnUpdate = function(data)
						data.summaryText = MaximumPrice == GoldCap and "NO LIMIT" or GetCoinTextureString(MaximumPrice);
						return app.AlwaysShowUpdate(data);
					end,
				}),
				app.CreateRawText(L.ACHIEVEMENT, {	-- Achievements
					Metas = { "Achievement", "AchievementCriteria" },
					icon = app.asset("Category_Achievements"),
					description = "All items that could be used for an achievement.",
					SortPriority = 2,
				}),
				app.CreateRawText("Appearances", {	-- Appearances
					Metas = { "ItemWithAppearance", "ItemAsTransmog", "SimpleItemAsTransmog" },
					icon = 135349,
					description = "All items that could be learned for transmog are listed here.",
					SortPriority = 2,
				}),
				app.CreateFilter(101, {	-- Battle Pets
					Metas = { "BattlePetWithItem", "Species" },
					description = "All battle pets that you have not collected yet are displayed here.",
					SortPriority = 2,
				}),
				app.CreateRawText(CHARACTER, {	-- Character
					icon = app.asset("Category_ItemSets"),
					Metas = { "CharacterUnlockQuestItem", "CharacterUnlockSpellItem" },
					description = "All spells or quests than can be learned by your character are displayed here.",
					SortPriority = 2,
				}),
				app.CreateRawText(L.FACTIONS, {	-- Factions
					Metas = { "ItemWithFaction" },
					icon = app.asset("Category_Factions"),
					description = "All items that can be used to increase reputation for a faction that you have not collected yet are displayed here.",
					SortPriority = 2,
				}),
				app.CreateFilter(100, {	-- Mounts
					Metas = { "MountWithItem" },
					description = "All mounts that you have not collected yet are displayed here.",
					SortPriority = 2,
				}),
				app.CreateRawText("Materials", {	-- Materials
					Metas = { "Material" },
					icon = 132856,
					description = "All items that can be used to craft an item using a profession on your account.",
					SortPriority = 2,
				}),
				app.CreateRawText("Miscellaneous", {	-- Miscellaneous
					Metas = { "Item", "SimpleItem" },
					icon = 132595,
					description = "All items that could be used for some non-transmog related purpose such as for an achievement are displayed here.",
					SortPriority = 2,
				}),
				app.CreateCustomHeader(app.HeaderConstants.QUESTS, {	-- Quest
					Metas = { "ItemAsHQT" },
					description = "All items that trigger a non-character unlock quest.",
					SortPriority = 2,
				}),
				app.CreateFilter(200, {	-- Recipes
					Metas = { "RecipeWithItem" },
					description = "All recipes that you have not collected yet are displayed here.",
					SortPriority = 2,
				}),
				app.CreateRawText("Toys", {	-- Toys
					Metas = { "Toy", "ToyEventually" },
					icon = 133015,
					description = "All items that are classified as Toys either by ATT for the future or by the game presently.",
					SortPriority = 2,
				}),
				app.CreateRawText("Legacy", {	-- Legacy
					Metas = { "legacyID" },
					icon = 135331,
					description = "All items that were removed from game that you could probably still collect for a... nominal fee.\n\nAlso if you have found something here, feel free to post about it on the ATT Discord's #classic-general channel! I'm sure some folks might want to find these.",
					SortPriority = 100000,
					OnUpdate = function(data)
						local rawSettings = app.Settings:GetRawSettings("Unobtainable");
						oldLegacyFilter = rawSettings[2];
						rawSettings[2] = true;
					end,
				}),
				app.CreateRawText("Legacy Cleaner", {	-- Legacy Cleaner
					icon = 135331,
					SortPriority = 100001,
					OnUpdate = function(data)
						app.Settings:GetRawSettings("Unobtainable")[2] = oldLegacyFilter;
					end,
				})
			},
			OnUpdate = function(data)
				local g = data.g;
				if #g < 1 then
					for i,option in ipairs(data.options) do
						option.g = {};
						tinsert(g, option);
						if option.Metas then
							for j,meta in ipairs(option.Metas) do
								data.metas[meta] = option;
							end
						end
					end

					-- Determine if anything is cached in the Auction Data.
					if next(auctionData) then
						-- Search the ATT Database for information related to the auction links (items, species, etc)
						local searchResultsByKey = {}
						local searchResult, key, keys, value
						for itemID,price in pairs(auctionData) do
							-- TODO: investigate if this needs to properly handle multiple variants of an item, or if AH data is just bad
							searchResult = app.SearchForObject("itemID", itemID, "field") or app.CreateItem(itemID)
							key = searchResult.key;
							local __type = searchResult.__type or key;
							if key == "npcID" and searchResult.itemID then
								key = "itemID";
							end
							if key == "itemID" and searchResult.sourceID then
								key = "sourceID";
							end
							value = searchResult[key];

							if searchResult.u and (searchResult.u == 1 or searchResult.u == 2) then
								value = value .. "_" .. searchResult.u;
								__type = "legacyID";
							end

							-- Make sure that the key type is represented.
							keys = searchResultsByKey[__type];
							if not keys then
								keys = {};
								searchResultsByKey[__type] = keys;
							end

							-- First time this key value was used.
							if not keys[value] then
								keys[value] = app.CreateAuctionItem(searchResult, tonumber(price))
							end
						end

						-- Process the Non-Collectible Items for Materials (Reagents)
						local reagentCache = AllTheThingsAD.Reagents;
						if not reagentCache then
							reagentCache = {};
							AllTheThingsAD.Reagents = reagentCache;
						end
						if reagentCache and searchResultsByKey.SimpleItem then
							local cachedItems = searchResultsByKey.SimpleItem;
							searchResultsByKey.SimpleItem = {};
							searchResultsByKey.Material = {};
							for itemID,entry in pairs(cachedItems) do
								if reagentCache[itemID] then
									searchResultsByKey.Material[itemID] = entry;
									if not entry.g then entry.g = {}; end
									for itemID2,count in pairs(reagentCache[itemID][2]) do
										local searchResults = app.SearchForField("itemID", itemID2);
										if searchResults and #searchResults > 0 then
											local craftedItem = app.CloneClassInstance(searchResults[1]);
											craftedItem.OnClick = OnClickForAuctionItem;
											tinsert(entry.g, craftedItem);
										end
									end
								else
									-- Push it back into the itemID table
									searchResultsByKey.SimpleItem[itemID] = entry;
								end
							end
						end

						-- Display Test for Raw Data + Filtering
						for key, searchResults in pairs(searchResultsByKey) do
							local subdata = self.data.metas[key];
							if subdata then
								wipe(subdata.g)
							else
								subdata = app.CreateRawText(key, {
									text = key,
									Metas = { key },
									description = "Container for '" .. key .. "' object types.",
									SortPriority = 2,
									g = {},
								});
								self.data.metas[key] = subdata;
								g[#g + 1] = subdata
							end
							local subdatag = subdata.g
							for _,j in pairs(searchResults) do
								subdatag[#subdatag + 1] = j
							end
							app.Sort(subdatag, SortByPrice)
						end
					else
						tinsert(g, app.CreateRawText("No auctions cached. Waiting on Auction data.", {
							SortPriority = 99999,
						}));
					end
					app.AssignChildren(self.data)
				end
			end,
		}));
		self.VisibilityFilter = function(t)
			local price = t.price
			if not price or price <= MaximumPrice then return app.VisibilityFilter(t) end
		end
		if rawget(app.HeaderConstants, "MOUNT_MODS") then
			tinsert(self.data.options, app.CreateCustomHeader(app.HeaderConstants.MOUNT_MODS, {	-- Mount Mods
				Metas = { "MountMod" },
				description = "All mount mods that you have not collected yet are displayed here.",
				SortPriority = 2,
			}));
		end
		if app.GameBuildVersion >= 70000 then
			tinsert(self.data.options, app.CreateFilter(103, {	-- Illusions
				Metas = { "IllusionWithItem" },
				description = "All illusions that you have not collected yet are displayed here.",
				SortPriority = 2,
			}));
		end
		if app.GameBuildVersion > 110000 then
			tinsert(self.data.options, app.CreateRawText(L.DECOR, {	-- Decor
				Metas = { "Decor" },
				icon = app.asset("Category_Housing"),
				description = "All decor that you have not collected yet are displayed here.",
				SortPriority = 2,
			}));
		end
		-- If we have left over auction data from previous, then use it.
		if AllTheThingsAuctionData then
			auctionData = AllTheThingsAuctionData;
		end
		self:UpdatePosition();
	end,
	OnLoad = function(self, settings)
		-- If we have left over auction data from previous, then use it.
		if AllTheThingsAuctionData then
			auctionData = AllTheThingsAuctionData;
		end
		if settings.LastCmd then
			ParseCommand(self, settings.LastCmd, true);
		end
	end,
	OnSave = function(self, settings)
		settings.LastCmd = LastCmd;
	end,
	OnRebuild = function(self)
		self:UpdatePosition();
	end,
	OnShow = function(self)
		ParseCommand(self, LastCmd);
	end,
});
