-- ------------- --
-- Price Tracker --
-- ------------- --

PriceTracker = {
	queryDelay = 3000,
	isSearching = false,
	settingsVersion = 0.2,
	icons = {
		gold = "EsoUI/Art/currency/currency_gold.dds"
	},
	colors = {
		default = "|c" .. ZO_TOOLTIP_DEFAULT_COLOR:ToHex(),
		instructional = "|c" .. ZO_TOOLTIP_INSTRUCTIONAL_COLOR:ToHex(),
		yellow = "|cFFFF00",
		title = "|c00B5FF",
	},
	selectedItem = {},
}
local PriceTracker = PriceTracker

-- Addon initialization
function PriceTracker:OnLoad(eventCode, addOnName)
	if(addOnName ~= "PriceTracker") then return end

	EVENT_MANAGER:RegisterForEvent("OnSearchResultsReceived", EVENT_TRADING_HOUSE_SEARCH_RESULTS_RECEIVED, function(...) self:OnSearchResultsReceived(...) end)
	EVENT_MANAGER:RegisterForEvent("OnTradingHouseClosed", EVENT_CLOSE_TRADING_HOUSE, function(...) self:OnTradingHouseClosed(...) end)

	ZO_PreHookHandler(ItemTooltip, "OnUpdate", function() self:OnUpdateTooltip(moc(), ItemTooltip) end)
	ZO_PreHookHandler(ItemTooltip, "OnHide", function() self:OnHideTooltip(ItemTooltip) end)

	PriceTracker.enchantingTable:OnLoad(eventCode, addOnName)

	SLASH_COMMANDS["/pt"] = function(...) self:CommandHandler(...) end
	SLASH_COMMANDS["/pricetracker"] = function(...) self:CommandHandler(...) end

	local defaults = {
		itemList = {},
		algorithm = self.menu.algorithmTable[1],
		showMinMax = true,
		showSeen = true,
		keyPress = self.menu.keyTable[1],
	}

	-- Load saved settings
	self.settings = ZO_SavedVars:NewAccountWide("PriceTrackerSettings", self.settingsVersion, nil, defaults)

	-- Do some housekeeping and remove inparsable items
	self:Housekeeping()

	-- Create a button in the trading house window
	self.button = PriceTrackerControlButton
	self.button:SetParent(ZO_TradingHouseLeftPaneBrowseItemsCommon)
	self.button:SetWidth(ZO_TradingHouseLeftPaneBrowseItemsCommonQuality:GetWidth())

	self.menu:InitAddonMenu(addOnName)
end

-- Handle slash commands
function PriceTracker:CommandHandler(text)
	if not text or text == "" or text == "help" then
		self:ShowHelp()
		return
	end

	if text == "reset" or text == "clear" then
		self.settings.itemList = {}
		return
	end

	if text == "clean" then
		self:CleanItemList()
		return
	end

	-- Hidden option
	if text == "housekeeping" then
		self:Housekeeping()
		return
	end

end

function PriceTracker:ShowHelp()
	d("To scan all item prices in all guild stores, click the 'Scan Prices' button in the guild store window.")
	d(" ")
	d("/pt help - Show this help")
	d("/pt clear - Clear stored price values")
	d("pt clean - Remove stale items (experimental)")
end

-- This method makes sure the item list is intact and parsable, in order to avoid exceptions later on
function PriceTracker:Housekeeping()
	if not self.settings.itemList then return end
	for k, v in pairs(self.settings.itemList) do
		-- Remove any empty items
		for p, q in pairs(v) do
			if not q.purchasePrice or not q.stackCount then v[p] = nil end
		end
		if not next(v) then self.settings.itemList[k] = nil end
	end
end

function PriceTracker:OnUpdateTooltip(item, tooltip)
	if not tooltip then tooltip = ItemTooltip end
	if not item or not item.dataEntry or not item.dataEntry.data or not self.menu:IsKeyPressed() or self.selectedItem[tooltip] == item then return end
	self.selectedItem[tooltip] = item
	local stackCount = item.dataEntry.data.stackCount or item.dataEntry.data.stack
	if not stackCount then return end

	local matches = self:GetMatches(item.dataEntry.data.name)
	if not matches then return end

	local item = self:SuggestPrice(matches)
	if not item then return end

	ZO_Tooltip_AddDivider(tooltip)
	tooltip:AddLine("Price Tracker", "ZoFontHeader2")
	local r, g, b = ZO_TOOLTIP_DEFAULT_COLOR:UnpackRGB()
	tooltip:AddLine(self:FormatTooltipLine("Suggested price:", math.floor(item.purchasePrice / item.stackCount)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
	if stackCount > 1 then
		tooltip:AddLine(self:FormatTooltipLine("Stack price:", math.floor(item.purchasePrice / item.stackCount * stackCount)), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
	end
	if self.settings.showMinMax then
		local minItem = self.mathUtils:Min(matches)
		local maxItem = self.mathUtils:Max(matches)
		local minPrice = math.floor(minItem.purchasePrice / minItem.stackCount)
		local maxPrice = math.floor(maxItem.purchasePrice / maxItem.stackCount)
		tooltip:AddLine(self:FormatTooltipLine("Min (each / stack):", minPrice, minPrice * stackCount, minItem.guildName), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
		tooltip:AddLine(self:FormatTooltipLine("Max (each / stack):", maxPrice, maxPrice * stackCount, maxItem.guildName), "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, true)
	end
	if self.settings.showSeen then
		tooltip:AddLine("Seen " .. #matches .. " times", "ZoFontGame", r, g, b, CENTER, MODIFY_TEXT_TYPE_NONE, CENTER, false)
	end
end

function PriceTracker:OnHideTooltip(tooltip)
	self.selectedItem[tooltip] = nil
end

function PriceTracker:OnScanPrices()
	if self.isSearching then return end

	self.button:SetEnabled(false)
	self.isSearching = true
	self.numOfGuilds = GetNumTradingHouseGuilds()
	self.currentGuild = 0
	while not CanSellOnTradingHouse(self.currentGuild) and self.currentGuild < self.numOfGuilds do
		self.currentGuild = self.currentGuild + 1
	end

	zo_callLater(function() ExecuteTradingHouseSearch(0, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay)
end

function PriceTracker:OnSearchResultsReceived(eventId, guildId, numItemsOnPage, currentPage, hasMorePages)
	if not self.isSearching then return end

	for i = 1, numItemsOnPage do
		self:AddItem(GetTradingHouseSearchResultItemInfo(i))
	end

	if hasMorePages then
		zo_callLater(function() ExecuteTradingHouseSearch(currentPage + 1, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay)
	else
		if self.currentGuild < self.numOfGuilds then
			self.currentGuild = self.currentGuild + 1
			SelectTradingHouseGuildId(self.currentGuild)
			zo_callLater(function() ExecuteTradingHouseSearch(0, TRADING_HOUSE_SORT_SALE_PRICE, true) end, self.queryDelay)
		else
			self:OnTradingHouseClosed()
		end
	end
end

function PriceTracker:OnTradingHouseClosed()
	self.isSearching = false
	self.button:SetEnabled(true)
end

function PriceTracker:AddItem(icon, itemName, quality, stackCount, sellerName, timeRemaining, purchasePrice)
	if not purchasePrice or not stackCount then return end

	local item = {}
	item.expiry = timeRemaining + GetTimeStamp()
	item.icon = icon
	item.name = itemName
	item.normalizedName = self:NormalizeName(itemName)
	item.quality = quality
	item.stackCount = stackCount
	item.sellerName = sellerName
	item.purchasePrice = purchasePrice
	item.eachPrice = purchasePrice / stackCount
	item.guildId = self.currentGuild
	item.guildName = GetGuildName(item.guildId)

	if not self.settings.itemList[item.normalizedName] then
		self.settings.itemList[item.normalizedName] = {}
	end

	-- Do not add items that are already in the database
	if not self.settings.itemList[item.normalizedName][item.expiry] then
		self.settings.itemList[item.normalizedName][item.expiry] = item
	end
end

function PriceTracker:CleanItemList()
	local timestamp = GetTimeStamp()
	for k, v in pairs(PriceTracker.settings.itemList) do
		for itemK, itemV in pairs(v) do
			if itemV.expiry > timestamp then
				table.remove(v, itemK)
			end
		end
	end
end

function PriceTracker:GetMatches(itemName)
	local normalizedName = self:NormalizeName(itemName)
	if not self.settings.itemList or not self.settings.itemList[normalizedName] then
		return nil
	end

	local matches = {}
	for k, v in pairs(self.settings.itemList[normalizedName]) do
		table.insert(matches, v)
	end
	return matches
end

-- TODO: Base calculation on user preference
function PriceTracker:SuggestPrice(matches)
	if self.settings.algorithm == self.menu.algorithmTable[1] then
		return self.mathUtils:WeightedAverage(matches)
	end

	if self.settings.algorithm == self.menu.algorithmTable[2] then
		return self.mathUtils:Median(matches)
	end

	if self.settings.algorithm == self.menu.algorithmTable[3] then
		return self.mathUtils:Mode(matches)
	end

	d("Error deciding how to calculate suggested price")
	return nil
end

function PriceTracker:FormatTooltipLine(title, price1, price2, guild)
	if price2 then
		price1 = price1 .. " / " .. price2
	end
	local str
	if guild then
		str = "%-20.20s  (%-10.10s)"
	else
		str = "%-40.40s  %1.1s"
	end
	str = str .. " %12.12s%s"
	return string.format(str, title, guild or "", price1, zo_iconFormat(self.icons.gold, 16, 16))
end

function PriceTracker:NormalizeName(name)
	return zo_strformat(SI_TOOLTIP_ITEM_NAME, name)
end

EVENT_MANAGER:RegisterForEvent("PriceTrackerLoaded", EVENT_ADD_ON_LOADED, function(...) PriceTracker:OnLoad(...) end)