--[[
Name: Talismonger-3.0
Revision: $Rev: 68978 $
Author(s): kagaro
Email: sal.scotto@gmail.com
Website: http://www.wowace.com
Documentation: http://wiki.wowace.com/index.php/Talismonger-3.0
SVN: http://svn.wowace.com/root/trunk/TalismongerLib/Talismonger-3.0
Description: A library to provide information about gathering professions.
Dependencies: AceLibrary, AceEvent-2.0, Babble-Spell-2.2
License: LGPL v2.1
]]

local MAJOR_VERSION = "Talismonger-3.0"
local MINOR_VERSION = tonumber(string.sub("$Revision: 68978 $", 12, -3))

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 Talismonger = {}

--[[
	Since it was getting difficult to get translations added to this library, I decided to go a different route.
	This library will require the users of it to register the nodes/loot they wish to discover. It will be
	assumed the registration will be for the current locale. Talismonger will no longer act as an instance of
	AceLocale, but instead of a more general loot/node library and let the people listenting to the events handle
	doing translations. Babble-Ore,Herbs, and Fish are getting updated so mods can use those translations to get
	all their ducks in a row as it were.
]]

-- External libraries
local AceEvent = AceLibrary("AceEvent-2.0")

-- Nodes we care about
local tracked_nodes = {}
-- Loot we care about, mods must supply exact names of what they are interested in receiving events about
local tracked_loot = {}

-- Functions to allow mods to indicate what ndoes and loot to track
function Talismonger:TrackNode(node_name)
	tracked_nodes[node_name] = true
end
function Talismonger:TrackLoot(loot_name)
	tracked_loot[loot_name] = true
end
function Talismonger:GetTracked()
	return tracked_loot,tracked_nodes
end

-- Map of spells to events
-- this is a map of spells (in enUS) which can result in node discovery or loot gained from a profession
local spell_map = {
	--["Mining"] = true,
	[(GetSpellInfo(2575))] = true, -- Mining
	--["Fishing"] = true,
	[(GetSpellInfo(7620))] = true, -- Fishing	
	--["Herb Gathering"] = true,
	[(GetSpellInfo(2366))] = true, -- Herb Gathering	
	--["Skinning"] = true,
	[(GetSpellInfo(8613))] = true, -- Skinning	
	--["Extract Gas"] = true,
	[(GetSpellInfo(30427))] = true, -- Extract Gas
	--["Prospecting"] = true,
	[(GetSpellInfo(31252))] = true, -- Prospecting
	--["Pick Lock"] = true,
	[(GetSpellInfo(1804))] = true, -- Pick Lock
	--["Opening"] = true,
	[(GetSpellInfo(3365))] = true, -- Opening
	--["Pick Pocket"] = true,
	[(GetSpellInfo(921))] = true, -- Pick Lock
}

local spell,last_spell, found_node, last_spell_target
-- we keep track of 12 items at most during a loot, only bosses like Ony will have more than 2 pages of data
local loot_slots = {}
for i = 1, 12 do
	loot_slots[i] = {
		name  = "",
		count = 0,
		quest = false,
		giver = false,
	}
end

-- frame reference and accessor
local frame, tooltipper
function Talismonger:GetFrame()
	return frame
end
function Talismonger:GetTooltip()
	return tooltipper
end
-- clear up out loot list, reusing the table
local function ClearLootList()
	for idx=1,12 do
		loot_slots[idx].name = ""
		loot_slots[idx].count = 0
		loot_slots[idx].quest = false
		loot_slots[idx].giver = false
	end
end
-- event handlers
local function LOOT_CLOSED()
	ClearLootList()
	last_spell = "None"
	last_spell_target = nil
	spell = "None"
	found_node = false
end
-- get a particular loot slot
local function GetLootSlot(index)
	if index < 13 and index > 0 then
		return loot_slots[index].name, loot_slots[index].count, loot_slots[index].quest, loot_slots[index].giver
	end
end
-- load up a list based on the loot we found
local function PopulateLootList()
	ClearLootList()
	for idx = 1, GetNumLootItems(), 1 do
		if LootSlotIsItem(idx) then
			local _,name,count,_ = GetLootSlotInfo(idx)
			local sLink = GetLootSlotLink(idx);
			local questGiver = false
			local questItem = false
			if sLink then
				tooltipper:SetOwner(UIParent, "ANCHOR_PRESERVE")
				tooltipper:ClearLines()
				tooltipper:SetHyperlink(sLink)
				for i=2,tooltipper:NumLines() do
					local textline = getglobal("TalismongerLibTooltipTextLeft"..i)
					local text = textline:GetText()
					if text == ITEM_STARTS_QUEST then
						questGiver = true
					elseif text == ITEM_BIND_QUEST then
						questItem = true
					end
				end
				tooltipper:Hide()
			end
			if idx > 0 and idx < 13 then
				loot_slots[idx].name = name
				loot_slots[idx].count = count
				loot_slots[idx].quest = questItem 
				loot_slots[idx].giver = questGiver
			end
		end
	end
end
-- TODO REWRITE
local function GetWorldTarget()
	if found_node then
		return
	end
	local what = GameTooltipTextLeft1:GetText();
	if what and not last_spell_target then
		last_spell_target = what
	end
	if what and tracked_nodes[what] then
		found_node = true
		AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",last_spell,what)
	end
end
local mining = select(1,GetSpellInfo(2575))
local herbalism = select(1,GetSpellInfo(13614))
local picklock = select(1,GetSpellInfo(1804))
local herbing = select(1,GetSpellInfo(2366))
local opening = select(1,GetSpellInfo(3365))
-- This function listens for UI_ERROR_MESSAGES about failure to skin,mine,herb,etc.. due to skill
local function UI_ERROR_MESSAGE(msg)
	local what = GameTooltipTextLeft1:GetText();
	if tracked_nodes[what] then -- "what" can be nil, tracked_nodes[nil] returns nil and will fail the if test
		if msg:find(mining) then -- Failed to mine a node
			AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",mining,what)	
		elseif msg:find(herbalism) or msg:find("약초학") then -- Failed to herb a plant
			AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",herbing,what)
		elseif msg:find(picklock) then --Failed to pick a lock on an node
			AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",opening,what)
		end
	end
end
-- We fire an event when we loot indicating, what we did to generate this loot
-- signature of the receiver is Talismonger_Radical(spell,loot,count)
-- Where spell can be:
--	Mining
-- 	Fishing
-- 	Herb Gathering
-- 	Pick Pocket
--	Prospecting
--	Quest
--	Questor
-- 	Opening
--	Extract Gas (I dont know if this works, i dont have a gas extractor)
--	Kill
--	Skinning
local function LOOT_SLOT_CLEARED(index)
	local lspell = last_spell
	local lootName,lootCount, questItem, questGiver = GetLootSlot(index)
	if questItem then
		lspell = "Quest"
	elseif questGiver then
		lspell = "Questor"
	elseif not spell_map[last_spell] then
		lspell = "Kill"
	end	
	if tracked_loot[lootName] then
		AceEvent.TriggerEvent(Talismonger,"Talismonger_Radical",lspell,lootName,lootCount, last_spell_target)
	end
end
-- Handle loot being opened
local function LOOT_OPENED()
	PopulateLootList()
end
-- Handle the cursor changing to a gear/spell target thingy
local function CURSOR_UPDATE()
	if found_node then 
		return 
	end
	if spell_map[last_spell] then 
		GetWorldTarget()
	end
end
-- Handle the channeling is done
local function UNIT_SPELLCAST_STOP(unit)
	if unit ~= "player" then 
		return 
	end
	if spell_map[last_spell] then
		GetWorldTarget()
	end
	last_spell = spell
	spell = "None"
end
-- Handle failure to cast
local function UNIT_SPELLCAST_FAILED(unit)
	if unit ~= "player" then 
		return 
	end
	spell = "None"
	last_spell = "None"
end
-- Handle interruption
local function UNIT_SPELLCAST_INTERRUPTED(unit)
	if unit ~= "player" then 
		return 
	end
	spell = "None"
	last_spell = "None"
end
-- Handle spell cast started
local function UNIT_SPELLCAST_SENT(unit,spellcast,rank,target)
	if unit ~= "player" then
		return
	end
	found_node = false
	if spell_map[spellcast] then
		spell = spellcast
		last_spell = spell
		last_spell_target = target
		GetWorldTarget()
	else
		spell = "None"
		last_spell = "None"
		last_spell_target = nil
	end
end
-- Gas Extraction
local function CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS(msg)
	local sub_string = "%%s"
	if (GetLocale() == "deDE") then
		sub_string = "%%%d$s"
	end
	local search_string = string.gsub(AURAADDEDOTHERHELPFUL, sub_string, "(.+)")
	local unit, spellcast = string.match(msg, search_string)
	if spell_map[spellcast] then
		spell = spellcast
		last_spell = spell
		if found_node then
			return
		end
		if unit and tracked_nodes[unit] then
			found_node = true
			AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",last_spell,unit)
			return
		end
	end
	found_node = false
end
local function COMBAT_LOG_EVENT_UNFILTERED(timestamp, eventType, srcGUID, srcName, srcFlags, dstGUID, dstName, dstFlags, spellId,spellName,spellSchool,auraType)
	if spell_map[spellName] then
		spell = spellName
		last_spell = spell
		if found_node then
			return
		end
		if tracked_nodes[dstName] then
			found_node = true
			AceEvent.TriggerEvent(Talismonger,"Talismonger_Node",last_spell,dstName)
			return
		end
	end
	found_node = false
end
local eventhandlers = {
	UNIT_SPELLCAST_SENT = UNIT_SPELLCAST_SENT,
	UNIT_SPELLCAST_STOP = UNIT_SPELLCAST_STOP,
	UNIT_SPELLCAST_FAILED = UNIT_SPELLCAST_FAILED,
	UNIT_SPELLCAST_INTERRUPTED = UNIT_SPELLCAST_INTERRUPTED,
	LOOT_OPENED = LOOT_OPENED,
	LOOT_SLOT_CLEARED = LOOT_SLOT_CLEARED,
	LOOT_CLOSED = LOOT_CLOSED,
	CURSOR_UPDATE = CURSOR_UPDATE,
	UI_ERROR_MESSAGE = UI_ERROR_MESSAGE,
	CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS = CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS,
	COMBAT_LOG_EVENT_UNFILTERED = COMBAT_LOG_EVENT_UNFILTERED,
}
-- Activate our selves
local function activate(self, oldLib, oldDeactive)
	Talismonger = self
	spell = "None"
	last_spell = "None"
	found_node = false
	-- copy what we were tracking
	if oldLib then
		tracked_loot,tracked_nodes = oldLib:GetTracked()
	end
	-- hidden tooltip frame
	tooltipper = oldLib and oldLib:GetTooltip() or CreateFrame("GameTooltip","TalismongerLibTooltip",UIParent,"GameTooltipTemplate")
	tooltipper:SetWidth(1)
	tooltipper:SetHeight(1)
	tooltipper:Hide()
	-- create a frame reference so we dont get translations errors
	frame = oldLib and oldLib:GetFrame() or CreateFrame("Frame", "TalismongerLibFrame", UIParent)
	frame:UnregisterAllEvents()
	for eventName in pairs(eventhandlers) do
		frame:RegisterEvent(eventName)
	end
	-- frame event handle
	frame:SetScript("OnEvent", function(f, event, ...)
		eventhandlers[event](...)
	end)
	if oldDeactivate then 
		oldDeactivate(oldLib) 
	end	
end

AceLibrary:Register(Talismonger, MAJOR_VERSION, MINOR_VERSION, activate,nil, nil)
