Quantcast
-- $Revision: 206 $
-- Cauldron queue management functions

local L = LibStub("AceLocale-3.0"):GetLocale("Cauldron")

CauldronQueue = {};

--[[
	The following table describes a queue item in the "main" and "intermediate"
	sections of the queue:

	queueItem = {
		["name"] = "<name of skill>",
		["icon"] = "<icon path>",
		["tradeskill"] = "<name of tradeskill>",
		["amount"] = <amount>,
		["priority"] = <priority>,
		["link"] = "<link>",
	};

	The following table describes a reagent needed by items in the queue.

	reagent = {
		["name"] = "<name of item>",
		["icon"] = "<icon path>",
		["amount"] = <amount>,
		["tradeskill"] = "<tradeskill that caused this reagent to be listed>",
		["link"] = "<link>",
	};
--]]

function CauldronQueue:NewQueue()
	local queue = {
		["main"] = {},
		["intermediate"] = {},
		["reagents"] = {},
	};

	return queue;
end

function CauldronQueue:NewItem(name, icon, amount, priority, tradeskill, link, spell)

	local queueItem = {
		["name"] = name or "",
		["icon"] = icon or "",
		["tradeskill"] = tradeskill or "",
		["link"] = link,
		["amount"] = amount or 1,
		["priority"] = priority or 0,
		["spell"] = spell,
	};

	return queueItem;
end

function CauldronQueue:NewReagent(name, icon, amount, tradeskill, link)

	local reagent = {
		["name"] = name or "",
		["icon"] = icon or "",
		["amount"] = amount or 1,
		["tradeskill"] = tradeskill,
		["link"] = link,
	};

	return reagent;
end

function CauldronQueue:GetItems(queue)

	-- sanity checks
	if not queue then
		Cauldron:error("GetItems: No queue found!");
		return nil;
	end

	local items = {};

	for _, item in pairs(queue.main) do
		if item.amount > 0 then
			table.insert(items, item);
		end
	end

	-- sort the list
	table.sort(items, function(r1, r2) return r2.priority < r1.priority; end);

	return items;
end

function CauldronQueue:GetIntermediates(queue)

	-- sanity checks
	if not queue then
		Cauldron:error("GetIntermediates: No queue found!");
		return nil;
	end

	local items = {};

	for _, item in pairs(queue.intermediate) do
--		if tradeskill then
--			if tradeskill == item.tradeskill then
--				table.insert(items, item);
--			end
--		else
		local t = CopyTable(item);
		local skillInfo = Cauldron:GetSkillInfo(t.tradeskill, t.name);
		if skillInfo and (skillInfo.available > 0) then
			-- increase the priority of items that can be crafted, so that they appear at the top of the list
			t.priority = t.priority + 1000;
		end
		table.insert(items, t);
--		end
	end

	-- sort the list
	table.sort(items, function(r1, r2) return r2.priority < r1.priority; end);

	return items;
end

function CauldronQueue:GetReagents(queue)

	-- sanity checks
	if not queue then
		Cauldron:error("GetReagents: No queue found!");
		return nil;
	end

	local items = {};

	for _, item in pairs(queue.reagents) do
--		if tradeskill then
--			if tradeskill == item.tradeskill then
--				table.insert(items, item);
--			end
--		else
			table.insert(items, item);
--		end
	end

	return items;
end

function CauldronQueue:AddItem(queue, skillInfo, amount, suppressCalc)

	-- sanity checks
	if not queue then
		Cauldron:error("AddItem: No queue found!");
		return;
	end

	if not queue.main then
		queue.main = {};
	end

	-- look for the item in the "main" section
	local item = queue.main[skillInfo.name];
	if item then
		-- it's there, so increase the amount
		item.amount = item.amount + amount;
	else
		-- it's not there, so create a new instance
		Cauldron:debug("AddItem: recipeLink="..tostring(skillInfo.recipeLink));
		queue.main[skillInfo.name] = CauldronQueue:NewItem(skillInfo.name, skillInfo.icon, amount, nil, skillInfo.tradeskill, skillInfo.itemLink, Cauldron:GetSpellNameFromLink(skillInfo.recipeLink));
	end

	if not suppressCalc then
		CauldronQueue:CalculateAllRequiredItems(queue);
	end
end

function CauldronQueue:CalculateAllRequiredItems(queue)

	-- sanity checks
	if not queue then
		Cauldron:error("CalculateAllRequiredItems: No queue found!");
--		Cauldron:Print(debugstack(1));
		return;
	end

	-- reset the intermediate and reagent lists
	queue.intermediate = {};
	queue.reagents = {};

	-- iterate over the queued items
	for name, queueInfo in pairs(queue.main) do
		local skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name);
		CauldronQueue:CalculateRequiredItems(queue, skillInfo, queueInfo.amount, queueInfo.priority);
	end

end

function CauldronQueue:CalculateRequiredItems(queue, skillInfo, amount, priority)

	-- sanity checks
	if not queue then
		Cauldron:error("CalculateRequiredItems: No queue found!");
		return;
	end

	-- get the intermediates and reagents for the item
	local intermediates, reagents = Cauldron:GetRequiredItems(skillInfo, amount);

	-- check the intermediate list; if the item is available somewhere (inventory, bank, alt, etc.)
	-- then move it to the reagent list; otherwise, update the intermediate list in the queue

	-- update the intermediate and reagent lists
	for i, reagent in ipairs(intermediates) do
		local count = Cauldron:ReagentCount(reagent.name);

		if count.has >= reagent.numRequired then
			-- add the item to the reagent list if the character has all the required amount
			table.insert(reagents, reagent);
		else
			local amount = reagent.numRequired;

			-- if the character has some, add that amount to the reagent list
			if count.has > 0 then
				-- create a reagent copy of the item
				local newItem = CopyTable(reagent);
				newItem.numRequired = count.has;

				table.insert(reagents, newItem);

				-- adjust item count to how many need to be crafted
				amount = reagent.numRequired - count.has;
			end

			-- add the remaining amount to the intermediate list
			if amount > 0 then
				-- adjust the amount if the item produces more than one per execution
				local intSkillInfo = Cauldron:GetSkillInfoForItem(reagent.name);
				if intSkillInfo then
					if intSkillInfo.minMade > 1 then
						-- we ignore maxMade, since if it is greater than minMade, then the amount
						-- produced is variable, so we err on the side of caution and account for
						-- only ever making the minimum possible; besides, each execution of the
						-- skill will cause the reagent list to be reassessed, so producing more
						-- will be handled appropriately
						amount = math.ceil(amount / intSkillInfo.minMade);
					end
				end

				CauldronQueue:AddIntermediate(queue, reagent, amount, priority);

				-- add the intermediate's reagents also
				CauldronQueue:CalculateRequiredItems(queue, intSkillInfo, amount, priority);
			end
		end
	end

	for i, reagent in ipairs(reagents) do
		local v = CauldronQueue:AddReagent(queue, reagent, reagent.numRequired, skillInfo.tradeskill, reagent.link);

		if not v then
			Cauldron:error("Failed to add reagent to queue; marking recipe for rescan: "..skillInfo.name);
			Cauldron:MarkRecipeForRescan(Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[tradeskill], skillInfo.name);
		end

	end

end

function CauldronQueue:AddIntermediate(queue, reagent, amount, priority)

	-- sanity checks
	if not queue then
		Cauldron:error("AddIntermediate: No queue found!");
		return false;
	end

	amount = math.max(0, tonumber(amount) or 0);

	if not queue.intermediate then
		queue.intermediate = {};
	end

	-- look for the item in the "intermediate" section
	local item = queue.intermediate[reagent.name];
	if item then
		-- it's there, so increase the amount
		item.amount = item.amount + amount;
	else
		-- it's not there, so create a new instance
		if reagent.name then
			local skillInfo = Cauldron:GetSkillInfoForItem(reagent.name);
			if skillInfo then
				local newItem = CauldronQueue:NewItem(reagent.name, reagent.icon, amount, nil, skillInfo.tradeskill, reagent.link, Cauldron:GetNameFromLink(skillInfo.recipeLink));
				newItem.priority = priority or 0;
				queue.intermediate[reagent.name] = newItem;
			else
				Cauldron:error("AddIntermediate: no skill info found for reagent: "..reagent.name);
				return false;
			end
		else
			-- this shouldn't occur unless there's something wrong with the reagent info, but if it does, the user can report this error
			Cauldron:error("AddIntermediate: reagent.name is nil!  Please report this.  (skillIndex: "..reagent.skillIndex..", index: "..reagent.index..")");
			return false;
		end
	end

	return true;
end

function CauldronQueue:AddReagent(queue, reagent, amount, tradeskill)

	-- sanity checks
	if not queue then
		Cauldron:error("AddReagent: No queue found!");
		return false;
	end

	amount = math.max(1, tonumber(amount) or 1);

	if not queue.reagents then
		queue.reagents = {};
	end

	-- look for the item in the "reagents" section
	local item = queue.reagents[reagent.name];
	if item then
		-- it's there, so increase the amount
		item.amount = (tonumber(item.amount) or 0) + amount;
	else
		-- it's not there, so create a new instance
		if reagent.name then
			queue.reagents[reagent.name] = CauldronQueue:NewReagent(reagent.name, reagent.icon, amount, tradeskill, reagent.link);
		else
			-- this shouldn't occur unless there's something wrong with the reagent info, but if it does, the user can report this error
			Cauldron:error("AddReagent: reagent.name is nil!");
			return false;
		end
	end

	return true;
end

function CauldronQueue:AdjustItemCount(queue, name, delta)

	-- sanity checks
	if not queue then
		Cauldron:error("AdjustItemCount: No queue found!");
		return;
	end

	local item = queue.main[name];
	--[[
	for qName, qInfo in pairs(queue.main) do
		local qItem = Cauldron:GetNameFromLink(qInfo.link);
		if name == qItem then
			item = qInfo;
			break;
		end
	end
	--]]

	-- check for a mapped item, like "perfect" gem cuts, etc.
	--[[ TODO
	if not item then
		local map = Cauldron.vars.results[Cauldron:GetIdFromLink()];
	end
	--]]

	Cauldron:debug("AdjustItemCount: item="..tostring(item));
	if item then
		Cauldron:debug("AdjustItemCount: item.amount="..item.amount);
		item.amount = item.amount + delta;
		Cauldron:debug("AdjustItemCount: item.amount(delta)="..item.amount);

		if item.amount < 1 then
			Cauldron:debug("AdjustItemCount: remove item: "..name);
			queue.main[name] = nil;
		end
	end

	Cauldron:debug("AdjustItemCount: calculate required items");
	CauldronQueue:CalculateAllRequiredItems(queue);

end

function CauldronQueue:RemoveItem(queue, itemName)
	Cauldron:debug("RemoveItem enter");

	-- sanity checks
	if (not queue) and (not itemName) then
		Cauldron:error("RemoveItem: No queue or item name found!");
		return;
	end

	if queue.main[itemName] then
		queue.main[itemName] = nil;

		CauldronQueue:CalculateAllRequiredItems(queue);
	end

	Cauldron:debug("RemoveItem exit");
end

function CauldronQueue:IncreasePriority(queue, itemName, top)
	Cauldron:debug("IncreasePriority enter");

	-- sanity checks
	if (not queue) and (not itemName) then
		Cauldron:error("IncreasePriority: No queue or item name found!");
		return;
	end

	local item = queue.main[itemName];
	if item then
		local priority = item.priority + 1;
		local highest = 0;

		if top then
			for _, info in pairs(queue.main) do
				if info.priority > highest then
					highest = info.priority;
				end
			end

			priority = highest + 1;
		end

		item.priority = priority;
	end

	Cauldron:debug("IncreasePriority exit");
end

function CauldronQueue:DecreasePriority(queue, itemName, bottom)
	Cauldron:debug("DecreasePriority enter");

	-- sanity checks
	if (not queue) and (not itemName) then
		Cauldron:error("DecreasePriority: No queue or item name found!");
		return;
	end

	local item = queue.main[itemName];
	if item then
		local priority = item.priority - 1;
		local lowest = 0;

		if top then
			for _, info in pairs(queue.main) do
				if info.priority < lowest then
					lowest = info.priority;
				end
			end

			priority = lowest - 1;
		end

		item.priority = priority;
	end

	Cauldron:debug("DecreasePriority exit");
end

function CauldronQueue:ClearQueue(queue)
	Cauldron:debug("ClearQueue enter");

	-- sanity checks
	if not queue then
		Cauldron:error("ClearQueue: No queue found!");
		return;
	end

	--[[
	if tradeskill then
		Cauldron:debug("ClearQueue: clearing tradeskill: "..tradeskill);

		-- set aside the current main queue
		Cauldron:debug("ClearQueue: set aside main table");
		local main = queue.main;

		-- clear out the tables
		Cauldron:debug("ClearQueue: clear out tables");
		queue.main = {};

		-- iterate over the items and re-add the ones not for the specified tradeskill
		Cauldron:debug("ClearQueue: iterate over items");
		for i, item in ipairs(main) do
			Cauldron:debug("ClearQueue: item: "..i);
			if item.tradeskill ~= tradeskill then
				-- get the skill for the item
				Cauldron:debug("ClearQueue: item.tradeskill: "..item.tradeskill);
				local skillInfo = Cauldron:GetSkillInfo(item.tradeskill, item.name);

				-- recalculate
				Cauldron:debug("ClearQueue: recalculate");
				CauldronQueue:AddItem(queue, skillInfo, item.amount, true);
			end
		end

		CauldronQueue:CalculateAllRequiredItems(queue);
	else
	--]]

	queue.main = {};
	queue.intermediate = {};
	queue.reagents = {};

	Cauldron:debug("ClearQueue exit");
end

function CauldronQueue:GetIntermediateItem(queue, itemName)

	-- sanity checks
	if not queue then
		Cauldron:error("GetIntermediateItem: No queue found!");
		return nil;
	end

	return queue.intermediate[itemName];
end

function CauldronQueue:GetReagentItem(queue, itemName)

	-- sanity checks
	if not queue then
		Cauldron:error("GetReagentItem: No queue found!");
		return nil;
	end

	return queue.reagents[itemName];
end