local GTT = _G.GameTooltip
local GetAddOnMetadata = 			_G.GetAddOnMetadata
local GetContainerNumSlots = 		_G.GetContainerNumSlots
local GetContainerItemQuestInfo = 	_G.GetContainerItemQuestInfo
local GetContainerItemInfo = 		_G.GetContainerItemInfo
local GetItemInfo = 				_G.GetItemInfo
local GetItemSpell = 				_G.GetItemSpell
local GetNumQuestLogEntries = 		_G.GetNumQuestLogEntries
local GetQuestLogTitle = 			_G.GetQuestLogTitle
local GetQuestLogSpecialItemInfo = 	_G.GetQuestLogSpecialItemInfo
local InCombatLockdown = 			_G.InCombatLockdown

local Addon = "QuickQuestItem"
local sides = {"Left","Right"}
local copyTable, debug, queuedItem
local IsInInventory, ParseInventory, ParseQuestLog, ParseTooltip, AddIgnoredItem, IsIgnoredItem
local MacroFinder, CreateQQIMacro, MacroUpdater, UpdateMacro
local ParsedItems = {}
local QuestItems = {}

local function Meta(vaL)
	local text = GetAddOnMetadata(Addon, vaL)
	return text
end

local defaults = {
	last = nil,
	itemlist = {},
	ignorelist = {},
	useTargets = true,
	targets = "",
	version = tonumber(Meta("Version"))
}

local Print = function(text)
	print("|cffccff00"..Meta("Title").."|r: "..text)
end

copyTable = function(i)
	local t = {}
	for key, value in pairs(i) do
		if type(value) == "table" then 
			t[key] = copyTable(value)
		else 
			t[key] = value 
		end
	end
	return t
end

AddIgnoredItem = function(item)
	local itemString,itemLink = GetItemInfo(item)
	if itemString then
		QQI_DB.ignorelist[itemString] = true
		Print(itemLink.." is now ignored.")
	else
		Print("'"..item.."'".." is not a valid item.")
	end
end

RemoveIgnoredItem = function(item)
	local itemString,itemLink = GetItemInfo(item)
	if itemString then 
		QQI_DB.ignorelist[itemString] = nil		
		Print(itemLink.." is no longer being ignored.")
	else
		Print("'"..item.."'".." is not a valid item.")
	end
end

local function SlashHandler(...)
	local arg1, arg2 = strsplit(" ",tostring(...), 2)
	if arg1 == "add" and arg2 ~= nil then
		AddIgnoredItem(arg2)
	elseif arg1 == "remove" and arg2 ~= nil then
		RemoveIgnoredItem(arg2)
	elseif arg1 == "list" then
		local total = 0
		for k,v in pairs(QQI_DB.ignorelist) do
			local itemString = select(2,GetItemInfo(k))
			if v then
				Print(itemString.." is no longer being ignored.")
			else
				Print(itemString.." is now ignored.")
			end
			total = total + 1
		end
		Print(total.." item(s) are currently ignored.")
	else
		Print("Please type /qqi 'add' or 'remove' followed by an 'item id number' or 'itemlink'.")
	end
	ParseInventory()
	ParseQuestLog()
end

SLASH_QQI1 = "/qqi"
SlashCmdList["QQI"] = SlashHandler

IsIgnoredItem = function(item)
	local itemString = GetItemInfo(item)
	if QQI_DB.ignorelist[itemString] then
		return false
	else
		return true
	end
end

IsInInventory = function(itemName)
	for b = 0,4,1 do 
		for s = 1,GetContainerNumSlots(b),1 do 
			local _,_,_,_,readable,_,itemLink = GetContainerItemInfo(b,s)
			if itemLink and IsIgnoredItem(itemLink) then
				local itemString = GetItemInfo(itemLink)
				if itemName == itemString then
					return true, b, s
				end
			end
		end 
	end
	return false
end

ParseInventory = function()
	wipe(ParsedItems)
	for b = 0,4,1 do 
		for s = 1,GetContainerNumSlots(b),1 do 
			local IsQuestItem,StartsQuest,_ = GetContainerItemQuestInfo(b,s)
			local _,_,_,_,readable,_,itemLink = GetContainerItemInfo(b,s)
			local name,rank = GetItemSpell(itemLink)  -- Only means to detect if the item casts any spells (ie usable)
			if IsQuestItem and not StartsQuest and not readable and name then
				local itemString = GetItemInfo(itemLink)
				if not tContains(ParsedItems,itemString) and IsIgnoredItem(itemLink) then
					tinsert(ParsedItems,itemString)
				end
			end
		end 
	end
	UpdateMacro()
end

ParseQuestLog = function()
	wipe(QuestItems)
	for i=1,GetNumQuestLogEntries() do
		local questLogTitleText,_,_,_,isHeader,_,_,isDaily,_,_ = GetQuestLogTitle(i) -- isDaily may possibly be used later
		if not isHeader then
			local link,icon,charges = GetQuestLogSpecialItemInfo(i) -- charges may play a role later
			if link and IsIgnoredItem(link)  then
				local itemName = GetItemInfo(link)
				local name,rank = GetItemSpell(link) -- Only means to detect if the item casts any spells (ie usable)
				if name then
					QuestItems[questLogTitleText] = itemName
				end
			else
				QuestItems[questLogTitleText] = nil
			end
		end
	end
	UpdateMacro()
end

ParseTooltip = function()
	for i=1,2 do -- sides[i] checks 'Left' and 'Right'
		for n=1,GTT:NumLines() do
			if _G["GameTooltipText"..sides[i]..n] then 
				local TooltipText = _G["GameTooltipText"..sides[i]..n]:GetText()
				if TooltipText then
					for k,v in pairs(QuestItems) do
						if k == TooltipText and v ~= nil then -- May need to be changed to string.find / match for more accurate results
							if UnitExists("mouseover") then
								QQI_DB.itemlist[UnitName("mouseover")] = v
								UpdateMacro("mouseover")
							end
						end
					end
				end
			end
		end
	end
end

MacroFinder = function()
	local found = false
	local global, character = GetNumMacros()
	for i=1, global do
		local name = GetMacroInfo(i)
		if name == "QQI_MACRO" then
			found = true
			return true, i
		end
	end
	if not found then
		return false, nil
	end
end

CreateQQIMacro = function()
	if InCombatLockdown() then return end
	local global, character = GetNumMacros()
	local isFound, macroSlot = MacroFinder()
	local hasSpace = global < MAX_ACCOUNT_MACROS -- Changed in 6.0.2
	if hasSpace then 
		if not isFound and not InCombatLockdown() then
			CreateMacro("QQI_MACRO","INV_MISC_QUESTIONMARK","--QQI_MACRO--",nil,nil)
		end
		ParseQuestLog()
		ParseInventory()
		UpdateMacro()
	else
		print(Meta("Title")..": No global macro space. Please delete a macro to create space.")
	end
end

MacroUpdater = function(macroSlot,itemName)
	if itemName then
		EditMacro(macroSlot, "QQI_MACRO","INV_MISC_QUESTIONMARK","#showtooltip\n/use "..itemName,nil,nil)
	else
		EditMacro(macroSlot, "QQI_MACRO","INV_Sigil_UlduarAll","--QQI MACRO--\n--No items available!--",nil,nil)
	end
end

UpdateMacro = function(unit)
	local isFound,macroSlot = MacroFinder()
	if isFound and macroSlot then
		if unit then -- Unit Update is the only point we save QQI_DB.last since we have a Unit we can match a quest item to.
			local itemName = QQI_DB.itemlist[UnitName(unit)]
			if itemName and IsInInventory( itemName ) and IsIgnoredItem(itemName) then
				if not InCombatLockdown() then
					MacroUpdater( macroSlot, itemName )
				else -- Only places information for QQI_DB.last if we're in combat.
					QQI_DB.last = itemName
					queuedItem = true
				end
			end
		else -- Use Parsed Item List
			if not InCombatLockdown() then
				if queuedItem then
					if QQI_DB.last then
						MacroUpdater( macroSlot, QQI_DB.last )
						QQI_DB.last = nil
						queuedItem = false
					end
				else
					if ParsedItems and #ParsedItems > 0 then
						local useList = ""
						for f=0,#ParsedItems do -- Creates a multiline string for the macro to use all parsed items. (This is affected by macro character limits of course)
							if f == 1 then
								useList = ParsedItems[f]
							elseif f > 1 then
								useList = useList.."\n/use "..ParsedItems[f]
							end
						end
						MacroUpdater( macroSlot, useList )
					else
						MacroUpdater( macroSlot, false)
					end
				end				
			end
		end
	else
		CreateQQIMacro()
	end
end

local QQI = CreateFrame("Frame","QQI")
QQI:SetScript("OnEvent",function(self,event,...)
	local arg1, arg2, arg3 = ...
	if event == "ADDON_LOADED" and arg1 == Addon then
		if not QQI_DB or not QQI_DB.version or tonumber(Meta("Version")) > QQI_DB.version then
			QQI_DB = copyTable(defaults)
		end
		self:UnregisterEvent("ADDON_LOADED")
	elseif event == "PLAYER_ENTERING_WORLD" then
		CreateQQIMacro()		
	elseif event == "PLAYER_TARGET_CHANGED" and QQI_DB.useTargets then
		UpdateMacro("target")
	elseif event == "UPDATE_MOUSEOVER_UNIT" then
		ParseTooltip()
	elseif event == "PLAYER_REGEN_ENABLED" then
		UpdateMacro()
	elseif event == "QUEST_LOG_UPDATE" or (event == "UNIT_QUEST_LOG_CHANGED" and arg1 == "player") then
		ParseQuestLog()
		self:UnregisterEvent("QUEST_LOG_UPDATE")
	elseif event == "BAG_UPDATE" then
		ParseInventory()
	end
end)

QQI:RegisterEvent("ADDON_LOADED")
QQI:RegisterEvent("PLAYER_FOCUS_CHANGED")
QQI:RegisterEvent("PLAYER_TARGET_CHANGED")
QQI:RegisterEvent("PLAYER_ENTERING_WORLD")
QQI:RegisterEvent("PLAYER_REGEN_ENABLED")
QQI:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
QQI:RegisterEvent("BAG_UPDATE")
QQI:RegisterEvent("QUEST_LOG_UPDATE")
QQI:RegisterEvent("UNIT_QUEST_LOG_CHANGED")