--[[
--	Sea.wow.questLog
--		Basic Quest Log Functions
--
--	$LastChangedBy: morac $
--	$Rev: 5022 $
--	$Date: 2007-07-14 16:54:00 -0500 (Sat, 14 Jul 2007) $
--]]

-- Stored Quest and entry count (protected from outside modification)
local numEntries = 0;
local numQuests = 0;

-- Protect this function to prevent outside calling without protecting quest log.
-- See Sea.wow.questLog.getPlayerQuestData() for function details
local _getPlayerQuestData = function ( id )
	if ( not id ) then
		return nil;
	end
	
	local questInfo = {};

	-- Select it
	SelectQuestLogEntry(id);

	questInfo.id = id;
	questInfo.title, questInfo.level, questInfo.tag = GetQuestLogTitle(id);
	questInfo.failed = IsCurrentQuestFailed();
	questInfo.description, questInfo.objective = GetQuestLogQuestText();
	questInfo.pushable = GetQuestLogPushable();
	questInfo.timer = GetQuestLogTimeLeft();

	questInfo.objectives = {};

	for i=1, GetNumQuestLeaderBoards(), 1 do
		local item = {};

		item.text, item.questType, item.finished = GetQuestLogLeaderBoard(i);

		if ( item.questType == "item" or item.questType == "object" ) then
			local realGlobal = QUEST_ITEMS_NEEDED;
			QUEST_ITEMS_NEEDED = "%s:%d:%d";
			local info = Sea.util.split(GetQuestLogLeaderBoard(i),":");
			QUEST_ITEMS_NEEDED = realGlobal;

			item.info = {};
			item.info.name = info[1];
			item.info.done = tonumber(info[2]);
			item.info.total = tonumber(info[3]);
		elseif ( item.questType == "monster" ) then
			local realGlobal = QUEST_MONSTERS_KILLED;
			QUEST_MONSTERS_KILLED = "%s:%d:%d";
			local info = Sea.util.split(GetQuestLogLeaderBoard(i),":");
			QUEST_MONSTERS_KILLED = realGlobal;

			-- Could be an objective such as "Find Mankrik's Wife: 0/1" so check
			if info[1] == nil or info[2] == nil or info[3] == nil then
				realGlobal = QUEST_OBJECTS_FOUND;
				QUEST_OBJECTS_FOUND = "%s:%d:%d";
				info = Sea.util.split(GetQuestLogLeaderBoard(i),":");
				QUEST_OBJECTS_FOUND = realGlobal;
			end
			
			item.info = {};
			item.info.name = info[1];
			item.info.done = tonumber(info[2]);
			item.info.total = tonumber(info[3]);
		elseif ( item.questType == "reputation" ) then
			local realGlobal = QUEST_FACTION_NEEDED;
			QUEST_FACTION_NEEDED = "%s:%s:%s";
			local info = Sea.util.split(GetQuestLogLeaderBoard(i),":");
			QUEST_FACTION_NEEDED = realGlobal;

			item.info = {};
			item.info.name = info[1];
			item.info.done = info[2];
			item.info.total = info[3];
		elseif ( item.questType == "event" ) then
			item.info = {};
			item.info.name = "";
			if (item.finished == 1) then 
				item.info.done = 1;
			else
				item.info.done = 0;
			end
			item.info.total = 1;
		end
		table.insert(questInfo.objectives, item);
	end

	if ( GetQuestLogRequiredMoney() > 0 ) then
		questInfo.requiredMoney = GetQuestLogRequiredMoney();
	end
	questInfo.rewardMoney = GetQuestLogRewardMoney();

	questInfo.rewards = {};
	questInfo.choices = {};

	for i=1, GetNumQuestLogChoices(), 1 do
		local item = {};
		item.name, item.texture, item.numItems, item.quality, item.isUsable = GetQuestLogChoiceInfo(i);

		local info = GetQuestLogItemLink("choice", i );
		if ( info ) then
			item.info = {};
			item.info.color = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%1");
			item.info.link = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%2");
			item.info.linkname = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%3");
		end
		table.insert(questInfo.choices, item);
	end
	for i=1, GetNumQuestLogRewards(), 1 do
		local item = {};
		item.name, item.texture, item.numItems, item.quality, item.isUsable = GetQuestLogRewardInfo(i);

		local info = GetQuestLogItemLink("reward", i );
		if ( info ) then
			item.info = {};
			item.info.color = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%1");
			item.info.link = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%2");
			item.info.linkname = string.gsub(info, "|c(.*)|H(.*)|h(.*)|h|r.*", "%3");
		end

		table.insert(questInfo.rewards, item);
	end

	if ( GetRewardSpell() or GetQuestLogRewardSpell() ) then
		questInfo.spellReward={};
		if ( GetRewardSpell() ) then
			questInfo.spellReward.texture, questInfo.spellReward.name = GetRewardSpell();
		else
			questInfo.spellReward.texture, questInfo.spellReward.name = GetQuestLogRewardSpell();
		end
	end

	return questInfo;
end

Sea.wow.questLog = {
	-- Protection Data
	ProtectionData = {};

	--
	--	GetNumQuestLogEntries
	--
	--	 Sea version of Blizzard's GetNumQuestLogEntries function.  The difference is that this always
	--	 returns the total number of entries and quests, while Blizzard's version only returns non-collapsed ones.
	--
	-- Returns:
	-- 	numEntries - (integer) number of entries in the Quest Log (includes collapsable area grouping headlines)
	-- 	numQuests - (integer) number of actual quests
	--
	GetNumQuestLogEntries = function ()
		return numEntries, numQuests;
	end;
	
	--
	--	storeCollapsedQuests
	--
	--	 Stores the currently collapsed quests and returns them
	--
	-- Returns:
	-- 	(table quests)
	-- 	quests - a list containing the title of the collapsed quests
	--
	storeCollapsedQuests = function ()
		local collapsed = {};
		for i=1, MAX_QUESTLOG_QUESTS do
			local title, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete = GetQuestLogTitle(i);
			if ( isCollapsed ) then
				table.insert(collapsed, title);
			end
		end

		return collapsed;
	end;

	--
	--	collapseStoredQuests (collapseTitles)
	--		Collapses all quests whose titles are in the list
	--
	-- Args:
	--	(table collapseTitles)
	--	collapseTitles - the titles which will be collapsed
	--
	-- Returns:
	-- 	nil
	--
	collapseStoredQuests = function (collapseThese)
		for i=1, MAX_QUESTLOG_QUESTS do
			local title, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete = GetQuestLogTitle(i);
			if ( Sea.list.isInList(collapseThese, title) ) then
				CollapseQuestHeader(i);
			end
		end
	end;


	--
	-- 	protectQuestLog()
	-- 		Preserves the quest log state.
	-- 		Don't forget to call unprotectQuestLog()
	-- 		after modifying the log.
	--
	--		This will return false if someone else
	--		has protected the quest log and
	--		not unprotected it yet.
	--
	--	Args:
	--		none
	--	Returns:
	--		true - the log was protected
	--		false - someone else is modifying the log now
	--
	protectQuestLog = function ()
		if ( Sea.wow.questLog.ProtectionData.protected ) then
			return nil;
		end

		-- Store the protected state
		Sea.wow.questLog.ProtectionData.protected = true;

		-- Store the event
		Sea.wow.questLog.ProtectionData.ql_OnEvent = QuestLog_OnEvent;
		QuestLog_OnEvent = function() end;

		-- Store the collapsed
		Sea.wow.questLog.ProtectionData.collapsed = Sea.wow.questLog.storeCollapsedQuests();

		-- Store the selection
		Sea.wow.questLog.ProtectionData.savedID = GetQuestLogSelection();

		-- Store the scroll bar position
		Sea.wow.questLog.ProtectionData.savedScrollBar = FauxScrollFrame_GetOffset(QuestLogListScrollFrame);
		Sea.wow.questLog.ProtectionData.savedValue = QuestLogListScrollFrameScrollBar:GetValue()

		-- Expand everything if there is anything collapse
		-- Expanding triggers a QUEST_LOG_UPDATE event so only do it if really necessary.
		if #(Sea.wow.questLog.ProtectionData.collapsed) > 0 then
			ExpandQuestHeader(0);
		end

		return true;
	end;

	--
	--	unprotectQuestLog()
	--		Restores the state of the quest log.
	--		Useful if you're going to mess with the
	--		quest log in order to get info out, and
	--		don't want the user to notice.
	--
	--	Args:
	--		none
	--	Returns:
	--		nil
	--
	unprotectQuestLog = function ()
		-- Collapse again
		Sea.wow.questLog.collapseStoredQuests(Sea.wow.questLog.ProtectionData.collapsed);

		-- Restore the scroll bar position
		FauxScrollFrame_SetOffset(QuestLogListScrollFrame,Sea.wow.questLog.ProtectionData.savedScrollBar)
		QuestLogListScrollFrameScrollBar:SetValue( Sea.wow.questLog.ProtectionData.savedValue )
		
		-- Restore the selection
		SelectQuestLogEntry(Sea.wow.questLog.ProtectionData.savedID);

		-- Restore the event
		QuestLog_OnEvent = Sea.wow.questLog.ProtectionData.ql_OnEvent;
		Sea.wow.questLog.ProtectionData.ql_OnEvent = nil;

		-- Unprotect
		Sea.wow.questLog.ProtectionData.protected = false;
	end;

	--
	--	getPlayerQuestTree()
	--		Returns a formatted table containing all of
	--		the quests the player currently has.
	--
	--	Returns:
	--
	--	questTree  - list of zones
	--	{
	--		["zone name"] - list of quests
	--		{
	--			{
	--				flatid - (string) id when fully expanded
	--				title - (string) quest title
	--				level - (number) quest level
	--				tag - (string) Elite or Raid
	--				groupNum - (number) Number of recommended party members
	--				complete - (number) is quest completed (1 = complete, -1 = fail, nil otherwise)
	--				daily - (boolean) is daily quest
	--			}
	--		}
	--	}
	--
	getPlayerQuestTree = function()
		local questList = {};

		-- Build our quest list
		local tempNumEntries, tempNumQuests = GetNumQuestLogEntries();

		-- If no quests, exit so we don't call ExpandQuestHeader which triggers another QUEST_LOG_UPDATE event
		if ( tempNumEntries == 0 ) then
			return questList;
		end

		if ( not Sea.wow.questLog.protectQuestLog() ) then
			return nil;
		end

		-- Rebuild our quest list since expanding quest headers can cause a change
		numEntries, numQuests = GetNumQuestLogEntries();

		local activeTable = nil;

		for i=1, MAX_QUESTLOG_QUESTS + numEntries, 1 do
			local questIndex = i;
			if ( questIndex <= numEntries ) then
				local title, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete, isDaily = GetQuestLogTitle(questIndex);
				local color;
				if ( title ) then
					if ( isHeader ) then
						questList[title] = {};
						activeTable = questList[title];
					else
						if ( activeTable == nil ) then
							activeTable = questList;
						end

						local entry = {};
						entry.id = i;
						entry.flatid = i;
						entry.title = title;
						entry.level = level;
						entry.tag = questTag;
						entry.groupNum = suggestedGroup;
						entry.complete = isComplete;
						entry.daily = isDaily;

						table.insert(activeTable, entry);
					end
				end
			end
		end

		-- Unprotect
		Sea.wow.questLog.unprotectQuestLog();

		return questList;

	end;

	--
	--	getPlayerQuestTreeFlat()
	--		Returns a flat table containing all of
	--		the quests the player currently has.
	--
	--	Returns:
	--
	--	questTree  - table of quests, the key in this table equals id
	--	{
	--		{
	--			id - (string) id when fully expanded
	--			title - (string) quest title
	--			level - (number) quest level
	--			tag - (string) Elite or Raid
	--          groupNum - (number) Number of recommended party members
	--			complete - (number) is quest completed (1 = complete, -1 = fail, nil otherwise)
	--			zone - (string) collapsable header name
	--          daily - (boolean) is daily quest
	--		}
	--	}
	--
	getPlayerQuestTreeFlat = function()
		local questList = {};

		-- Build our quest list
		local tempNumEntries, tempNumQuests = GetNumQuestLogEntries();

		-- If no quests, exit so we don't call ExpandQuestHeader which triggers another QUEST_LOG_UPDATE event
		if ( tempNumEntries == 0 ) then
			return questList;
		end

		if ( not Sea.wow.questLog.protectQuestLog() ) then
			return nil;
		end

		-- Rebuild our quest list since expanding quest headers can cause a change
		numEntries, numQuests = GetNumQuestLogEntries();

		local currentZone;

		for questIndex=1, numEntries, 1 do
			local title, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete, isDaily = GetQuestLogTitle(questIndex);
			if ( title ) then
				if ( isHeader ) then
					currentZone = title;
				else
					local entry =
					{
						id = questIndex;
						title = title;
						level = level;
						tag = questTag;
						groupNum = suggestedGroup;
						complete = isComplete;
						zone = currentZone;
						daily = isDaily
					};

					questList[questIndex] = entry;
				end
			end
		end

		-- Unprotect
		Sea.wow.questLog.unprotectQuestLog();

		return questList;
	end;

	-- 
	--
	--	convertPlayerQuestTreeFromFlatToNormal()
	--		Converts output of getPlayerQuestTreeFlat to format of
	--		output of getPlayerQuestTree. It's shallow copy: doesn't 
	--		copy actual quest entries, just reference them.
	--
	--	Args:
	--		tree - table of quests as returned by getPlayerQuestTreeFlat
	--
	--	Returns:
	--
	--	See getPlayerQuestTree.
	--
	convertPlayerQuestTreeFromFlatToNormal = function(tree)
		if not tree then
			return;
		end
		local currentZone;
		local result = {};
		for k,v in pairs(tree) do
			if v.zone ~= currentZone then
				currentZone = v.zone;

				if result[currentZone] == nil then -- Only Zap when NIL!
				   result[currentZone] = {};
				end
			end
			v.flatid = v.id;
			table.insert(result[currentZone], v);
		end
		return result;
	end;

	--
	--	getPlayerQuestData ( number id )
	--
	--		Returns a formatted table containing the quest's information
	--		If you specify its id when the table is flat.
	--
	--	Args:
	--		id - flattened (expanded) id of the quest
	--
	--	Returns:
	--		questData - (table)
	--		{
	--			id - flattened id (number)
	--			title - quest title (string)
	--			level - quest level (number)
	--			tag - quest tag (string)
	--			failed - (boolean) - has quest failed?
	--			pushable - (boolean) - is quest shareable?
	--			timer - (number)
	--			description - full description text (string)
	--			objective - objective text (string)
	--			requiredMoney - (number) Money required
	--			rewardMoney - (number) Money reward
	--			objectives - objective status (table)
	--				{
	--					text - description (string)
	--					questType - monster, reputation or item (string)
	--					finished - (boolean)
	--					info - status information (table)
	--					{
	--						name - monster/item/faction name
	--						done 	- number - number obtained or killed
	--							- string - faction achieved
	--						total 	- number - amount needed
	--							- string - faction required
	--					}
	--				}
	--			choices - choices of items (table)
	--				{
	--					name - item name
	--					texture - item texture
	--					numItems - count of items (number)
	--					quality - item quality (number)
	--					isUsable - (boolean)
	--					info - link information (table)
	--						{
	--							color - color string (string)
	--							link - item link (string)
	--							linkname - [Item of Power] (string)
	--						}
	--				}
	--			rewards - items always recieved (table)
	--				{
	--					name - item name
	--					texture - item texture
	--					numItems - count of items (number)
	--					quality - item quality (number)
	--					isUsable - (boolean)
	--					info - link information (table)
	--						{
	--							color - color string (string)
	--							link - item link (string)
	--							linkname - [Item of Power] (string)
	--						}
	--				}
	--			spellReward - the spell given to you after the quest (table)
	--				{
	--					name - spell name (string)
	--					texture - spell texture (string)
	--				}
	--		}
	--
	getPlayerQuestData = function ( id )
		if ( not Sea.wow.questLog.protectQuestLog() ) then
			return false;
		end

		if ( not id ) then
			-- Unprotect
			Sea.wow.questLog.unprotectQuestLog();
			return nil;
		end

        -- Get Quest Info
		local questInfo = _getPlayerQuestData(id);
		
		-- Unprotect
		Sea.wow.questLog.unprotectQuestLog();

		return questInfo;
	end;
	
	--
	--	getPlayerDetailedQuestTreeFlat()
	--		Returns a flat table containing all of
	--		the quests the player currently has and the 
	--      details of the quests.
	--
	--	Returns:
	--
	--	questTree  - table of quests, the key in this table equals id
	--	{
	--		{
	--			id - (string) id when fully expanded
	--			title - (string) quest title
	--			level - (number) quest level
	--			tag - (string) Elite or Raid
	--          groupNum - (number) Number of recommended party members
	--			complete - (number) is quest completed (1 = complete, -1 = fail, nil otherwise)
	--			zone - (string) collapsable header name
	--          daily - (boolean) is daily quest
	--			failed - (boolean) - has quest failed?
	--			pushable - (boolean) - is quest shareable?
	--			timer - (number)
	--			description - full description text (string)
	--			objective - objective text (string)
	--			requiredMoney - (number) Money required
	--			rewardMoney - (number) Money reward
	--			objectives - objective status (table)
	--				{
	--					text - description (string)
	--					questType - monster, reputation or item (string)
	--					finished - (boolean)
	--					info - status information (table)
	--					{
	--						name 	- monster/item/faction name
	--						done 	- number - number obtained or killed
	--								- string - faction achieved
	--						total 	- number - amount needed
	--								- string - faction required
	--					}
	--				}
	--			choices - choices of items (table)
	--				{
	--					name - item name
	--					texture - item texture
	--					numItems - count of items (number)
	--					quality - item quality (number)
	--					isUsable - (boolean)
	--					info - link information (table)
	--						{
	--							color - color string (string)
	--							link - item link (string)
	--							linkname - [Item of Power] (string)
	--						}
	--				}
	--			rewards - items always recieved (table)
	--				{
	--					name - item name
	--					texture - item texture
	--					numItems - count of items (number)
	--					quality - item quality (number)
	--					isUsable - (boolean)
	--					info - link information (table)
	--						{
	--							color - color string (string)
	--							link - item link (string)
	--							linkname - [Item of Power] (string)
	--						}
	--				}
	--			spellReward - the spell given to you after the quest (table)
	--				{
	--					name - spell name (string)
	--					texture - spell texture (string)
	--				}
	--		}
	--	}
	--
	getPlayerDetailedQuestTreeFlat = function()
		local questList = {};

		-- Build our quest list
		local tempNumEntries, tempNumQuests = GetNumQuestLogEntries();

		-- If no quests, exit so we don't call ExpandQuestHeader which triggers another QUEST_LOG_UPDATE event
		if ( tempNumEntries == 0 ) then
			return questList;
		end

		if ( not Sea.wow.questLog.protectQuestLog() ) then
			return nil;
		end

		-- Rebuild our quest list since expanding quest headers can cause a change
		numEntries, numQuests = GetNumQuestLogEntries();

		local currentZone;

		for questIndex=1, numEntries, 1 do
			local title, level, questTag, suggestedGroup, isHeader, isCollapsed, isComplete, isDaily = GetQuestLogTitle(questIndex);
			if ( title ) then
				if ( isHeader ) then
					currentZone = title;
				else
					local entry =
					{
						id = questIndex;
						title = title;
						level = level;
						tag = questTag;
						groupNum = suggestedGroup;
						complete = isComplete;
						zone = currentZone;
						daily = isDaily;
					};

					local questData = _getPlayerQuestData(questIndex);
					if (questData) then
						entry.failed = questData.failed;
						entry.pushable = questData.pushable;
						entry.timer = questData.timer;
						entry.description = questData.description;
						entry.objective = questData.objective;
						entry.objectives = Sea.table.copy(questData.objectives);
						entry.choices = Sea.table.copy(questData.choices);
						entry.rewards = Sea.table.copy(questData.rewards);
						entry.rewardMoney = questData.rewardMoney;
						if ( questData.rewardMoney) then
							entry.requiredMoney = questData.requiredMoney;
						end
						entry.spellReward = Sea.table.copy(questData.spellReward);
					end

					questList[questIndex] = entry;
				end
			end
		end

		-- Unprotect
		Sea.wow.questLog.unprotectQuestLog();

		return questList;
	end;
};

