--[[
Name: Stitch-1.1
Revision: $Rev: 38280 $
Author(s): Nymbia (nymbia@gmail.com)
Website: http://www.wowace.com/wiki/Stitch-1.1
Documentation: http://www.wowace.com/wiki/Stitch-1.1
SVN: http://svn.wowace.com/wowace/trunk/Stitch-1.1/Stitch-1.1/
Description: Library for tradeskill information access and queueing.
Dependencies: AceLibrary, AceEvent-2.0
License: LGPL v2.1
Copyright (C) 2006-2007 Nymbia

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

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
]]
local MAJOR_VERSION = "Stitch-1.1"
local MINOR_VERSION = "$Rev: 38280 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
if not AceLibrary:HasInstance("AceEvent-2.0") then error(MAJOR_VERSION .. " requires AceEvent-2.0") end
local AceEvent = AceLibrary("AceEvent-2.0")
local PT
if AceLibrary:HasInstance("PeriodicTable-3.0") then
	PT = AceLibrary("PeriodicTable-3.0")
end
local ENCHANTING_STRING
do
	local locale = GetLocale()
	if locale == "enUS" then
		ENCHANTING_STRING = "Enchanting"
	elseif locale == "frFR" then
		ENCHANTING_STRING = "Enchantement"
	elseif locale == "deDE" then
		ENCHANTING_STRING = "Verzauberkunst"
	elseif locale == "koKR" then
		ENCHANTING_STRING = "마법부여"
	elseif locale == "zhCN" then
		ENCHANTING_STRING = "附魔"
	elseif locale == "zhTW" then
		ENCHANTING_STRING = "附魔"
	elseif locale == "esES" then
		ENCHANTING_STRING = "Encantamiento"
	end
end
local Stitch = {}
Stitch.hooks = {}
local difficultyt = {
	o = "optimal",
	m = "medium",
	e = "easy",
	t = "trivial",
}
local difficultyr = {
	optimal = "o",
	medium = "m",
	easy = "e",
	trivial = "t",
}
local function squishlink(link)
	-- in:  |cffffffff|Hitem:13928:0:0:0:0:0:0:0|h[Grilled Squid]|h|r
	-- out: ffffff|13928|Grilled Squid
	local color, id, name = link:match("^|cff(......)|Hitem:(%d+):[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+:[^:]+|h%[([^%]]+)%]|h|r$")
	if id then
		return color.."|"..id.."|"..name
	else
		-- in:  |cffffffff|Henchant:7421|h[Runed Copper Rod]|h|r
		-- out: |-7421|Runed Copper Rod
		id, name = link:match("^|cffffd000|Henchant:(%d+)|h%[([^%]]+)%]|h|r$")
		return "|-"..id.."|"..name
	end
end
local function unsquishlink(link)
	-- in:  ffffff|13928|Grilled Squid
	-- out: |cffffffff|Hitem:13928:0:0:0:0:0:0:0|h[Grilled Squid]|h|r  ,false
	local color, id, name = link:match("^([^|].....)|(%d+)|(.+)$")
	if id then
		return "|cff"..color.."|Hitem:"..id..":0:0:0:0:0:0:0|h["..name.."]|h|r", false
	else
		-- in:  |-7421|Runed Copper Rod
		-- out: |cffffffff|Henchant:7421|h[Runed Copper Rod]|h|r ,true
		id, name = link:match("^|%-(%d+)|(.+)$")
		if id then
			return "|cffffd000|Henchant:"..id.."|h["..name.."]|h|r",true
		else
			return link
		end
	end
end
local itemmeta = {
	__index = function(self,key)
		if key == "numcraftable" then
			local num = 1000
			for _,v in ipairs(self) do
				if not v.vendor then
					local max = math.floor(v.num/v.needed)*self.nummade
					if max < num then
						num = max
					end
				end
			end
			if num == 1000 then
				for _,v in ipairs(self) do
					local max = math.floor(v.num/v.needed)*self.nummade
					if max < num then
						num = max
					end
				end
			end
			return num
		elseif key == "numcraftablewbank" then
			local num = 1000
			for _,v in ipairs(self) do
				if not v.vendor then
					local max = math.floor(v.numwbank/v.needed)*self.nummade
					if max < num then
						num = max
					end
				end
			end
			if num == 1000 then
				for _,v in ipairs(self) do
					local max = math.floor(v.numwbank/v.needed)*self.nummade
					if max < num then
						num = max
					end
				end
			end
			return num
		end
	end
}
local reagentmeta = {
	__index = function(self,key)
		if key == "num" then
			return GetItemCount(self.link)
		elseif key == "numwbank" then
			return GetItemCount(self.link,true)
		end
	end
}
local cache = setmetatable({},{
	__index = function(self,prof)
		if prof == "UNKNOWN" then
			return
		end
		self[prof] = setmetatable({},{
			__mode = 'v',
			__index = function(self,key)
				local datastring = AceLibrary("Stitch-1.1").data[prof][key]
				if not datastring then
					return
				end
				local itemchunk, reagentchunk = datastring:match("^([^;]-;[^;]-;[^;]-;[^;]-;)(.-)$")
				local nameoverride, link, difficultychar, numcrafted, tools = itemchunk:match("^([^;]-);([^;]+);(%a)(%d+);([^;]-);$")
				local isenchant
				link,isenchant = unsquishlink(link)
				if nameoverride:len() == 0 then
					nameoverride = link:match("%|h%[([^%]]+)%]%|h")
				end
				if tools:len() == 0 then
					tools = nil
				end
				local texture
				if isenchant then
					texture = "Interface\\Icons\\Spell_Holy_GreaterHeal"
				else
					texture = select(10,GetItemInfo(link))
				end
				self[key] = setmetatable({
					name = nameoverride,
					difficulty = difficultyt[difficultychar],
					nummade = tonumber(numcrafted),
					link = link,
					tools = tools,
					texture = texture,
					profession = prof,
					index = key,
				},itemmeta)
				for reagentnum, reagentlink in reagentchunk:gmatch("([^;]+);([^;]+);") do
					reagentlink = unsquishlink(reagentlink)
					local texture = select(10, GetItemInfo(reagentlink))
					local vendor
					if PT then
						vendor = PT:ItemInSet(reagentlink,"Tradeskill.Mat.BySource.Vendor")
					end
					table.insert(self[key],setmetatable({
						name = reagentlink:match("%|h%[([^%]]+)%]%|h"),
						link = reagentlink,
						needed = tonumber(reagentnum),
						texture = texture,
						vendor = vendor,
					},reagentmeta))
				end
				return self[key]
			end
		})
		return self[prof]
	end
})
-- API
function Stitch:EnableDataGathering(addon)
	assert(tostring(addon),"Usage: EnableDataGathering('addon')")
	self.datagatheraddons[addon] = true
	self:RegisterEvent("TRADE_SKILL_SHOW")
	self:RegisterEvent("CRAFT_SHOW")
	self:RegisterEvent("CHAT_MSG_SKILL")
	if not self.data then
		self.data = {}
	end
end
function Stitch:DisableDataGathering(addon)
	if not addon then
		self.data = nil
		self.datagatheraddons = {}
		return
	end
	assert(tostring(addon),"Usage: DisableDataGathering(['addon'])")
	self.datagatheraddons[addon] = false
	if next(self.datagatheraddons) then
		return
	end
	self:UnregisterEvent("TRADE_SKILL_SHOW")
	self:UnregisterEvent("CRAFT_SHOW")
	self:UnregisterEvent("CHAT_MSG_SKILL")
	self.data = nil
end
function Stitch:EnableQueue(addon)
	assert(tostring(addon),"Usage: EnableDataGathering('addon')")
	self.queueaddons[addon] = true
	self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", "StopCastCheckUnit")
	self:RegisterEvent("UNIT_SPELLCAST_FAILED", "StopCastCheckUnit")
	self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", "StopCastCheckUnit")
	if not self.queue then
		self.queue = {}
	end
	self.queueenabled = true
end
function Stitch:DisableQueue(addon)
	if not addon then
		self.queue = nil
		self.queueaddons = {}
		self.queueenabled = false
		return
	end
	assert(tostring(addon),"Usage: DisableDataGathering(['addon'])")
	self.queueaddons[addon] = false
	if next(self.queueaddons) then
		return
	end
	self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
	self:UnregisterEvent("UNIT_SPELLCAST_FAILED")
	self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED")
	self.queueenabled = false
	self.queue = nil
end
function Stitch:GetItemDataByIndex(profession, index)
	assert(tonumber(index) and profession,"Usage: GetItemDataByIndex('profession', index)")
	return cache[profession][index]
end
function Stitch:GetItemDataByName(name)
	assert(tostring(name) ,"Usage: GetItemDataByName('name')")
	for k,v in pairs(cache) do
		for l,w in pairs(v) do
			if w.name == name then
				return cache[k][l]
			end
		end
	end
	name = name:gsub("([%.%(%)%%%+%-%*%?%[%]%^%$])", "%%%1")
	for k,v in pairs(self.data) do
		for l,w in pairs(v) do
			-- protection against old manufac savedvars, remove eventually
			if type(w) ~= "string" then
				ManufacPerCharDB = nil
				error('Invalid DB, try reloading your ui.')
			end
			local chunk = w:match("^([^;]-;[^;]-;)")
			if chunk:match("^"..name) or chunk:match("|"..name..";") then
				return cache[k][l]
			end
		end
	end
end
local result = {}
function Stitch:GetItemDataByPartialName(name)
	for k,_ in pairs(result) do
		result[k] = nil
	end
	assert(tostring(name),"Usage: GetItemDataByPartialName('name')")
	name = name:gsub("([%.%(%)%%%+%-%*%?%[%]%^%$])", "%%%1")
	for k,v in pairs(self.data) do
		for l,w in pairs(v) do
			local chunk = w:match("([^;]-;[^;]-;)")
			if chunk:match("^"..name) or chunk:match("%|h%["..name.."%]%|h") then
				table.insert(result,cache[k][l])
			end
		end
	end
	if #result == 0 then
		return
	else
		return result
	end
end
function Stitch:GetQueueInfo()
	return self.queue
end
function Stitch:GetQueueItemInfo(index)
	return cache[self.queue[index]["profession"]][self.queue[index]["index"]]
end
function Stitch:RemoveFromQueue(index)
	table.remove(self.queue, index)
	if #self.queue == 0 then
		self:ClearQueue()
	end
end
function Stitch:ClearQueue()
	self.queue = {}
	AceEvent:TriggerEvent("Stitch_Queue_Complete")
end
function Stitch:ProcessQueue()
	local tradeskill = GetTradeSkillLine()
	if self.queue[1] and type(self.queue[1]) == "table" and tradeskill == self.queue[1]["profession"] then
		self.queuecasting = true
		return self.oldDoTrade(self.queue[1]["index"], self.queue[1]["numcasts"])
	else
		self.queue = {}
		AceEvent:TriggerEvent("Stitch_Queue_Complete")
	end
end
-- Internal
function Stitch:Stitch_AutoRescan()
	if InCombatLockdown() then
		return
	end
	if CraftFrame and self.recentcraft == ENCHANTING_STRING then
		self:ScanCraft()
	end
	if TradeSkillFrame then
		self:ScanTrade()
	end
end
function Stitch:TRADE_SKILL_SHOW()
	self.recenttrade = GetTradeSkillLine()
	if self.queue[1] and type(self.queue[1]) == "table" and self.recenttrade ~= self.queue[1]["profession"] then
		self:ClearQueue()
	end
	self:ScanTrade()
	if self.data.UNKNOWN then
		self.data.UNKNOWN = nil
	end
end
function Stitch:CRAFT_SHOW()
	self.recentcraft = GetCraftName()
	self:ScanCraft()
	if self.data.UNKNOWN then
		self.data.UNKNOWN = nil
	end
end
function Stitch:CHAT_MSG_SKILL()
	self:Stitch_AutoRescan()
end
function Stitch:StopCastCheckUnit(unit)
	if unit == "player" then
		self:StopCast()
	end
end
function Stitch:StopCast()
	if self.queuecasting then
		if event ~= "UNIT_SPELLCAST_SUCCEEDED" then
			AceEvent:TriggerEvent("Stitch_Queue_Continue", #self.queue)
			self.queuecasting = false
			return
		end
		if not self.queue[1] then
			self.queue = {}
			AceEvent:TriggerEvent("Stitch_Queue_Complete")
			return
		end
		self.queue[1].numcasts = self.queue[1].numcasts - 1
		if self.queue[1].numcasts < 1 then
			self:RemoveFromQueue(1)
			if table.getn(self.queue) > 0 then
				AceEvent:TriggerEvent("Stitch_Queue_Continue", #self.queue)
			else
				AceEvent:TriggerEvent("Stitch_Queue_Complete")
			end
			self.queuecasting = false
		else
			AceEvent:TriggerEvent("Stitch_Queue_Continue", #self.queue)
		end
	end
end
--------------------
-- Internal Stuff --
--------------------
function Stitch.hooks:DoTradeSkill(orig, index, times)
	if not index then return end
	if self.queueenabled then
		if index then self:AddToQueue(index, times) end
		self:ProcessQueue()
	else
		return orig(index, times)
	end
end
function Stitch:GetIDFromLink(link)
	local id = string.match(link, "item:(%d+)")
	return tonumber(id)
end
function Stitch:AddToQueue(index, times)
	if self.queue[1] and self.queue[1]["profession"] ~= self.recenttrade then
		self:ClearQueue()
	end
	if not times then
		times = 1
	end
	table.insert(self.queue, {
		["profession"] = self.recenttrade,
		["index"] = index,
		["numcasts"] = times,
	})
	AceEvent:TriggerEvent("Stitch_Queue_Add")
end
function Stitch:ScanCraft()
	local prof = GetCraftName()
	if prof~=ENCHANTING_STRING then
		return
	end
	if not self.data[prof] then
		self.data[prof] = {}
	end
	cache[prof] = nil
	local shred = false
	for i=1,GetNumCrafts() do
		local skillname, _, skilltype = GetCraftInfo(i)
		if skilltype~="header" and skillname then
			local newstr
			local link = GetCraftItemLink(i)
			if not link then
				shred = true
			else
				local v1, _, v2, _, v3, _, v4 = GetCraftSpellFocus(i)
				if v4 then
					v1 = v1..", "..v2..", "..v3..", "..v4
				elseif v3 then
					v1 = v1..", "..v2..", "..v3
				elseif v2 then
					v1 = v1..", "..v2
				elseif v1 then
					v1 = v1
				end
				local linkname = link:match("%|h%[([^%]]+)%]%|h")
				link = squishlink(link)
				if linkname == skillname then
					newstr = ";"..link..";"..difficultyr[skilltype].."1;"..(v1 or "")..";"
				else
					newstr = skillname..";"..link..";"..difficultyr[skilltype].."1;"..(v1 or "")..";"
				end
				for j=1,GetCraftNumReagents(i) do
					local _, _, rcount, _ = GetCraftReagentInfo(i,j)
					local link = GetCraftReagentItemLink(i,j)
					if not link then
						shred = true
					else
						link = squishlink(link)
						newstr = newstr..rcount..";"..link..";"
					end
				end
			end
			self.data[prof][i] = newstr
		else
			self.data[prof][i] = nil
		end
	end
	if shred then
		for k,v in pairs(self.data[prof]) do
			self.data[prof][k] = nil
		end
		if not AceEvent:IsEventScheduled("Stitch_AutoRescan") then
			AceEvent:ScheduleEvent("Stitch_AutoRescan", self.Stitch_AutoRescan, 3,self)
		end
	end
end
function Stitch:ScanTrade()
	local prof = GetTradeSkillLine()
	if prof == "UNKNOWN" then
		self.data[prof] = nil
	end
	if not self.data[prof] then
		self.data[prof] = {}
	end
	cache[prof] = nil
	local shred = false
	for i=1,GetNumTradeSkills() do
		local skillname, skilltype = GetTradeSkillInfo(i)
		if skilltype~="header" and skillname then
			local newstr
			local link = GetTradeSkillItemLink(i)
			if not link then
				shred = true
			else
				local v1, _, v2, _, v3, _, v4 = GetTradeSkillTools(i)
				if v4 then
					v1 = v1..", "..v2..", "..v3..", "..v4
				elseif v3 then
					v1 = v1..", "..v2..", "..v3
				elseif v2 then
					v1 = v1..", "..v2
				elseif v1 then
					v1 = v1
				end
				local linkname = link:match("%|h%[([^%]]+)%]%|h")
				link = squishlink(link)
				if linkname == skillname then
					newstr = ";"..link..";"..difficultyr[skilltype].."1;"..(v1 or "")..";"
				else
					newstr = skillname..";"..link..";"..difficultyr[skilltype].."1;"..(v1 or "")..";"
				end
				for j=1,GetTradeSkillNumReagents(i) do
					local _, _, rcount, _ = GetTradeSkillReagentInfo(i,j)
					local link = GetTradeSkillReagentItemLink(i,j)
					if not link then
						shred = true
					else
						link = squishlink(link)
						newstr = newstr..rcount..";"..link..";"
					end
				end
			end
			self.data[prof][i] = newstr
		else
			self.data[prof][i] = nil
		end
	end
	if shred then
		for k,v in pairs(self.data[prof]) do
			self.data[prof][k] = nil
		end
		if not AceEvent:IsEventScheduled("Stitch_AutoRescan") then
			AceEvent:ScheduleEvent("Stitch_AutoRescan", self.Stitch_AutoRescan, 3,self)
		end
	end
end
----------------------
-- AceLibrary Stuff --
----------------------
local function activate(self, oldLib, oldDeactivate)
	if oldLib then
		self.data = oldLib.data
		self.datagatheraddons = oldLib.datagatheraddons
		self.queueaddons = oldLib.queueaddons
		self.queue = oldLib.queue
		self.queuecasting = oldLib.queuecasting
		self.hooks = oldLib.hooks
		self.queueenabled = oldLib.queueenabled
		self.oldDoTrade = oldLib.oldDoTrade
	else
		self.oldDoTrade = DoTradeSkill
		function DoTradeSkill(index, times)
			if self.hooks.DoTradeSkill then
				return self.hooks.DoTradeSkill(self, self.oldDoTrade, index, times)
			else
				return self.oldDoTrade(index, times)
			end
		end
	end
	if not self.data then
		self.data = {}
	end
	if not self.queueenabled then
		self.queueenabled = false
	end
	if not self.queueaddons then
		self.queueaddons = {}
	end
	if not self.datagatheraddons then
		self.datagatheraddons = {}
	end
	if not self.queue then
		self.queue = {}
	end
	if not self.queuecasting then
		self.queuecasting = false
	end
	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end
local function external(self, major, instance)
	if major == "AceEvent-2.0" then
		AceEvent = instance
		AceEvent:embed(self)
		self:UnregisterAllEvents()
		self:CancelAllScheduledEvents()
	end
end
AceLibrary:Register(Stitch, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
Stitch = nil
--[[
self.data = {
	professionname = {
		
		--if name is the same as link
		
		[1] = ";link;diffnumcrafted;tools;reagent1num;reagent1link;reagent2num;reagent2link;",
		
		--if name is different from link
		
		[2] = "name;link;diffnummcrafted;tools;reagent1num;reagent1link;reagent2num;reagent2link;",
		
		--store difficulty as one letter
		--'o' = optimal
		--'m' = medium
		--'e' = easy
		--'t' = trivial
		
		index = {
			["name"] = itemname,
			["difficulty"] = "optimal",
			["nummade"] = nummade,
			["link"] = link,
			["tools"] = "tools",
			["texture"] = "texture",
			["numcraftable"] = number,
			["numcraftablewbank"] = number,
			[reagentindex] = {
				["name"] = name,
				["link"] = link,
				["needed"] = num,
				["texture"] = texture,
				["num"] = number,
				["numwbank"] = number,
				['vendor'] = bool,
			},
			
			--nuking..
			["numreagents"] = num,
			["index"] = index,
			["profession"] = profession,
			
		}
	}
}
]]
