view Modules/Alerts.lua @ 176:26c750a10b14

Renamed Inventorium debug channel to IMDebug (so it?s easier to recognize only IM changes, not from other addons), write /im d to register this new channel. Implemented stock alerts. Added ?don?t alert at characters? option which allows you to track groups at characters without being bothered about low stock. You can change the speed of the stock alert at the extra config tab.
author Zerotorescue
date Sun, 30 Jan 2011 15:39:18 +0100
parents
children 679d3664849d
line wrap: on
line source
local addon = select(2, ...);
local mod = addon:NewModule("Alerts", "AceEvent-3.0", "AceTimer-3.0");

local queue, cache = {}, {};

function mod:OnEnable()
	addon:Debug("Alerts:OnEnable");
	
	-- Register our alert slash command
	-- /im alert
	addon:RegisterSlash(function(this)
		mod:Scan(true);
	end, { "a", "alert" }, "|Hfunction:InventoriumCommandHandler:alert|h|cff00fff7/im alert|r|h (or /im a) - Rescan the items within all tracked groups and show item alerts for those items missing.");
	
	self:RegisterEvent("PLAYER_LOGIN", function()
		mod:Scan(false);
	end);
	
	--[[if addon.db.profile.defaults.scanInterval["00Login"] then
		self:RegisterEvent("PLAYER_LOGIN", "Scan");
	end
	
	if addon.db.profile.defaults.scanInterval["01Repeat5"] then
		self:ScheduleTimer("Scan", 5 * 60);
	elseif addon.db.profile.defaults.scanInterval["01Repeat10"] then
		self:ScheduleTimer("Scan", 10 * 60);
	elseif addon.db.profile.defaults.scanInterval["01Repeat15"] then
		self:ScheduleTimer("Scan", 15 * 60);
	elseif addon.db.profile.defaults.scanInterval["01Repeat30"] then
		self:ScheduleTimer("Scan", 30 * 60);
	elseif addon.db.profile.defaults.scanInterval["01Repeat60"] then
		self:ScheduleTimer("Scan", 60 * 60);
	elseif addon.db.profile.defaults.scanInterval["01Repeat120"] then
		self:ScheduleTimer("Scan", 120 * 60);
	end]]
end

local scanTypes = {
	Local = "local",
	Global = "global",
};

function mod:Scan(verbal)
	addon:Debug("Alerts:Scan");
	
	if verbal then
		addon:Print("Scanning items, the results will be presented to you in a moment. Please be patient.");
	end
	
	local playerName = UnitName("player");
	
	table.wipe(queue);
	table.wipe(cache);
	
	-- Go through all groups
	for groupName, values in pairs(addon.db.profile.groups) do
		-- Settings
		local trackAt = addon:GetOptionByKey(groupName, "trackAtCharacters");
		local dontAlertAt = addon:GetOptionByKey(groupName, "dontAlertAtCharacters");
		
		local alertBelowLocalMinimum = addon:GetOptionByKey(groupName, "alertBelowLocalMinimum");
		local minLocalStock = alertBelowLocalMinimum and addon:GetOptionByKey(groupName, "minLocalStock");
		
		local alertBelowGlobalMinimum = addon:GetOptionByKey(groupName, "alertBelowGlobalMinimum");
		local minGlobalStock = alertBelowGlobalMinimum and addon:GetOptionByKey(groupName, "minGlobalStock");
		
		local isTracked = (trackAt and trackAt[playerName] and (not dontAlertAt or not dontAlertAt[playerName])); -- Is this character interested in this data and does it want alerts?
		
		if values.items and isTracked and (alertBelowLocalMinimum or alertBelowGlobalMinimum) then
			addon:Debug("Scanning |cff00ff00%s|r", groupName);
			
			local groupSettings = {
				["name"] = groupName,
				["minLocalStock"] = minLocalStock,
				["minGlobalStock"] = minGlobalStock,
			};
			
			for itemId, _ in pairs(values.items) do
				if alertBelowLocalMinimum then
					self:QeueueScan(groupSettings, itemId, scanTypes.Local);
				end
				
				if alertBelowGlobalMinimum then
					self:QeueueScan(groupSettings, itemId, scanTypes.Global);
				end
			end
		end
	end
	
	if self:HasQueue() then
		addon:Debug("Alerts:HasQueue, processing");
		--addon:Debug(queue);
		
		self:ProcessScan(verbal);
	end
end

function mod:QeueueScan(groupSettings, itemId, scanType)
	table.insert(queue, {
		["group"] = groupSettings,
		["itemId"] = itemId,
		["type"] = scanType,
	});
end

function mod:HasQueue()
	return (#queue > 0);
end

function mod:ProcessScan(verbal)
	local thisItem = table.remove(queue, 1);
	
	if not thisItem then
		self:ScanFinished();
		return;
	end
	
	if thisItem.type == scanTypes.Global then
		local globalCount = addon:GetItemCount(thisItem.itemId, thisItem.group.name);
		
		if not cache[thisItem.itemId] then
			cache[thisItem.itemId] = {
				["itemId"] = thisItem.itemId, -- needed later for displaying
				["group"] = thisItem.group,
				["globalCount"] = globalCount,
			};
		else
			cache[thisItem.itemId].globalCount = globalCount;
		end
	elseif thisItem.type == scanTypes.Local then
		local localCount = addon:GetLocalItemCount(thisItem.itemId, thisItem.group.name);
		
		if not cache[thisItem.itemId] then
			cache[thisItem.itemId] = {
				["itemId"] = thisItem.itemId, -- needed later for displaying
				["group"] = thisItem.group,
				["localCount"] = localCount,
			};
		else
			cache[thisItem.itemId].globalCount = localCount;
		end
	end
	
	local nextScanDelay = (tonumber(addon.db.profile.defaults.scanInterval) or .1);
	
	if nextScanDelay == 0 then
		mod:ProcessScan(verbal);
	else
		self:ScheduleTimer(function()
			mod:ProcessScan(verbal);
		end, nextScanDelay); -- scan next item in nextScanDelay seconds
	end
end

local function OnProceed()
	InventoriumCommandHandler("summary");
	
	if InventoriumItemMover then
		InventoriumItemMover:Hide();
	end
end

local function OnCancel()
	if InventoriumItemMover then
		InventoriumItemMover:Hide();
	end
end

local function UseScanST()
	if not InventoriumItemMover then
		addon:CreateMoverFrame();
	end
	
	local frame = InventoriumItemMover; -- both for speed as code-consistency
	
	-- Scrolling table with a list of items to be moved
	local scrollTableWidth = ( frame.frmMeasureDummy:GetWidth() - 30 ); -- adjust width by the scrollbar size
	local headers = {
		{
			["name"] = "Item",
			["width"] = (scrollTableWidth * .6),
			["defaultsort"] = "asc",
			["comparesort"] = function(this, aRow, bRow, column)
				local aName, _, aRarity = GetItemInfo(this:GetRow(aRow).rowData.itemId);
				local bName, _, bRarity = GetItemInfo(this:GetRow(bRow).rowData.itemId);
				local template = "%d%s";
				aName = template:format((10 - (aRarity or 10)), (aName or ""):lower());
				bName = template:format((10 - (bRarity or 10)), (bName or ""):lower());
				
				if this.cols[column].sort == "dsc" then
					return aName > bName;
				else
					return aName < bName;
				end
			end,
			["sort"] = "asc", -- when the data is set, use this column so sort the default data
			["tooltipTitle"] = "Item",
			["tooltip"] = "Click to sort the list by item quality then item name.",
		},
		{
			["name"] = "Local",
			["width"] = (scrollTableWidth * .2),
			["align"] = "RIGHT",
			["defaultsort"] = "dsc",
			["sortnext"] = 1,
			["comparesort"] = function(this, aRow, bRow, column)
				local aAvailablePercent = (this:GetRow(aRow).rowData.localCount / this:GetRow(aRow).rowData.group.minLocalStock);
				local bAvailablePercent = (this:GetRow(bRow).rowData.localCount / this:GetRow(bRow).rowData.group.minLocalStock);
				
				if this.cols[column].sort == "dsc" then
					return aAvailablePercent > bAvailablePercent;
				else
					return aAvailablePercent < bAvailablePercent;
				end
			end,
			["tooltipTitle"] = "Local Stock",
			["tooltip"] = "Click to sort the list by the local stock percentage.",
		},
		{
			["name"] = "Global",
			["width"] = (scrollTableWidth * .2),
			["align"] = "RIGHT",
			["defaultsort"] = "dsc",
			["sortnext"] = 1,
			["comparesort"] = function(this, aRow, bRow, column)
				local aAvailablePercent = (this:GetRow(aRow).rowData.globalCount / this:GetRow(aRow).rowData.group.minGlobalStock);
				local bAvailablePercent = (this:GetRow(bRow).rowData.globalCount / this:GetRow(bRow).rowData.group.minGlobalStock);
				
				if this.cols[column].sort == "dsc" then
					return aAvailablePercent > bAvailablePercent;
				else
					return aAvailablePercent < bAvailablePercent;
				end
			end,
			["tooltipTitle"] = "Global Stock",
			["tooltip"] = "Click to sort the list by the global stock percentage.",
		},
	};
	
	local proceedButton = {
		text = "Show in summary",
		tooltipTitle = "Show in Summary",
		tooltip = "Show the summary giving a more detailed list of missing items.",
		onClick = OnProceed,
	};
	local cancelButton = {
		text = "Cancel",
		tooltipTitle = "Cancel",
		tooltip = "Close the window.",
		onClick = OnCancel,
	};
	
	addon:SetMoverFrameSettings("Inventorium Stock Alert", "You have elected to receive an alert when the following items are under your minimum stock requirement:", proceedButton, cancelButton, headers);
end

function mod:ScanFinished()
	table.wipe(queue);
	
	--addon:Debug(cache);
	
	-- This table is never copied, just referenced. It is the same for every row.
	local missingRow = {
		{
			["value"] = function(data, cols, realrow, column, table)
				return IdToItemLink(data[realrow].rowData.itemId);
			end,
		}, -- item
		{
			["value"] = function(data, cols, realrow, column, table)
				return addon:DisplayItemCount(data[realrow].rowData.localCount or -4, data[realrow].rowData.group.minLocalStock);
			end,
		}, -- local
		{
			["value"] = function(data, cols, realrow, column, table)
				return addon:DisplayItemCount(data[realrow].rowData.globalCount or -4, data[realrow].rowData.group.minGlobalStock);
			end,
		}, -- global
	};
	
	-- Store the list with rows in this
	local missingList = {};
	
	for itemId, info in pairs(cache) do
		if (info.globalCount and info.globalCount < info.group.minGlobalStock) or (info.localCount and info.localCount < info.group.minLocalStock) then
			-- Not enough global or not enough local
			
			tinsert(missingList, {
				["rowData"] = info, -- this is not a key usually found in a row item and ignored by the library
				["cols"] = missingRow,
			});
		end
	end
	
	table.wipe(cache); -- no longer needed, all missing items are now stored in the missingList
	
	if #missingList > 0 then
		UseScanST();
		
		addon:SetMoverFrameData(missingList);
		
		if verbal then
			addon:Print("Presenting the data...");
		end
	elseif verbal then
		addon:Print("No items that you elected to be alerted about are below the selected stock thresholds.");
	end
end