Quantcast
--[[
Feel free to use this source code for any purpose ( except developing nuclear weapon! :)
Please keep original author statement.
@author Alex Shubert (alex.shubert@gmail.com)
]]--
local _G = _G 	--Rumors say that global _G is called by lookup in a super-global table. Have no idea whether it is true.
local _ 		--Sometimes blizzard exposes "_" variable as a global.
local addonName, ptable = ...
local L = ptable.L
local C = ptable.CONST
local TOCVersion = GetAddOnMetadata(addonName, "Version")

AutoTurnIn = LibStub("AceAddon-3.0"):NewAddon("AutoTurnIn", "AceEvent-3.0", "AceConsole-3.0")
AutoTurnIn.defaults = {enabled = true, all = false, trivial = false, lootreward = 1, tournament = 2,
					   darkmoonteleport=true, todarkmoon=true, togglekey=4, darkmoonautostart=true, showrewardtext=true,
					   version=TOCVersion, autoequip = false, debug=false,
					   questlevel=true, watchlevel=true, questshare=false,
					   armor = {}, weapon = {}, stat = {}, secondary = {}}

AutoTurnIn.ldb, AutoTurnIn.allowed = nil, nil
AutoTurnIn.caption = addonName ..' [%s]'
AutoTurnIn.funcList = {[1] = function() return false end, [2]=IsAltKeyDown, [3]=IsControlKeyDown, [4]=IsShiftKeyDown}
AutoTurnIn.OptionsPanel, AutoTurnIn.RewardPanel = nil, nil
AutoTurnIn.autoEquipList={}
AutoTurnIn.questCache={}	-- daily quest cache. Initially is built from player's quest log

AutoTurnIn.ldbstruct = {
		type = "data source",
		icon = "Interface\\QUESTFRAME\\UI-QuestLog-BookIcon",
		label = addonName,
		text = addonName,
		OnClick = function(clickedframe, button)
			if InterfaceOptionsFrame:IsVisible() then
				if (InterfaceOptionsFrameAddOns.selection:GetName() == AutoTurnIn.OptionsPanel:GetName()) then --"AutoTurnInOptionsPanel"
					InterfaceOptionsFrame_OpenToCategory(AutoTurnIn.RewardPanel)
				elseif (InterfaceOptionsFrameAddOns.selection:GetName() == AutoTurnIn.RewardPanel:GetName() ) then --"AutoTurnInRewardPanel"
					InterfaceOptionsFrameCancel:Click()
				end
			else
				InterfaceOptionsFrame_OpenToCategory(AutoTurnIn.OptionsPanel)
			end
		end,
	}

function AutoTurnIn:OnInitialize()
	self:RegisterChatCommand("au", "ConsoleComand")
end

function AutoTurnIn:SetEnabled(enabled)
	AutoTurnInCharacterDB.enabled = not not enabled
	if self.ldb then
		self.ldb.text = self.caption:format((AutoTurnInCharacterDB.enabled) and 'on' or 'off' )
		self.ldb.label = self.ldb.text
	end
end

-- quest autocomplete handlers and functions
function AutoTurnIn:OnEnable()
	if (not AutoTurnInCharacterDB) or (not AutoTurnInCharacterDB.version or (AutoTurnInCharacterDB.version < TOCVersion)) then
		AutoTurnInCharacterDB = nil
		self:Print(L["reset"])
	end

	if not AutoTurnInCharacterDB then
		_G.AutoTurnInCharacterDB = CopyTable(self.defaults)
	end
	local DB = AutoTurnInCharacterDB

	if (tonumber(DB.lootreward) == nil) then
		DB.lootreward = 1
	end
	if (tonumber(DB.togglekey) == nil) then
		DB.togglekey = 1
	end
	DB.armor = DB.armor and DB.armor or {}
	DB.weapon = DB.weapon and DB.weapon or {}
	DB.stat = DB.stat and DB.stat or {}
	DB.secondary = DB.secondary and DB.secondary or {}
	DB.trivial = DB.trivial ~= nil and DB.trivial or false

	DB.questlevel = DB.questlevel == nil and true or DB.questlevel
	DB.watchlevel = DB.watchlevel == nil and true or DB.watchlevel
	DB.questshare = DB.questshare == nil and false or DB.questshare

	local LDB = LibStub:GetLibrary("LibDataBroker-1.1", true)
	if LDB then
		self.ldb = LDB:NewDataObject("AutoTurnIn", self.ldbstruct)
	end

	self:SetEnabled(DB.enabled)
	self:RegisterGossipEvents()
	hooksecurefunc("QuestLog_Update", AutoTurnIn.ShowQuestLevelInLog)
	hooksecurefunc(QuestLogScrollFrame, "update", AutoTurnIn.ShowQuestLevelInLog)
	hooksecurefunc("WatchFrame_Update", AutoTurnIn.ShowQuestLevelInWatchFrame)
	self:Print("Hi! AutoTurnIn author needs your help. If you have any ideas on how to improve addon, may help with translation or just notice a bug let me know pls via curse.com addon page. Your help is greatly appreciated. ")
end

function AutoTurnIn:OnDisable()
  self:UnregisterAllEvents()
end

function AutoTurnIn:RegisterGossipEvents()
	self:RegisterEvent("QUEST_GREETING")
	self:RegisterEvent("GOSSIP_SHOW")
	self:RegisterEvent("QUEST_DETAIL")
	self:RegisterEvent("QUEST_PROGRESS")
	self:RegisterEvent("QUEST_COMPLETE")
	self:RegisterEvent("QUEST_LOG_UPDATE")
end

function AutoTurnIn:QUEST_LOG_UPDATE()
	if ( GetNumQuestLogEntries() > 0 ) then
		for index=1, GetNumQuestLogEntries() do
			local title, _, _, _, isHeader , _, _, isDaily = GetQuestLogTitle(index)
			if not isHeader and isDaily then
				self.questCache[title] = true
			end
		end
		self:UnregisterEvent("QUEST_LOG_UPDATE")
	end
end

-- returns true if quest offered by gossip is daily
function AutoTurnIn:AllOrCachedDaily(questname)
	return AutoTurnInCharacterDB.all or (not not self.questCache[questname])
end

function AutoTurnIn:AllOrDaily(questname)
	return AutoTurnInCharacterDB.all or (QuestIsDaily() or QuestIsWeekly())
end

-- caches offered by gossip quest as daily
function AutoTurnIn:CacheAsDaily(gossipQuest)
	self.questCache[gossipQuest] = true
end

function AutoTurnIn:IsIgnoredQuest(quest)
	local function startsWith(str,template)
		return (string.len(str) >= string.len(template)) and (string.sub(str,1,string.len(template))==template)
	end

	for q in pairs(L.ignoreList) do
		if (startsWith(quest, q)) then
			return true
		end
	end

	return false
end

local p1 = {[true]=L["enabled"], [false]=L["disabled"]}
local p2 = {[true]=L["all"], [false]=L["list"]}
function AutoTurnIn:ConsoleComand(arg)
	arg = strlower(arg)
	if (#arg == 0) then
		-- http://wowpedia.org/Patch_5.3.0/API_changes double call is a workaround
		InterfaceOptionsFrame_OpenToCategory(AutoTurnIn.OptionsPanel)
		InterfaceOptionsFrame_OpenToCategory(AutoTurnIn.OptionsPanel)
	elseif arg == "on" then
		self:SetEnabled(true)
		self:Print(L["enabled"])
	elseif arg == "off"  then
		self:SetEnabled(false)
		self:Print(L["disabled"])
	end
end

-- returns specified item count on player character. It may be some sort of currency or present in inventory as real items.
function AutoTurnIn:GetItemAmount(isCurrency, item)
	local amount = isCurrency and select(2, GetCurrencyInfo(item)) or GetItemCount(item, nil, true)
	return amount and amount or 0
end

-- returns set 'self.allowed' to true if addon is allowed to handle current gossip conversation
-- Cases when it may not : (addon is enabled and toggle key was pressed) or (addon is disabled and toggle key is not pressed)
-- 'forcecheck' does what it name says: forces check
function AutoTurnIn:AllowedToHandle(forcecheck)
	if ( self.allowed == nil or forcecheck ) then
		-- Double 'not' converts possible 'nil' to boolean representation
		local IsModifiedClick = not not self.funcList[AutoTurnInCharacterDB.togglekey]()
		-- it's a simple xor implementation (a ~= b)
		self.allowed = (not not AutoTurnInCharacterDB.enabled) ~= (IsModifiedClick)
	end
	return self.allowed
end

-- Old 'Quest NPC' interaction system. See http://wowprogramming.com/docs/events/QUEST_GREETING
function AutoTurnIn:QUEST_GREETING()
	if (not self:AllowedToHandle(true)) then
		return
	end

	for index=1, GetNumActiveQuests() do
		local quest, isComplete = GetActiveTitle(index)
		if isComplete and (self:AllOrCachedDaily(quest)) then
			SelectActiveQuest(index)
		end
	end

	for index=1, GetNumAvailableQuests() do
		local isTrivial, isDaily, isRepeatable = GetAvailableQuestInfo(index)
		local triviaAndAllowedOrNotTrivia = (not isTrivial) or AutoTurnInCharacterDB.trivial
		local title = GetAvailableTitle(index)
		local quest = L.quests[title]
		local notBlackListed = not (quest and (quest.donotaccept or AutoTurnIn:IsIgnoredQuest(title)))

		if isDaily then
			self:CacheAsDaily(GetAvailableTitle(index))
		end

		if (triviaAndAllowedOrNotTrivia and notBlackListed and (AutoTurnInCharacterDB.all or isDaily)) then
			if quest and quest.amount then
				if self:GetItemAmount(quest.currency, quest.item) >= quest.amount then
					SelectAvailableQuest(index)
				end
			else
				SelectAvailableQuest(index)
			end
		end
	end
end

-- (gaq[i+3]) equals "1" if quest is complete, "nil" otherwise
-- why not 	gaq={GetGossipAvailableQuests()}? Well, tables in lua are truncated for values
-- with ending `nil`. So: '#' for {1,nil, "b", nil} returns 1
function AutoTurnIn:VarArgForActiveQuests(...)
    local MOP_INDEX_CONST = 5 -- was '4' in Cataclysm

	for i=1, select("#", ...), MOP_INDEX_CONST do
		local isComplete = select(i+3, ...) -- complete status
		if ( isComplete ) then
			local questname = select(i, ...)
			if self:AllOrCachedDaily(questname) then
				local quest = L.quests[questname]
				if quest and quest.amount then
					if self:GetItemAmount(quest.currency, quest.item) >= quest.amount then
						SelectGossipActiveQuest(math.floor(i/MOP_INDEX_CONST)+1)
						self.DarkmoonAllowToProceed = false
					end
				else
					SelectGossipActiveQuest(math.floor(i/MOP_INDEX_CONST)+1)
					self.DarkmoonAllowToProceed = false
				end
			end
		end
	end
end

-- like previous function this one works around `nil` values in a list.
function AutoTurnIn:VarArgForAvailableQuests(...)
	local MOP_INDEX_CONST = 6 -- was '5' in Cataclysm
	for i=1, select("#", ...), MOP_INDEX_CONST do
		local title = select(i, ...)
		local isTrivial = select(i+2, ...)
		local isDaily  = select(i+3, ...)
		local triviaAndAllowedOrNotTrivia = (not isTrivial) or AutoTurnInCharacterDB.trivial

		local quest = L.quests[title] -- this quest exists in addons quest DB. There are mostly daily quests
		local notBlackListed = not (quest and (quest.donotaccept or AutoTurnIn:IsIgnoredQuest(title)))

		-- Quest is appropriate if: (it is trivial and trivial are accepted) and (any quest accepted or (it is daily quest that is not in ignore list))
		if (triviaAndAllowedOrNotTrivia and notBlackListed and (AutoTurnInCharacterDB.all or isDaily )) then
			if quest and quest.amount then
				if self:GetItemAmount(quest.currency, quest.item) >= quest.amount then
					SelectGossipAvailableQuest(math.floor(i/MOP_INDEX_CONST)+1)
				end
			else
				SelectGossipAvailableQuest(math.floor(i/MOP_INDEX_CONST)+1)
			end
		end
	end
end

function AutoTurnIn:isDarkmoonAndAllowed(questCount)
	return (self.DarkmoonAllowToProceed and questCount) and
			AutoTurnInCharacterDB.darkmoonautostart and
			(GetZoneText() == L["Darkmoon Island"])
end

function AutoTurnIn:isYoungPandaren()
	return (UnitName("npc")== L["Scared Pandaren Cub"]) and
		(GetRealZoneText() == L["The Jade Forest"])
end

function AutoTurnIn:isDarkmoonFaireMysticMage()
	return AutoTurnInCharacterDB.todarkmoon and
		(UnitName("npc")== L["Darkmoon Faire Mystic Mage"]) and
		(GetRealZoneText() ~= L["Darkmoon Island"])
end

function AutoTurnIn:isDarkmoonFaireTeleportologist()
	return AutoTurnInCharacterDB.darkmoonteleport and (L["DarkmoonFaireTeleport"]==UnitName("target"))
end

function AutoTurnIn:GOSSIP_SHOW()
	if (not self:AllowedToHandle(true)) then
		return
	end

	-- darkmoon fairy gossip sometime turns in quest too fast so I can't relay only on quest number count. It often lie.
	-- this flag is set in VarArgForActiveQuests if any quest may be turned in
	self.DarkmoonAllowToProceed = true
	local questCount = GetNumGossipActiveQuests() > 0

	self:VarArgForActiveQuests(GetGossipActiveQuests())
	self:VarArgForAvailableQuests(GetGossipAvailableQuests())

	if self:isDarkmoonAndAllowed(questCount) then
		local options = {GetGossipOptions()}
		for k, v in pairs(options) do
			if ((v ~= "gossip") and strfind(v, "|cFF0008E8%(")) then
				local opcount = GetNumGossipOptions()
				SelectGossipOption((opcount == 1) and 1 or  math.floor(k / GetNumGossipOptions()) + 1)
			end
		end
	end

	if self:isYoungPandaren() then
		SelectGossipOption(1)
	end

	if self:isDarkmoonFaireMysticMage() or self:isDarkmoonFaireTeleportologist() then
		SelectGossipOption(1)
		StaticPopup1Button1:Click()
	end
end

function AutoTurnIn:QUEST_DETAIL()
	if (QuestIsDaily() or QuestIsWeekly()) then
		self:CacheAsDaily(GetTitleText())
	end

	if self:AllowedToHandle() and self:AllOrDaily() then
		QuestInfoDescriptionText:SetAlphaGradient(0, -1)
		QuestInfoDescriptionText:SetAlpha(1)
		AcceptQuest()
	end
end

function AutoTurnIn:QUEST_ACCEPTED(event, index)
	if AutoTurnInCharacterDB.questshare and GetQuestLogPushable() and GetNumGroupMembers() >= 1 then
		SelectQuestLogEntry(index);
		QuestLogPushQuest();
	end
end

function AutoTurnIn:QUEST_PROGRESS()
    if  self:AllowedToHandle() and IsQuestCompletable() and self:AllOrDaily() then
		CompleteQuest()
    end
end

-- return true if an item is of `ranged` type and is suitable with current options
function AutoTurnIn:IsRangedAndRequired(subclass)
	return (AutoTurnInCharacterDB.weapon['Ranged'] and
		(C.ITEMS['Crossbows'] == subclass or C.ITEMS['Guns'] == subclass or C.ITEMS['Bows'] == subclass))
end

-- return true if an item is of `Jewelry` type and is suitable with current options
function AutoTurnIn:IsJewelryAndRequired(equipSlot)
	return AutoTurnInCharacterDB.armor['Jewelry'] and (C.JEWELRY[equipSlot])
end

-- initiated in AutoTurnIn:TurnInQuest PLAYER_LEAVE_COMBAT ?
AutoTurnIn.delayFrame = CreateFrame('Frame')
AutoTurnIn.delayFrame:Hide()
AutoTurnIn.delayFrame:SetScript('OnUpdate', function()
	if not next(AutoTurnIn.autoEquipList) then
		AutoTurnIn.delayFrame:Hide()
		return
	end

	if (InCombatLockdown()) then
		return
	end

	if (time() < AutoTurnIn.delayFrame.delay) then
		return
	end

	for bag=0, NUM_BAG_SLOTS do
		for slot=1, GetContainerNumSlots(bag), 1 do
			local link = GetContainerItemLink (bag, slot)
			if ( link ) then
				local name = GetItemInfo(link)
				if ( name and AutoTurnIn.autoEquipList[name] ) then
					AutoTurnIn:Print(L["equipping reward"], link)
					EquipItemByName(name, AutoTurnIn.autoEquipList[name])
					AutoTurnIn.autoEquipList[name]=nil
				end
			end
		end
	end
end)

-- return 0 if itemlink is null, item level or math.huge if the item is heirloom
function AutoTurnIn:ItemLevel(itemLink)
	if (not itemLink) then
		return 0
	end
	-- 7 for heirloom http://wowprogramming.com/docs/api_types#itemQuality
	local invQuality, invLevel = select(3, GetItemInfo(itemLink))
	return (invQuality == 7) and math.huge or invLevel
end

-- turns quest in printing reward text if `showrewardtext` option is set.
-- prints appropriate message if item is taken by greed
-- equips received reward if such option selected
function AutoTurnIn:TurnInQuest(rewardIndex)
	if (AutoTurnInCharacterDB.showrewardtext) then
		self:Print((UnitName("target") and  UnitName("target") or '')..'\n', GetRewardText())
	end

	-- Greed or just a single reward
	if (self.forceGreed) or (GetNumQuestChoices() == 1) then
		if (GetNumQuestChoices() > 1) then
			self:Print(L["gogreedy"])
		end
	end

	if (not self.forceGreed) then
		local name = GetQuestItemInfo("choice", (GetNumQuestChoices() == 1) and 1 or rewardIndex)
		if (AutoTurnInCharacterDB.autoequip and (strlen(name) > 0)) then
			local lootLevel, _, _, _, _, equipSlot = select(4, GetItemInfo(GetQuestItemLink("choice", rewardIndex)))

			-- Compares reward and already equipped item levels. If reward level is greater than equipped item, auto equip reward
			local slot = C.SLOTS[equipSlot]
			if (slot) then
				local firstSlot = GetInventorySlotInfo(slot[1])
				local invLink = GetInventoryItemLink("player", firstSlot)
				local eqLevel = self:ItemLevel(invLink)

				-- If reward is a ring  trinket or one-handed weapons all slots must be checked in order to swap one with a lesser item-level
				if (#slot > 1) then
					local secondSlot = GetInventorySlotInfo(slot[2])
					invLink = GetInventoryItemLink("player", secondSlot)
					if (invLink) then
						local eq2Level = self:ItemLevel(invLink)
						firstSlot = (eqLevel > eq2Level) and secondSlot or firstSlot
						eqLevel = (eqLevel > eq2Level) and eq2Level or eqLevel
					end
				end

				-- comparing lowest equipped item level with reward's item level
				if(lootLevel > eqLevel) then
					self.autoEquipList[name] = firstSlot
					self.delayFrame.delay = time() + 2
					self.delayFrame:Show()
				end
			end
		end
	end

	if (AutoTurnInCharacterDB.debug) then
		local link = GetQuestItemLink("choice", rewardIndex)
		if (link) then
			self:Print("Debug: item to loot=", GetQuestItemLink("choice", rewardIndex))
		elseif (GetNumQuestChoices() == 0) then
			self:Print("Debug: turning quest in, no choice required")
		end
	else
		GetQuestReward(rewardIndex)
	end
end

function AutoTurnIn:Greed()
	local index, money = 0, 0;

	for i=1, GetNumQuestChoices() do
		local link = GetQuestItemLink("choice", i)
		if ( link == nil ) then
			return
		end
		local m = select(11, GetItemInfo(link))
		if m > money then
			money = m
			index = i
		end
	end

	if money > 0 then  -- some quests, like tournament ones, offer reputation rewards and they have no cost.
		self.forceGreed = true
		self:TurnInQuest(index)
	end
end

--[[
iterates all rewards and compares with chosen stats and types. If only one appropriate item found then it accepted and quest is turned in.
if more than one suitable item found then item list is shown in a chat window and addons return control to player.

@returns 'true' if one or more suitable reward is found, 'false' otherwise ]]--
-- tables are declared here to optimize memory model. Said that in current implementation it's cheaper to wipe than to create.
AutoTurnIn.found, AutoTurnIn.stattable = {}, {}
function AutoTurnIn:Need()
	wipe(self.found)

	for i=1, GetNumQuestChoices() do
		local link = GetQuestItemLink("choice", i)

		if ( link == nil ) then
			self:Print(L["rewardlag"])
			return true
		end

		local class, subclass, _, equipSlot = select(6, GetItemInfo(link))
		--[[trinkets are out of autoloot]]--
		if  ( 'INVTYPE_TRINKET' == equipSlot )then
			self:Print(L["stopitemfound"]:format(_G[equipSlot]))
			return true
		end
		local itemCandidate = {index=i, points=0, type="", stat="NOTCHOSEN", secondary={}}

		-- TYPE: item is suitable if there are no type specified at all or item type is chosen
		local OkByType = false
		if class == C.WEAPONLABEL then
			OkByType = (not next(AutoTurnInCharacterDB.weapon)) or (AutoTurnInCharacterDB.weapon[subclass] or
						self:IsRangedAndRequired(subclass))
		else
			OkByType = ( not next(AutoTurnInCharacterDB.armor) ) or ( AutoTurnInCharacterDB.armor[subclass] or
						AutoTurnInCharacterDB.armor[equipSlot] or self:IsJewelryAndRequired(equipSlot) )
		end
		itemCandidate.type=subclass .. ((not not OkByType) and "=>OK" or "=>FAIL")

		--STAT+SECONDARY: Same here: if no stat specified or item stat is chosen then item is wanted
		local OkByStat = not next(AutoTurnInCharacterDB.stat) 					-- true if table is empty
		local OkBySecondary = not next(AutoTurnInCharacterDB.secondary) -- true if table is empty
		if (not (OkByStat and OkBySecondaryStat)) then
			wipe(self.stattable)
			GetItemStats(link, self.stattable)
			for stat, value in pairs(self.stattable) do
				if ( AutoTurnInCharacterDB.stat[stat] ) then
					OkByStat = true
					itemCandidate.stat=_G[stat].."=>OK"
				end
				if ( AutoTurnInCharacterDB.secondary[stat] ) then
					OkBySecondary = true
					itemCandidate.points =  itemCandidate.points + 1
					tinsert(itemCandidate.secondary, _G[stat])
				end
			end
		end

		-- User may not choose any options hence any item became 'ok'. That situation is undoubtly incorrect.
		local SettingsExists = (class == C.WEAPONLABEL and next(AutoTurnInCharacterDB.weapon) or next(AutoTurnInCharacterDB.armor))
								or next(AutoTurnInCharacterDB.stat)
 		-- OK means that particular options section is empty or item meets requirements
		if (OkByType and OkByStat and OkBySecondary and SettingsExists) then
			tinsert(self.found, itemCandidate)
		end

		if (AutoTurnInCharacterDB.debug) then
			local secondaryDebug = ""
			for _, sec in pairs(itemCandidate.secondary) do
				secondaryDebug = sec..","..secondaryDebug
			end
			self:Print("Debug:", GetQuestItemLink("choice", itemCandidate.index), " type:", itemCandidate.type,
						" stat:", itemCandidate.stat, " secondary:[", secondaryDebug, "]=>", itemCandidate.points)
		end
	end

	-- HANDLE RESULT
	local foundCount = #self.found
	if foundCount > 1 then
		-- sorting found items by relevance (count of attributes that concidence)
		table.sort(self.found, function(a,b) return a.points > b.points end)

		if (self.found[1].points == self.found[2].points) then
			self:Print(L["multiplefound"])
			for _, reward in pairs(self.found) do
				self:Print(GetQuestItemLink("choice", reward.index))
			end
		else
			self:TurnInQuest(self.found[1].index)
		end
	elseif(foundCount == 1) then
		self:TurnInQuest(self.found[1].index)
	elseif  ( foundCount == 0 and GetNumQuestChoices() > 0 ) and ( not AutoTurnInCharacterDB.greedifnothingfound ) then
		self:Print(L["nosuitablefound"])
	end

	return ( foundCount ~= 0 )
end

-- I was forced to make decision on offhand, cloak and shields separate from armor but I can't pick up my mind about the reason...
function AutoTurnIn:QUEST_COMPLETE()
	-- blasted Lands citadel wonderful NPC. They do not trigger any events except quest_complete.
	if not self:AllowedToHandle() then
		return
	end

	--/script faction = (GameTooltip:NumLines() > 2 and not UnitIsPlayer(select(2,GameTooltip:GetUnit()))) and
    -- getglobal("GameTooltipTextLeft"..GameTooltip:NumLines()):GetText() DEFAULT_CHAT_FRAME:AddMessage(faction or "NIL")
    if self:AllOrDaily() then
		local questname = GetTitleText()
		local quest = L.quests[questname]

		if GetNumQuestChoices() > 1 then
			local function getItemId(typeStr)
				local link = GetQuestItemLink(typeStr, 1) --first item is enough
				return link:match("%b::"):gsub(":", "")
			end

			local itemID = getItemId("choice")
			if (itemID == "46114" or itemID == "45724") then -- Tournament quest found
				self:TurnInQuest(AutoTurnInCharacterDB.tournament)
				return
			end

			if AutoTurnInCharacterDB.lootreward > 1 then -- Auto Loot enabled!
				self.forceGreed = false

				if (AutoTurnInCharacterDB.lootreward == 3) then
					self.forceGreed = (not self:Need() ) and AutoTurnInCharacterDB.greedifnothingfound
				end
				if (AutoTurnInCharacterDB.lootreward == 2 or self.forceGreed) then
					self:Greed()
				end
			end
		else
			self:TurnInQuest(1) -- index greater than '0' enables autoequip check.
		end
    end
end

-- gossip and quest interaction goes through a sequence of windows: gossip [shows a list of available quests] - quest[describes specified quest]
-- sometimes some parts of this chain is skipped. For example, priest in Honor Hold show quest window directly. This is a trick to handle 'toggle key'
hooksecurefunc(QuestFrame, "Hide", function() AutoTurnIn.allowed = nil end)