--[[
Name: Talismonger-3.0
Revision: $Rev: 45183 $
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: 45183 $", 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
if not AceLibrary:HasInstance("Babble-Spell-2.2") then error(MAJOR_VERSION .. " requires Babble-Spell-2.2.") 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 bs = AceLibrary("Babble-Spell-2.2")
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
local spell_map = {}
-- this is a map of spells which can result in node discovery or loot gained from a profession
spell_map["Mining"] = true
spell_map["Fishing"] = true
spell_map["Herb Gathering"] = true
spell_map["Skinning"] = true
spell_map["Extract Gas"] = true
spell_map["Prospecting"] = true
spell_map["Pick Lock"] = true
spell_map["Opening"] = true
spell_map["Pick Pocket"] = true

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 = {
	[1] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[2] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[3] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[4] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[5] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[6] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[7] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
 	},
	[8] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[9] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[10] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[11] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
	[12] = {
		["name"] = "",
		count = 0,
		quest = false,
		giver = false,
	},
}

-- 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
-- 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 what and msg:find(bs["Mining"]) and tracked_nodes[what] then -- Failed to mine a node
		AceEvent.TriggerEvent(Talismonger,"Talismonger_Node","Mining",what)	
	elseif what and ( msg:find(bs["Herbalism"]) or msg:find("약초학") ) and tracked_nodes[what] then -- Failed to herb a plant
		AceEvent.TriggerEvent(Talismonger,"Talismonger_Node","Herb Gathering",what)
	elseif what and msg:find(bs["Pick Lock"]) and tracked_nodes[what] then --Failed to pick a lock on an node
		AceEvent.TriggerEvent(Talismonger,"Talismonger_Node","Opening",what)
	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
	if bs:HasReverseTranslation(spellcast) and spell_map[bs:GetReverseTranslation(spellcast)] then
		spell = bs:GetReverseTranslation(spellcast)
		last_spell = spell
		last_spell_target = target
		GetWorldTarget()
	else
		spell = "None"
		last_spell = "None"
		last_spell_target = nil
	end
	found_node = false
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 bs:HasReverseTranslation(spellcast) and spell_map[bs:GetReverseTranslation(spellcast)] then
		spell = bs:GetReverseTranslation(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)
		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,
}
-- 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()
	frame:RegisterEvent("UNIT_SPELLCAST_SENT") 
	frame:RegisterEvent("UNIT_SPELLCAST_STOP")
	frame:RegisterEvent("UNIT_SPELLCAST_FAILED")
	frame:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED")
	frame:RegisterEvent("CURSOR_UPDATE")
	frame:RegisterEvent("LOOT_OPENED")
	frame:RegisterEvent("LOOT_SLOT_CLEARED")	
	frame:RegisterEvent("LOOT_CLOSED")
	frame:RegisterEvent("UI_ERROR_MESSAGE")
	frame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS")
	-- 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)
