Quantcast
-- $Revision: 212 $
-- Cauldron tradeskill functions

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

function Cauldron:NeedsSkillUpdate()

--	Cauldron:info("checking for skill update");

	if CURRENT_TRADESKILL == "UNKNOWN" or CURRENT_TRADESKILL == "" then
--		Cauldron:info("no current tradeskill; no update needed");
		return false;
	end
	local skillName = CURRENT_TRADESKILL;
	if IsTradeSkillLinked() then
		skillName = "Linked-"..skillName;
	end
	if IsTradeSkillGuild() then
		skillName = "Guild-"..skillName;
	end

	-- initialize the trade skill entry
	if not Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName] then
		Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName] = {};
--		Cauldron:info("no skill entry; update needed");
		return true;
	end

	-- initialize the reagent map
	if not Cauldron.db.realm.userdata[Cauldron.vars.playername].reagentMap then
		Cauldron.db.realm.userdata[Cauldron.vars.playername].reagentMap = {};
--		Cauldron:info("no reagent map; update needed");
		return true;
	end

	-- save the skill entry in a local var
	local skillDB = Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName];
	if not skillDB.recipes then
		skillDB.recipes = {};
	end

	local numTradeSkills = GetNumTradeSkills();
	local name, rank, maxRank = GetTradeSkillLine();

	if skillDB.rescan and skillDB.rescan.failedRecipes and (#skillDB.rescan.failedRecipes > 0) then
		return true;
	end

	if (tonumber(numTradeSkills) ~= tonumber(skillDB.skillCount)) then
		return true;
	end
	if (tonumber(rank) ~= tonumber(skillDB.skillLevel)) then
		return true;
	end
	if ((time() - tonumber(skillDB.lastScanDate)) > 184000) then
	end

	return false;
end

function Cauldron:UpdateSkillItemCounts()

	local skillName, rank, maxRank = GetTradeSkillLine();

	if skillName == "UNKNOWN" then
		return;
	end
	-- check for linked or guild lists
	if IsTradeSkillLinked() then
		skillName = "Linked-"..skillName;
	end
	if IsTradeSkillGuild() then
		skillName = "Guild-"..skillName;
	end

	local skillDB = self.db.realm.userdata[self.vars.playername].skills[skillName];
	if not skillDB then
		return;
	end

	for i=1,GetNumTradeSkills() do
		local name, difficulty, avail, expanded, verb, numSkillUps = GetTradeSkillInfo(i);

		if name and difficulty ~= "header" then

			local minMade, maxMade = GetTradeSkillNumMade(i);

			if skillDB.recipes[name] then
				-- update the recipe info
				skillDB.recipes[name].index = i;
				skillDB.recipes[name].difficulty = difficulty;
				skillDB.recipes[name].available = avail;
				skillDB.recipes[name].minMade = minMade;
				skillDB.recipes[name].maxMade = maxMade;
				skillDB.recipes[name].numSkillUps = numSkillUps;

				-- update reagent skill index
				for _,r in ipairs(skillDB.recipes[name].reagents) do
					r.skillIndex = i;
				end
			end
		end
	end

end

function Cauldron:UpdateSkills(firstIndex, lastIndex)

	if not self.updatingSkills then
		self:debug("not updating skills; return");
		return;
	end

	local skillName, rank, maxRank = GetTradeSkillLine();

	if skillName == "UNKNOWN" then
		return;
	end

	local numTradeSkills = GetNumTradeSkills();
	Cauldron.vars.numSkills = numTradeSkills;
	local baseSkillName = skillName;

--	Cauldron:info("scanningSkills="..tostring(self.scanningSkills));
	if not Cauldron.scanningSkills then

		local startTime = GetTime();

		Cauldron.scanningSkills = true;
--		Cauldron:Print("Scanning recipes..."); -- TODO: remove

		-- make sure we're getting a full list
		SetTradeSkillItemNameFilter(nil);
		SetTradeSkillItemLevelFilter(0, 0);
		TradeSkillOnlyShowSkillUps(false);
		TradeSkillOnlyShowMakeable(false);

		-- make sure the list is fully expanded
		for i=GetNumTradeSkills(),1,-1 do
			local name = GetTradeSkillInfo(i);
			if name == "header" then
				ExpandTradeSkillSubClass(i);
			end
		end

		-- check for linked or guild lists
		if IsTradeSkillLinked() then
			skillName = "Linked-"..skillName;
		end
		if IsTradeSkillGuild() then
			skillName = "Guild-"..skillName;
		end

		-- initialize the trade skill entry
		if not self.db.realm.userdata[self.vars.playername].skills[skillName] then
			self.db.realm.userdata[self.vars.playername].skills[skillName] = {};
		end

		-- initialize the reagent map
		if not self.db.realm.userdata[self.vars.playername].reagentMap then
			self.db.realm.userdata[self.vars.playername].reagentMap = {};
		end

		-- save the skill entry in a local var
		local skillDB = self.db.realm.userdata[self.vars.playername].skills[skillName];
		if not skillDB.recipes then
			skillDB.recipes = {};
		end

		-- record skill stats
		skillDB.skillLevel = rank;
		skillDB.skillCount = numTradeSkills;
		skillDB.lastScanDate = time();

		local rescanNotify = false;

		-- initialize window information, if necessary
		if not skillDB.window then
			skillDB.window = {
				search = "",
				filter = {
					optimal = true,
					medium = true,
					easy = true,
					trivial = true,
					haveAllReagents = false,
					haveKeyReagents = false,
					haveAnyReagents = false,
					sortDefault = true,
					sortDifficulty = false,
					sortAlpha = false,
					sortBenefit = false,
					sortItemLevel = false,
					sortRequiredLevel = false,
					sortFavorites = false,
					favorites = false,
					achievements = false,
				},
				skills = {},
				slots = {},
				categories = {},
				selected = 1,
			};
		end

		--[[
		local slots = { GetTradeSkillSubClassFilteredSlots(0) };
		for i,slot in pairs(slots) do
			SetTradeSkillInvSlotFilter(i, 0, 0);
		end
		local subClasses = { GetTradeSkillSubClasses() };
		for i,subClass in pairs(subClasses) do
			SetTradeSkillSubClassFilter(i, 0, 0);
		end
		--]]

		local category = "";
		local rescanCount = 0;

		for i=1,GetNumTradeSkills() do
			local name, difficulty, avail, expanded, verb, numSkillUps = GetTradeSkillInfo(i);

			if name and difficulty ~= "header" then

				local minMade, maxMade = GetTradeSkillNumMade(i);

				-- adjust min/max made for specialities
				-- TODO

--[[
				-- check if we're rescanning, updating or adding
				local rescan = false;
				if skillDB.rescan then
					if skillDB.rescan.failedRecipes[name] then
						rescan = true;
					end
				end

				if skillDB.recipes[name] and not rescan then
					-- update the recipe info
					skillDB.recipes[name].index = i;
					skillDB.recipes[name].difficulty = difficulty;
					skillDB.recipes[name].available = avail;
					skillDB.recipes[name].minMade = minMade;
					skillDB.recipes[name].maxMade = maxMade;
					skillDB.recipes[name].numSkillUps = numSkillUps;

					-- update reagent skill index
					for _,r in ipairs(skillDB.recipes[name].reagents) do
						r.skillIndex = i;
					end
				else
--]]
				-- add the recipe info and other miscellaneous info
				local itemLink = GetTradeSkillItemLink(i);
				local recipeLink = GetTradeSkillRecipeLink(i);
				local _, _, _, _, _, itemType, itemSubType, _, slot, _ = GetItemInfo(itemLink);

				local keywords = name;

--[[
				if rescan then
					-- local msg = string.format(L["Rescanning recipe: %1$s..."], name);
					-- Cauldron:Print(msg);

					-- remove it from the list
					skillDB.rescan.failedRecipes[name] = nil;

					-- populate the reagent list
					local num = GetTradeSkillNumReagents(i);
					for j=1,num do
						local rName, rIcon, rCount, _ = GetTradeSkillReagentInfo(i, j);
						local rLink = GetTradeSkillReagentItemLink(i, j);
						local rItemId = Cauldron:GetIdFromLink(rLink);
						local key = not Cauldron:IsVendorItem(rItemId);

						if not skillDB.recipes[name].reagents[j].name then
							if (not rName) or (not rIcon) or (not rLink) then
--									Cauldron:MarkRecipeForRescan(skillDB, name);
--									rescanCount = rescanCount + 1;
							else
								local r = {
									["index"] = j,
									["name"] = rName,
									["numRequired"] = rCount,
									["skillIndex"] = i,
									["icon"] = rIcon,
									["link"] = rLink,
									["key"] = key,
								};

								skillDB.recipes[name].reagents[j] = r;

								if not self.db.realm.userdata[self.vars.playername].reagentMap[rName] then
									self.db.realm.userdata[self.vars.playername].reagentMap[rName] = {};
								end
								table.insert(self.db.realm.userdata[self.vars.playername].reagentMap[rName], skillName..";"..recipeLink);

								skillDB.recipes[name].keywords = skillDB.recipes[name].keywords..","..rName;
							end
						end
					end
				else
--]]
					-- fill in the db entry
					skillDB.recipes[name] = {
						['index'] = i,
						['name'] = name,
						['description'] = GetTradeSkillDescription(i),
						['itemLink'] = itemLink,
						['recipeLink'] = recipeLink,
						['icon'] = GetTradeSkillIcon(i),
						['tradeskill'] = baseSkillName,
						['difficulty'] = difficulty,
						['available'] = avail,
						['minMade'] = minMade,
						['maxMade'] = maxMade,
						['numSkillUps'] = numSkillUps,
						['verb'] = verb,

						-- filter information
						['slot'] = _G[slot],
						['defaultCategory'] = category,
						['type'] = itemType,
						['subtype'] = itemSubType,

						['reagents'] = {},
					};

					-- set the action verb for this skill
--					skillDB.recipes[name].verb = verb;

					-- make sure the skill window info is initialized
					if not skillDB.window.skills[name] then
						skillDB.window.skills[name] = {
							['expanded'] = false,
							['favorite'] = false,
						};
					end

					-- make sure the category for the window is initialized
					if category ~= "" then
						if not skillDB.window.categories[category] then
							skillDB.window.categories[category] = {
								['shown'] = true,
								['expanded'] = true,
							};
						end
					end

					--[[
					-- populate the slot list
					Cauldron:debug("slot: "..tostring(slot));
					if slot and (slot ~= "") then
						skillDB.window.slots[slot] = {
							checked = true,
						};
					else
						-- special slot representing items that don't have a slot
						skillDB.window.slots.none = {
							checked = true,
						};
					end
					--]]

					-- populate the reagent list
					local num = GetTradeSkillNumReagents(i);
					for j=1,num,1 do
						local rName, rIcon, rCount, playerAmount = GetTradeSkillReagentInfo(i, j);
						local rLink = GetTradeSkillReagentItemLink(i, j);
						local rItemId = Cauldron:GetIdFromLink(rLink);
						local key = not Cauldron:IsVendorItem(rItemId);

						if (not rName) or (not rIcon) or (not rLink) then
							rescanNotify = true;
Cauldron:error("Failed to retrieve reagent info; marking recipe for rescan: "..name);
Cauldron:warn("i="..tostring(i).."; j="..tostring(j));
--[[
							-- store the info if it's not already from rescanning
							if not rescan then
								-- Cauldron:error("Failed to retrieve reagent info; marking recipe for rescan: "..name);
--									Cauldron:MarkRecipeForRescan(skillDB, name);
--									rescanCount = rescanCount + 1;
							end
--]]
						else
							local r = {
								["index"] = j,
								["name"] = rName,
								["numRequired"] = rCount,
								["skillIndex"] = i,
								["icon"] = rIcon,
								["link"] = rLink,
								["key"] = key,
								["amount"] = playerAmount,
							};

							table.insert(skillDB.recipes[name].reagents, r);

							-- add the reagent to the reagent map
							if rName then
								if not self.db.realm.userdata[self.vars.playername].reagentMap[rName] then
									self.db.realm.userdata[self.vars.playername].reagentMap[rName] = {};
								end
								table.insert(self.db.realm.userdata[self.vars.playername].reagentMap[rName], skillName..";"..recipeLink);

								keywords = keywords..","..rName;
							end
						end
					end

					-- fill in the keywords db entry
					skillDB.recipes[name].keywords = keywords;
--				end
--				end
			else
				-- save the header name
				if name then
					category = name;

					-- expand the header, so we get all the skills
					if not expanded then
						ExpandTradeSkillSubClass(i);
					end
				else
					category = "Uncategorized";
				end
			end
		end

		Cauldron.scanningSkills = false;

		local endTime = GetTime();
		if Cauldron.vars.displayTimers then
			Cauldron:info("scan time: "..tostring(endTime-startTime).."ms");
		end

		--[[
		if rescanCount > 0 then
			Cauldron:Print(rescanCount.." recipes marked for rescan.");
		end
		--]]
	end

	--[[
	if rescanNotify then
		Cauldron:error(L["Cauldron had issues with some recipes for this profession."]);
		local msg = string.format(L["Please close and re-open the %1$s window again to re-scan."], baseSkillName);
		Cauldron:info(msg);
	end
	--]]

	self.updatingSkills = false;

end

function Cauldron:GetDefaultCategories(player, skillName)

	local categories = {};

	if self.db then
		for name, info in pairs(self.db.realm.userdata[player].skills[skillName].window.categories) do
			categories[name] = info.shown;
		end
	end

--	table.sort(categories);

	return categories;
end

--[[
function Cauldron:GetCategories(skillList)
	self:debug("GetCategories enter");

	local categories = {};

	if not skillList then
		return categories;
	end

	for _, info in ipairs(skillList) do
		table.insert(categories, info.defaultCategory);
	end

	table.sort(categories);

	self:debug("GetCategories exit");

	return categories;
end
--]]

function Cauldron:GetSlots(player, skillName)

	local slots = {};

	if self.db then
		for name, info in pairs(self.db.realm.userdata[player].skills[skillName].window.slots) do
			slots[name] = info;
		end
	end

--	table.sort(slots);

	return slots;
end

function Cauldron:GetSkillCount(skillName)

	if not skillName then
		skillName = CURRENT_TRADESKILL;
	end
	if not skillName then
		-- still couldn't get a skill name, so return 0
		return 0;
	end

	local skillCount = 0;

	if (Cauldron.db.realm.userdata[Cauldron.vars.playername]) and
	   (Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName]) then
		for _, _ in pairs(Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName].recipes) do
			skillCount = skillCount + 1;
		end
	end

	return skillCount;
end

function Cauldron:GetSkillNumAvailable(skillInfo)
	if not skillInfo then
		return 0;
	end
	if skillInfo.tradeskill ~= CURRENT_TRADESKILL then
		return 0;
	end

	local _, _, avail, _, _, _ = GetTradeSkillInfo(skillInfo.index);

	return avail or 0;
end

function Cauldron:LearnSkill()
end

function Cauldron:GetSkillList(playername, skillName)

	if (not playername) or
	   (not skillName) then
		self:warn("GetSkillList: playername ("..tostring(playername)..") or skillName ("..tostring(skillName)..") not set!");
		return {};
	end
	if (not self.db.realm.userdata[playername]) or
	   (not self.db.realm.userdata[playername].skills[skillName]) then
		return {};
	end

	local skills = {};

	local startTime = GetTime();

	for name, recipe in pairs(self.db.realm.userdata[playername].skills[skillName].recipes) do

		local add = true;

		-- check the search text
		local search = self.db.realm.userdata[playername].skills[skillName].window.search or "";
		if #search > 0 then
			-- check for numbers
			local minLevel, maxLevel;
--			local matchItemLevel = false;
			local approxLevel, matchItemLevel = strmatch(search, "^~(%d+)(i?)");
			-- self:debug("GetSkillList: approxLevel="..tostring(approxLevel));
			if approxLevel then
				minLevel = approxLevel - 2;
				maxLevel = approxLevel + 2;
			else
				minLevel, maxLevel, matchItemLevel = strmatch(search, "^(%d+)%s*-*%s*(%d*)(i?)$");
				-- self:debug("GetSkillList: minLevel="..tostring(minLevel).."; maxLevel="..tostring(maxLevel));
			end
			if minLevel then
				if maxLevel == "" or maxLevel == "i" or maxLevel < minLevel then
					maxLevel = minLevel;
				end
				-- cleanse the level values
				minLevel = tonumber(minLevel) or 0;
				maxLevel = tonumber(maxLevel) or 0;

				local _,_,_,itemLevel,reqLevel,_,_,_,_,_ = GetItemInfo(recipe.itemLink);
				if itemLevel and (matchItemLevel == "i") then
					itemLevel = tonumber(itemLevel) or 0;
					if itemLevel < minLevel or itemLevel > maxLevel then
						add = false;
					end
				elseif reqLevel then
					reqLevel = tonumber(reqLevel) or 0;
					if reqLevel < minLevel or reqLevel > maxLevel then
						add = false;
					end
				end
			else
				-- match name or reagents
				if not string.find(string.lower(recipe.keywords or ""), string.lower(search)) then
					add = false;
				end
			end

		end

		-- check difficulty filter
		if not self.db.realm.userdata[playername].skills[skillName].window.filter[recipe.difficulty] then
			add = false;
		end

		-- check categories
		local categories = self.db.realm.userdata[playername].skills[skillName].window.categories;
		local catInfo = categories[recipe.defaultCategory] or categories[recipe.type] or categories[recipe.subtype];
		if catInfo and (not catInfo.shown) then
			add = false;
		end

		-- check slot
		local slotName = recipe.slot;
		if (not slotName) or (slotName == "") then
			slotName = "none";
		end
		local slotInfo = self.db.realm.userdata[playername].skills[skillName].window.slots[slotName];
		self:debug("slotInfo: "..tostring(slotInfo));
		if slotInfo and not(slotInfo.checked) then
			add = false;
		end

		-- check reagent filter
		if self.db.realm.userdata[playername].skills[skillName].window.filter.haveAllReagents then
			-- check if the available count is 0
			if recipe.available == 0 then
				add = false;
			end
		elseif self.db.realm.userdata[playername].skills[skillName].window.filter.haveKeyReagents then
			-- check if the reagent count for key reagents is 0
			for _, rinfo in ipairs(recipe.reagents) do
				-- check possession count
				if (GetItemCount(rinfo.link, false) < rinfo.numRequired) and (rinfo.key) then
					add = false;
				end
			end
		elseif self.db.realm.userdata[playername].skills[skillName].window.filter.haveAnyReagents then
			-- check if the reagent count for any reagent is > 0
			add = false;
			for _, rinfo in ipairs(recipe.reagents) do
				-- check possession count
				if GetItemCount(rinfo.link, false) > 0 then
					add = true;
				end
			end
		end

		-- check favorites filter
		if self.db.realm.userdata[playername].skills[skillName].window.filter.favorites then
			if not self.db.realm.userdata[playername].skills[skillName].window.skills[recipe.name].favorite then
				add = false;
			end
		end

		-- check achievements filter
		if self.db.realm.userdata[playername].skills[skillName].window.filter.achievements then
			local achievements = Cauldron:GetAchievementsForSkill(recipe);
			if (not achievements) or (#achievements == 0) then
				add = false;
			end
		end

		-- we got here, add the recipe to the list
		if add then
			table.insert(skills, recipe);
		end
	end

	local endTime = GetTime();
	if Cauldron.vars.displayTimers then
		Cauldron:info("get skill list: "..tostring(endTime-startTime).."ms");
	end

	startTime = GetTime();

	-- sort the list
	table.sort(skills, function(r1, r2)
			if (not r1) or (not r2) then
				return true;
			end
			if not r1.name or not r2.name then
				return true;
			end

			if self.db.realm.userdata[playername].skills[skillName].window.filter.sortDefault then
				local r1v = self.db.realm.userdata[playername].skills[skillName].recipes[r1.name].index;
				local r2v = self.db.realm.userdata[playername].skills[skillName].recipes[r2.name].index;
				return r1v < r2v;
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortAlpha then
				return r1.name < r2.name;
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortDifficulty then
				local difficulty = {
					optimal = 4,
					medium = 3,
					easy = 2,
					trivial = 1,
				};

				local r1v = self.db.realm.userdata[playername].skills[skillName].recipes[r1.name].difficulty;
				local r2v = self.db.realm.userdata[playername].skills[skillName].recipes[r2.name].difficulty;
				return difficulty[r1v] > difficulty[r2v];
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortItemLevel then
				-- get item info from item link
				local _,_,_,r1iLevel,_,_,_,_,_,_ = GetItemInfo(r1.itemLink);
				local _,_,_,r2iLevel,_,_,_,_,_,_ = GetItemInfo(r2.itemLink);

				return (r2iLevel or 0) < (r1iLevel or 0);
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortRequiredLevel then
				-- get item info from item link
				local _,_,_,_,r1Level,_,_,_,_,_ = GetItemInfo(r1.itemLink);
				local _,_,_,_,r2Level,_,_,_,_,_ = GetItemInfo(r2.itemLink);

				return (r2Level or 0) < (r1Level or 0);
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortFavorites then
				local r1f = (self.db.realm.userdata[playername].skills[skillName].window.skills[r1.name].favorite and 100) or 1;
				local r2f = (self.db.realm.userdata[playername].skills[skillName].window.skills[r2.name].favorite and 100) or 1;

				return r2f < r1f;
			elseif self.db.realm.userdata[playername].skills[skillName].window.filter.sortBenefit then
				return true; -- TODO
			end

			return true;
		end);

	endTime = GetTime();
	if Cauldron.vars.displayTimers then
		Cauldron:info("sort skill list: "..tostring(endTime-startTime).."ms");
	end

	return skills;
end

function Cauldron:GetSkillInfo(tradeskill, skill)

	-- sanity checks
	if (not tradeskill) or (not skill) then
		self:warn("GetSkillInfo: missing tradeskill ("..tostring(tradeskill)..") or skill ("..tostring(skill)..")!");
		return nil;
	end

	if not self.db.realm.userdata[self.vars.playername].skills[tradeskill] then
		return nil;
	end

	local skillInfo = self.db.realm.userdata[self.vars.playername].skills[tradeskill].recipes[skill];
	if not skillInfo then
		-- couldn't find a skill with the item name, so scan the list for skills that craft
		-- the item
		for _, recipe in pairs(self.db.realm.userdata[self.vars.playername].skills[tradeskill].recipes) do
			local name, _ = GetItemInfo(recipe.itemLink);
			if name == skill then
				return recipe;
			end
		end
	end

	return skillInfo;
end

function Cauldron:GetSkillInfoForItem(item)

	if not item then
		Cauldron:error("GetSkillInfoForItem: item is nil!");
		return nil;
	end

	for tradeskill, list in pairs(self.db.realm.userdata[self.vars.playername].skills) do
		-- skip linked skills
		if not (string.find(tradeskill, "Linked-")) and not (string.find(tradeskill, "Guild-")) then
			for _, recipeInfo in pairs(list.recipes) do
				local name, _ = GetItemInfo(recipeInfo.itemLink);
				if name == item then
					return recipeInfo;
				end
			end
		end
	end

	return nil;
end

function Cauldron:GetSkillInfoForLink(recipeLink)

	if not recipeLink then
		Cauldron:error("GetSkillInfoForLink: recipeLink is nil!");
		return nil;
	end

	for tradeskill, list in pairs(self.db.realm.userdata[self.vars.playername].skills) do
		-- skip linked skills
		if not (string.find(tradeskill, "Linked-")) and not (string.find(tradeskill, "Guild-")) then
			for _, recipeInfo in pairs(list.recipes) do
				local id = Cauldron:GetIdFromLink(recipeLink);
				local recipeId = Cauldron:GetIdFromLink(recipeInfo.recipeLink);
				if id == recipeId then
					return recipeInfo;
				end
			end
		end
	end

	return nil;
end

function Cauldron:GetSkillInfoByIndex(itemIndex)

	if not itemIndex then
		Cauldron:error("GetSkillInfoByIndex: itemIndex is nil!");
		return nil;
	end

	for tradeskill, list in pairs(self.db.realm.userdata[self.vars.playername].skills) do
		-- skip linked skills
		if not (string.find(tradeskill, "Linked-")) and not (string.find(tradeskill, "Guild-")) then
			for _, recipeInfo in pairs(list.recipes) do
				if recipeInfo.index == itemIndex then
					return recipeInfo;
				end
			end
		end
	end

	return nil;
end

function Cauldron:GetReagentInfoByIndex(item, reagentIndex)

	local skillInfo = nil;

	if type(item) == "table" then
		skillInfo = item;
	elseif type(item) == "number" then
		skillInfo = self:GetSkillInfoByIndex(tonumber(item));
	end

	if skillInfo then
		for i, reagentInfo in ipairs(skillInfo.reagents) do
			if reagentInfo.index == reagentIndex then
				return reagentInfo;
			end
		end
	end

	return nil;
end

function Cauldron:GetRequiredItems(skillInfo, amount)

	local intermediates = {};
	local reagents = {};

	-- sanity checks
	if not skillInfo then
		Cauldron:error("GetRequiredItems: no skillInfo provided!");
		return intermediates, reagents;
	end

	-- get a proper amount value
	amount = math.max(1, tonumber(amount) or 1);

	-- find out what the reagents are
	for i, reagent in ipairs(skillInfo.reagents) do

		-- copy the reagent info so we can modify the amounts
		local r = CopyTable(reagent);
		r.numRequired = r.numRequired * amount;

		-- see if the character can make the item
		local si = Cauldron:GetSkillInfoForItem(r.name);
		if si then
			table.insert(intermediates, r);
		else
			table.insert(reagents, r);
		end
	end

	return intermediates, reagents;
end

local function split(sep, str)
	local result = {};

	while str do
		local s1, rest = strsplit(sep, str, 2);
		table.insert(result, s1);
		str = rest;
	end

	return result;
end

--[==[
function Cauldron:GetReagentsForSkill(skillInfo)

	if not skillInfo then
		self:error("No skill info provided!");
		return {};
	end

	local reagents = {};

	local itemId = self:GetIdFromLink(skillInfo.itemLink);

	local skillName = CURRENT_TRADESKILL;
	local baseSkillName = CURRENT_TRADESKILL;
	if IsTradeSkillLinked() then
		skillName = "Linked-"..skillName;
	end
	if IsTradeSkillGuild() then
		skillName = "Guild-"..skillName;
	end

	-- check if the reagents are already populated
	if #skillInfo.reagents > 0 then
		return skillInfo.reagents;
	end

	-- check for the proper trade skill context
	if CURRENT_TRADESKILL == skillInfo.tradeskill then

		for i=1,GetTradeSkillNumReagents(skillInfo.index) do
			local name, icon, count, _ = GetTradeSkillReagentInfo(skillInfo.index, i);
			local link = GetTradeSkillReagentItemLink(skillInfo.index, i);
			local itemId = Cauldron:GetIdFromLink(link);
			local value, set = self.libs.PT:ItemInSet(itemId, "Tradeskill.Mat.BySource.Vendor");
			local key = true;
			if value then
				key = false;
			end

			local r = {
--				["toonHas"] = GetItemCount(link),
				["name"] = name,
				["numRequired"] = count,
				["skillIndex"] = skillInfo.index,
				["icon"] = icon,
				["link"] = link,
				["key"] = key,
			};

			table.insert(reagents, r);
		end

		-- save the reagent list
		skillInfo.reagents = reagents;
	else
		self:warn("No tradeskill context found; using PeriodicTable for reagent info!");

		-- check the standard skill
		local reagentStr = self.libs.PT:ItemInSet(itemId, "TradeskillResultMats.Forward."..skillInfo.tradeskill);
		if not reagentStr then
			-- lookup the mapped skill
			local skillMap = {
				['Mining'] = 'Smelting',
	--			['Inscription'] = 'Milling',
	--			['Jewelcrafting'] = 'Prospecting',
			};
			if skillMap[skillInfo.tradeskill] then
				reagentStr = self.libs.PT:ItemInSet(itemId, "TradeskillResultMats.Forward."..skillMap[skillInfo.tradeskill]);
			end
		end
		if not reagentStr then
			self:error("No reagents found for skill: "..skillInfo.name);
			return {};
		end

		-- split the reagent info
		for _, reagent in ipairs(split(";", reagentStr)) do
			local id, numRequired = strsplit("x", reagent);

			-- get item details for the reagent
			local name, link, _, _, _, _, _, _, _, icon = GetItemInfo(id);

			local r = {
--				["toonHas"] = GetItemCount(id),
				["name"] = name,
				["numRequired"] = tonumber(numRequired),
				["skillIndex"] = skillInfo.index,
				["icon"] = icon,
				["link"] = link,
			};

			table.insert(reagents, r);
		end
	end

	return reagents;
end
--]==]

function Cauldron:GetSkillsForReagent(id)
	if not id then
		return {};
	end

	local itemName,itemInfo,_ = GetItemInfo(id);
	local skillList = {};

	if not Cauldron.db.realm.userdata[Cauldron.vars.playername].reagentMap then
		return {};
	end

	-- find the reagent in the map
	local reagentMap = Cauldron.db.realm.userdata[Cauldron.vars.playername].reagentMap[itemName];
	if reagentMap then
		for _,skill in ipairs(reagentMap) do
			table.insert(skillList, skill);
		end
	end

	return skillList;
end