--[[
	Itemizer Addon for World of Warcraft(tm).
	Version: 3.9.0.1247 (Kangaroo)
	Revision: $Id: ItemScanner.lua 1129 2006-12-08 04:38:55Z mentalpower $

	Itemizer itemLink and itemCache scanning and parsing functions.

	License:
		This program is free software; you can redistribute it and/or
		modify it under the terms of the GNU General Public License
		as published by the Free Software Foundation; either version 2
		of the License, or (at your option) any later version.

		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		You should have received a copy of the GNU General Public License
		along with this program(see GPL.txt); if not, write to the Free Software
		Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

	Note:
		This AddOn's source code is specifically designed to work with
		World of Warcraft's interpreted AddOn system.
		You have an implicit licence to use this AddOn with these facilities
		since that is it's designated purpose as per:
		http://www.fsf.org/licensing/licenses/gpl-faq.html#InterpreterIncompat
]]

local itemInfo
local onUpdate
local getNumLines
local buildItemData

local elapsedTime = 0; --Running time to compare with frequencyOfUpdates
local entriesToProcess = 25; --Items per OnUpdate call
local frequencyOfUpdates = 0.2; --Seconds to wait between OnUpdate calls.

--Making a local copy of these extensively used functions will make their lookup faster.
local pairs = pairs;
local tonumber = tonumber;
local strfind = string.find;
local tremove = table.remove;
local strlower = string.lower;

local GetTime = GetTime;

local processStack = ItemizerProcessStack;
local clearTable = Itemizer.Util.ClearTable;
local pruneTable = Itemizer.Util.PruneTable;
local storeItemData = Itemizer.Storage.StoreItemData;
local gameBuildNumber = Itemizer.Core.Constants.GameBuildNumber;
local gameBuildNumberString = Itemizer.Core.Constants.GameBuildNumberString;

--Just one table to be re-used
local itemInfo = {};

function onUpdate(timePassed)
	if (elapsedTime < frequencyOfUpdates) then
		elapsedTime = elapsedTime + timePassed;
		return;
	end

	local numItems = 0;
	local numLines = 0;

	for itemLink, info in pairs(processStack) do
		numLines = getNumLines(itemLink);
		if (info.lines < numLines) then
			info.lines = numLines;
			info.timer = GetTime();

		elseif ((GetTime() - info.timer) > 5) then
			if (info.lines > 0) then
				numItems = numItems + 1;
				storeItemData(buildItemData(itemLink, itemInfo));
				processStack[itemLink] = nil;

			else
				info.timer = GetTime();
			end
		end

		if (numItems >= entriesToProcess) then
			elapsedTime = 0;
			return;
		end
	end
	elapsedTime = 0;
end

--Be VERY careful with what you send to this function, it will DC the user if the item is invalid.
function getNumLines(itemLink)
	if (not (type(itemLink) == "string")) then
		EnhTooltip.DebugPrint("Itemizer: getNumLines() error", itemLink);
		return
	end
	local hyperLink = Itemizer.Util.GetItemHyperLinks(itemLink, false, true);

	ItemizerHidden:SetOwner(ItemizerHidden, "ANCHOR_NONE");
	ItemizerHidden:SetHyperlink(hyperLink);
	return ItemizerHidden:NumLines();
end

--Be VERY careful with what you send to this function, it will DC the user if the item is invalid.
function buildItemData(itemLink, itemInfo)
	if (not (type(itemLink) == "string")) then
		EnhTooltip.DebugPrint("Itemizer: buildItemData() error", "Link type", type(itemLink), itemLink);
		return;
	end

	itemInfo = clearTable(itemInfo)
	if (not (type(itemInfo) == "table")) then
		EnhTooltip.DebugPrint("Itemizer: buildItemData() error", "itemInfo type", type(itemInfo), itemLink);
		itemInfo = {};
	end

	local _
	local hyperLink = Itemizer.Util.GetItemHyperLinks(itemLink, false, true);
	local baseHyperLink = Itemizer.Util.GetItemHyperLinks(itemLink, true, true);
	local curLine, textLeft, textRight, switch, keepGoing, matched, matchedRight

	--Reconstruct the table
	if (not itemInfo.tooltip) then
		itemInfo.tooltip = {};
	end
	if (not itemInfo.tooltip.leftText) then
		itemInfo.tooltip.leftText = {};
	end
	if (not itemInfo.tooltip.rightText) then
		itemInfo.tooltip.rightText = {};
	end
	if (not itemInfo.attributes) then
		itemInfo.attributes = {};
	end
	if (not itemInfo.attributes.skills) then
		itemInfo.attributes.skills = {};
	end
	if (not itemInfo.attributes.resists) then
		itemInfo.attributes.resists = {};
	end
	if (not itemInfo.attributes.basicStats) then
		itemInfo.attributes.basicStats = {};
	end
	if (not itemInfo.attributes.equipBonuses) then
		itemInfo.attributes.equipBonuses = {};
	end

	itemInfo.itemLink = itemLink;
	itemInfo.itemHyperLink = hyperLink;
	itemInfo.itemRevision = gameBuildNumber;
	itemInfo.itemName = GetItemInfo(hyperLink);
	itemInfo.itemID, itemInfo.randomProp = Itemizer.Util.BreakLink(itemLink);
	itemInfo.itemBaseName, _, itemInfo.itemQuality, itemInfo.minLevel, itemInfo.itemType, itemInfo.itemSubType, _, itemInfo.itemEquipLocation = GetItemInfo(baseHyperLink);

	ItemizerHidden:SetOwner(ItemizerHidden, "ANCHOR_NONE");
	ItemizerHidden:SetHyperlink(baseHyperLink);
	--EnhTooltip.DebugPrint("Itemizer: Set Hyperlink to", itemLink, baseHyperLink, "NumLines", ItemizerHidden:NumLines())

	for index = 1, ItemizerHidden:NumLines() do

		curLine = getglobal("ItemizerHiddenTextLeft"..index);
		if (curLine and curLine:IsShown()) then
			textLeft = curLine:GetText();
		else
			textLeft = nil;
		end

		curLine = getglobal("ItemizerHiddenTextRight"..index);
		if (curLine and curLine:IsShown()) then
			textRight = curLine:GetText();
		else
			textRight = nil;
		end

		itemInfo.tooltip.leftText[index] = textLeft;
		itemInfo.tooltip.rightText[index] = textRight;

		matched = false;
		matchedRight = false;
		keepGoing = true;
		while (keepGoing and (textLeft or textRight)) do
			--If either of these are nil, they can cause problems later in the loop.
			if (not textLeft) then
				textLeft = "";
			end

			if (not textRight) then
				textRight = "";
			end

			--First line is the item's name, nothing to do here.
			if (not switch) then
				switch = 2;
				keepGoing = false;
				matched = true;

			--Scan for binding status
			elseif (switch == 2) then
				local _, _, binds = strfind(textLeft, "Binds when (.+)"); --%Localize%
				if (binds) then
					if (strlower(binds) == "equipped") then --%Localize%
						itemInfo.binds = 1;

					elseif (strlower(binds) == "picked up") then --%Localize%
						itemInfo.binds = 2;

					elseif (strlower(binds) == "used") then --%Localize%
						itemInfo.binds = 3;
					end
					keepGoing = false;
					matched = true;

				else
					itemInfo.binds = 0;
				end
				switch = 3;

			--Scan for unique status
			elseif (switch == 3) then
				if (strfind(textLeft, "Unique")) then --%Localize%
					itemInfo.isUnique = 1;
					keepGoing = false;
					matched = true;

				else
					itemInfo.isUnique = 0;
				end
				switch = 4;

			--Redirect the loop according to item type.
			elseif (switch == 4) then
				if (itemInfo.itemEquipLocation == "INVTYPE_WEAPONMAINHAND"
				or itemInfo.itemEquipLocation == "INVTYPE_WEAPONOFFHAND"
				or itemInfo.itemEquipLocation == "INVTYPE_RANGEDRIGHT"
				or itemInfo.itemEquipLocation == "INVTYPE_2HWEAPON"
				or itemInfo.itemEquipLocation == "INVTYPE_THROWN"
				or itemInfo.itemEquipLocation == "INVTYPE_RANGED") then
					switch = 5;
					keepGoing = false;
					matchedRight = true;
					matched = true;

				elseif (itemInfo.itemEquipLocation == "INVTYPE_BAG") then
					switch = 9;

				else
					switch = 7;
					keepGoing = false;
					matchedRight = true;
					matched = true;
				end

			--Scan for weapon damage and speed.
			elseif (switch == 5) then
				local _, _, minDamage, maxDamage, damageType = strfind(textLeft, "(%d+) %- (%d+)%s*(%w*) Damage"); --%Localize%
				if (minDamage and maxDamage) then
					itemInfo.attributes.itemMinDamage = tonumber(minDamage);
					itemInfo.attributes.itemMaxDamage = tonumber(maxDamage);
					matched = true;
				end

				local_, _, itemSpeed = strfind(textRight, "Speed (.+)");
				if (itemSpeed) then
					itemInfo.attributes.itemSpeed = tonumber(itemSpeed);
					matchedRight = true;
				end
				keepGoing = false;
				switch = 6;

			--Scan for weapon DPS
			elseif (switch == 6) then
				local _, _, itemDPS = strfind(textLeft, "%((.+) damage per second%)"); --%Localize%
				if (itemDPS) then
					itemInfo.attributes.itemDPS = tonumber(itemDPS);
					keepGoing = false;
					matched = true;
				end
				switch = 7;

			--Scan for item armor values.
			elseif (switch == 7) then
				local _, _, itemArmor = strfind(textLeft, "(%d+) Armor"); --%Localize%
				if (itemArmor) then
					itemInfo.attributes.itemArmor = tonumber(itemArmor);
					keepGoing = false;
					matched = true;
				end
				if (itemInfo.itemEquipLocation == "INVTYPE_SHIELD") then
					switch = 8;
				else
					switch = 10;
				end

			--Scan for shield base block values.
			elseif (switch == 8) then
				local _, _, itemBlock = strfind(textLeft, "(%d+) Block"); --%Localize%
				if (itemBlock) then
					itemInfo.attributes.itemBlock = tonumber(itemBlock);
					keepGoing = false;
					matched = true;
				end
				switch = 10;

			--Scan for bag size.
			elseif (switch == 9) then
				local _, _, bagSize = strfind(textLeft, "(%d+) Slot Bag"); --%Localize%
				if (bagSize) then
					itemInfo.attributes.bagSize = tonumber(bagSize);
					keepGoing = false;
					matched = true;
				end
				switch = 10;

			--Scan for the base attributes that appear in white in the tooltip.
			elseif (switch == 10) then
				local found, quantity

				_, _, quantity = strfind(textLeft, "(%p?%d+) Agility"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.agility = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Health"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.health = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Intellect"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.intellect = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Mana"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.mana = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Spirit"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.spirit = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Stamina"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.stamina = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Strength"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.strength = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Defense"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.basicStats.defense = tonumber(quantity);
				end

				if (not found) then
					switch = 11;
				else
					keepGoing = false;
					matched = true;
				end

			--Scan for resists, which appear below base stats in the tooltip.
			elseif (switch == 11) then
				local found, quantity

				_, _, quantity = strfind(textLeft, "(%p?%d+) Fire Resistance"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.resists.fire = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Frost Resistance"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.resists.frost = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Arcane Resistance"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.resists.arcane = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Shadow Resistance"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.resists.shadow = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "(%p?%d+) Nature Resistance"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.resists.nature = tonumber(quantity);
				end

				if (not found) then
					switch = 12;
				else
					keepGoing = false;
					matched = true;
				end

			--Scan for class requirements
			elseif (switch == 12) then
				local _, _, itemClasses = strfind(textLeft, "Classes: (.+)"); --%Localize%
				if (itemClasses) then

					if (strfind(itemClasses, ", ")) then
						itemInfo.attributes.classes = Itemizer.Util.Split(itemClasses, ", ");

					else
						itemInfo.attributes.classes = {itemClasses};
					end

					keepGoing = false;
					matched = true;
				else
					switch = 13;
				end

			--Scan for profession, skill, level or honor requirements.
			elseif (switch == 13) then
				local _, _, requires = strfind(textLeft, "Requires (.+)"); --%Localize%
				if (requires) then
					local _, _, skill, skillLevel = strfind(requires, "(.+) %((%d+)%)");
					local _, _, level = strfind(requires, "Level (%d+)");

					if (skillLevel) then
						itemInfo.attributes.skills[skill] = tonumber(skillLevel);

					elseif (level) then
						assert(tonumber(itemInfo.minLevel) == tonumber(level));
						itemInfo.minLevel = level;

					else
						itemInfo.attributes.skills.honor = requires;
					end
					keepGoing = false;
					matched = true;

				else
					switch = 14;
				end

			--Scan for equip bonuses and itemSet name.
			elseif (switch == 14) then
				local found, damageOrHealing, schoolType, quantity, increaseType, itemSetName

				if (not strfind(textLeft, "Equip: ")) then --%Localize%
					keepGoing = false;

					--Detect set item info here because its the last thing we'll see
					_, _, itemSetName = strfind(textLeft, "(.+) %(%d/%d%)");
					if (itemSetName) then
						found = true;
						itemInfo.setName = itemSetName;
					end

					break;
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by fire spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.fire = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by frost spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.frost = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by holy spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.holy = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by shadow spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.shadow = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by arcane spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.arcane = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases damage done by nature spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.nature = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases healing done by spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.healing = tonumber(quantity);
				end
				_, _, quantity = strfind(textLeft, "Equip: Increases damage and healing done by magical spells and effects by up to (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.damageAndHealing = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Restores (%d+) health per 5 sec"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.healthPerFive = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Restores (%d+) mana per 5 sec"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.manaPerFive = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Improves your chance to get a critical strike with spells by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.spellCrit = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Improves your chance to get a critical strike by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.meleeCrit = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Improves your chance to hit with spells by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.spellHit = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Improves your chance to hit by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.meleeHit = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: (%p?%d+) Attack Power"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.attackPower = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: (%p?%d+) ranged Attack Power"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.rangedAttackPower = tonumber(quantity);
				end

				_, _, increaseType, quantity = strfind(textLeft, "Equip: Increased (.+) (%p?%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses[string.lower(increaseType)] = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases the block value of your shield by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.blockValue = tonumber(quantity);
				end

				_, _, increaseType, quantity = strfind(textLeft, "Equip: Increases your chance to dodge an attack by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.dodgeAttack = tonumber(quantity);
				end

				_, _, increaseType, quantity = strfind(textLeft, "Equip: Increases your chance to parry an attack by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.parryAttack = tonumber(quantity);
				end

				_, _, quantity = strfind(textLeft, "Equip: Increases your chance to block attacks with a shield by (%d+)"); --%Localize%
				if (quantity) then
					found = true;
					itemInfo.attributes.equipBonuses.blockAttack = tonumber(quantity);
				end

				keepGoing = false;
				if (found) then
					matched = true;
				end
			end
		end
		if (matched) then
			itemInfo.tooltip.leftText[index] = "¶"..itemInfo.tooltip.leftText[index];
		end
		if (matchedRight) then
			if (itemInfo.tooltip.rightText[index]) then
				itemInfo.tooltip.rightText[index] = "¶"..itemInfo.tooltip.rightText[index];
			end
		end
	end
	itemInfo = pruneTable(itemInfo);
	if (not itemInfo.itemID) then
		EnhTooltip.DebugPrint("ERROR!", itemLink)
		return itemInfo;
	end
end

Itemizer.Scanner = {
	OnUpdate = onUpdate,
	BuildItemData = buildItemData,
}