-- MetaMapQST
-- Written by MetaHawk - aka Urshurak

QST_DELAY				= 0.5;
QST_BUTTONWIDTH = 400;

QST_HEADERCOLOUR    = "|cFFBFBFFF";
QST_TITLECOLOUR     = "|cFFFFFFFF";
QST_OVERVIEWCOLOUR  = "|cFF7F7F7F";
QST_SPECIALCOLOUR   = "|cFFFFFF00";
QST_INITIALCOLOUR   = "|cFFD82619";
QST_MIDDLECOLOUR    = "|cFFFFFF00";
QST_COMPLETEDCOLOUR	= "|cFF00FF19";

QST_Options       = {};
QST_QuestLog     = {};
QST_QuestBase     = {};
QST_QuestList     = {};
QST_ItemList      = {};
QST_AcceptQuest   = nil;
QST_FinishQuest   = nil;
QST_AbandonQuest   = nil;

QST_Default = {
	["LoadAlways"]  = false,
	["ShowPlayers"] = false,
	["ShowAll"]     = false,
	["ShowActive"]  = false,
	["SaveHistory"] = true,
	["SaveDesc"]    = true,
	["SaveRew"]     = true,
	["Padding"]     = 10,
	["SortOrder"]   = "logtime",
}

QST_VarsLoaded      = false;
QST_ButtonTotal	    = 0;
QST_ActivePlayer    = "";
QST_SelectedPlayer  = "";
QST_LastRefresh     = 0.0;
QST_QuestLogUpdate  = false;
QST_ShowHeader      = true;
QST_LastSearch      = "";
QST_QuestDetail     = nil;

QST_FilterMenu = {
	[1] = {QST_SORT_TITLE,  "SortOrdertitle"},
	[2] = {QST_SORT_LEVEL,  "SortOrderlevel"},
	[3] = {QST_SORT_LOGGED, "SortOrderlogtime"},
	[4] = {"", "", 1},
	[5] = {QST_FILTER_ALL, "ShowAll"},
	[6] = {QST_FILTER_MAP, "ShowPlayers"},
	[7] = {QST_FILTER_ACT, "ShowActive"},
	[8] = {"", "", 1},
}

function QST_OnLoad()
	this:RegisterEvent("ADDON_LOADED");
	this:RegisterEvent("QUEST_LOG_UPDATE");
	this:RegisterEvent("QUEST_DETAIL");
	this:RegisterEvent("QUEST_FINISHED");
	this:RegisterEvent("UNIT_NAME_UPDATE");
	this:RegisterEvent("PLAYER_LEVEL_UP");
	this:RegisterEvent("WORLD_MAP_UPDATE");
	this:RegisterEvent("ZONE_CHANGED_NEW_AREA");
	this:RegisterEvent("UPDATE_MOUSEOVER_UNIT");
end

function QST_OnEvent(event)
	if(event == "ADDON_LOADED" and arg1 == "MetaMapQST") then
		QST_LoadConfig();
	elseif(event == "QUEST_DETAIL") then
		QST_QuestDetail = GetTitleText();
	elseif(event == "ZONE_CHANGED_NEW_AREA") then
		QST_RefreshDisplay();
	elseif(event == "WORLD_MAP_UPDATE" and QST_DisplayFrame:IsVisible()) then
		QST_RefreshDisplay();
	elseif(event == "INSTANCE_MAP_UPDATE" and QST_DisplayFrame:IsVisible()) then
		QST_RefreshDisplay();
	elseif(event == "PLAYER_LEVEL_UP" and QST_DisplayFrame:IsVisible()) then
		QST_ShowDisplay();
	elseif(event == "UNIT_NAME_UPDATE" and QST_DisplayFrame:IsVisible()) then
		QST_ShowDisplay();
	elseif(event == "UPDATE_MOUSEOVER_UNIT") then
		QST_EnhanceTooltip();
	elseif(event == "QUEST_LOG_UPDATE") then
		QST_QuestLogUpdate = true;
		QST_LastRefresh = 0.0;
	end
end

function QST_ToggleFrame(mode)
	if(mode == 2) then return; end
	if(QST_DisplayFrame:IsVisible()) then
		MetaMapContainer_ShowFrame();
		if(mode == 1) then
			MetaMap_ToggleFrame(WorldMapFrame);
		end
	else
		if(not WorldMapFrame:IsVisible()) then
			MetaMap_ToggleFrame(WorldMapFrame);
		end
		MetaMapContainer_ShowFrame(QST_DisplayFrame);
	end
end

function QST_LoadConfig()
	--- Hooks
	QST_Orig_afTooltip = aftt_setName;
	aftt_setName = QST_afTooltip;
	QST_Orig_ContainerButtonOnEnter = ContainerFrameItemButton_OnEnter;
	ContainerFrameItemButton_OnEnter = QST_ContainerButtonOnEnter;
	Orig_QuestAcceptOnClick = QuestDetailAcceptButton_OnClick;
	QuestDetailAcceptButton_OnClick = QST_QuestAcceptOnClick;
	Orig_RewardCompleteOnClick = QuestRewardCompleteButton_OnClick;
	QuestRewardCompleteButton_OnClick = QST_QuestCompleteOnClick;
	Orig_AbandonQuest = AbandonQuest;
	AbandonQuest = QST_AbandonQuestOnClick;
	if(IsAddOnLoaded("Sanity")) then
		QST_OrigSanityButtonOnEnter = SanityItemButton_OnEnter;
		SanityItemButton_OnEnter = QST_SanityButtonOnEnter;
	end
	--- End Hooks
	for option, value in QST_Default do
		if(QST_Options[option] == nil) then QST_Options[option] = value; end
	end
	if(QST_QuestLog == nil) then QST_QuestLog = {}; end
	QST_ActivePlayer = UnitName("player").." of "..GetCVar("realmName");
	QST_SelectedPlayer = QST_ActivePlayer;
	QST_UpdateQuests();
	QST_UpdatePlayer();
	UIDropDownMenu_Initialize(QST_FilterSelectMenu, QST_FilterMenuInit, "MENU");
	UIDropDownMenu_SetText(QST_SelectedPlayer, QST_FilterSelect)
	QST_VarsLoaded = true;
end

function QST_OnUpdate(arg1)
	if(not QST_VarsLoaded) then return; end
	if(QST_QuestLogUpdate == true) then
		QST_LastRefresh = QST_LastRefresh + arg1;
		if(QST_LastRefresh > QST_DELAY) then
			QST_UpdateQuests();
			QST_LastRefresh = 0.0;
			QST_QuestLogUpdate = false;
		end
	end
end

function QST_UpdatePlayer()
	local tempPlayers = {};
	local t = table.getn(QST_FilterMenu)+1;
	for index, qData in QST_QuestLog do
		for player, value in qData.qPlayer do
			if(tempPlayers[player] == nil) then
				tempPlayers[player] = 1;
			end
		end
	end
	for index, qData in QST_QuestBase do
		for player, value in qData.qPlayer do
			if(tempPlayers[player] == nil) then
				tempPlayers[player] = 1;
			end
		end
	end
	for player, value in tempPlayers do
		QST_FilterMenu[t] = {player, "player"};
		t = t +1;
	end
end

function QST_UpdateQuests()
	local qData, qZone;
	for qIndex=1, GetNumQuestLogEntries(), 1 do
		local QuestTitle, _, _, isHeader = GetQuestLogTitle(qIndex);
		if(isHeader) then
			qZone = QuestTitle;
		else
			SelectQuestLogEntry(qIndex);
			for index, qQuests in QST_QuestLog do
				if(qQuests.qTitle == QuestTitle and qQuests.qPlayer[QST_ActivePlayer]) then 
					qData = qQuests; break;
				end
			end
			if(qData) then
				QST_UpdateDataBase(qData, qIndex);
			else
				local n = table.getn(QST_QuestLog)+1;
				QST_QuestLog[n] = {};
				QST_CreateDatabase(QST_QuestLog[n], qZone, qIndex);
			end
			qData = nil;
		end
	end
	QST_SetLogOrder();
	if(QST_DisplayFrame:IsVisible()) then
		QST_RefreshDisplay();
	end
end

function QST_SetLogOrder()
	local TmpData = {};
	local new = 1;
	for index, value in QST_QuestLog do
		TmpData[new] = {};
		TmpData[new] = value;
		new = new +1;
	end
	QST_QuestLog = {};
	QST_QuestLog = TmpData;
end

function QST_UpdateDataBase(qData, qIndex)
	local QuestTitle, QuestLevel, QuestTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(qIndex);
	local Description, Objectives = GetQuestLogQuestText();
	local status = "";
	if(isComplete == nil) then status = QST_MIDDLECOLOUR..QST_QUEST_ACTIVE;
	elseif(isComplete < 0) then status = QST_INITIALCOLOUR..QST_QUEST_FAILED;
	elseif(isComplete == 1) then status = QST_COMPLETEDCOLOUR..QST_QUEST_DONE; end

	qData.qIndex = qIndex;
	qData["qPlayer"][QST_ActivePlayer]["qStatus"] = status;
	if(QST_AcceptQuest and QST_AcceptQuest.Title == QuestTitle) then
		qData["qNPC"][1] = {};
		qData["qNPC"][1].qName = QST_AcceptQuest.NPC;
		qData["qNPC"][1].qZone = QST_AcceptQuest.zone;
		qData["qNPC"][1].qX = QST_AcceptQuest.xPos;
		qData["qNPC"][1].qY = QST_AcceptQuest.yPos;
		QST_AcceptQuest = nil;
	end
	QST_UpdateQuestItems(QuestTitle, qData);
end

function QST_CreateDatabase(qData, qZone, qIndex)
	local QuestTitle, QuestLevel, QuestTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(qIndex);
	local Description, Objectives = GetQuestLogQuestText();
	local continent, zone = MetaMap_NameToZoneID(qZone);
	local status = "";

	if(isComplete == nil) then status = QST_MIDDLECOLOUR..QST_QUEST_ACTIVE;
	elseif(isComplete < 0) then status = QST_INITIALCOLOUR..QST_QUEST_FAILED;
	elseif(isComplete == 1) then status = QST_COMPLETEDCOLOUR..QST_QUEST_DONE; end
	if(zone == 0) then qData["qArea"] = GetRealZoneText(); end

	qData["qTitle"] = QuestTitle;
	qData["qZone"] = qZone;
	qData["qObj"] = Objectives;
	qData["qDesc"] = Description;
	qData["qLogtime"] = GetTime();
	qData["qNote"] = "";
	qData["qMoney"] = GetQuestLogRewardMoney();
	qData["qLevel"] = QuestLevel;
	qData["qTag"] = QuestTag;
	qData["qIndex"] = qIndex;
	qData["qPlayer"] = {};
	qData["qNPC"] = {};
	qData["qPlayer"][QST_ActivePlayer] = {};
	qData["qPlayer"][QST_ActivePlayer]["qStatus"] = status;
		
	if(GetNumQuestLogRewards() > 0) then
		qData["qReward"] = {};
		for index=1, GetNumQuestLogRewards(), 1 do
			local name, texture, numItems, quality, isUsable = GetQuestLogRewardInfo(index);
			qData["qReward"][index] = {};
			qData["qReward"][index]["qAmount"] = numItems;
			qData["qReward"][index]["qLink"] = GetQuestLogItemLink("reward", index);
			qData["qReward"][index]["qTex"] = texture;
		end
	end
	if(GetNumQuestLogChoices() > 0) then
		qData["qChoice"] = {};
		for index=1, GetNumQuestLogChoices(), 1 do
			local name, texture, numItems, quality, isUsable = GetQuestLogChoiceInfo(index);
			qData["qChoice"][index] = {};
			qData["qChoice"][index]["qAmount"] = numItems;
			qData["qChoice"][index]["qLink"] = GetQuestLogItemLink("choice", index);
			qData["qChoice"][index]["qTex"] = texture;
		end
	end
	if(GetQuestLogRewardSpell()) then
		qData["qSpell"] = {};
		local texture, name = GetQuestLogRewardSpell();
		qData["qSpell"] = {};
		qData["qSpell"]["qLink"] = name;
		qData["qSpell"]["qTex"] = texture;
	end
	if(QST_AcceptQuest and QST_AcceptQuest.Title == QuestTitle) then
		qData["qNPC"][1] = {};
		qData["qNPC"][1].qName = QST_AcceptQuest.NPC;
		qData["qNPC"][1].qZone = QST_AcceptQuest.zone;
		qData["qNPC"][1].qX = QST_AcceptQuest.xPos;
		qData["qNPC"][1].qY = QST_AcceptQuest.yPos;
		QST_AcceptQuest = nil;
	end
	QST_UpdateQuestItems(QuestTitle, qData);
end

function QST_UpdateQuestItems(QuestTitle, qData)
	if(GetNumQuestLeaderBoards() > 0) then
		qData["qItems"] = {};
		for index=1, GetNumQuestLeaderBoards(), 1 do
			local LeaderBoardText, iType, Finished = GetQuestLogLeaderBoard(index);
			local i, j, ItemName, NumItems, NumNeeded = string.find(LeaderBoardText, "(.*):%s*([-%d]+)%s*/%s*([-%d]+)%s*$");
			if(NumItems ~= nil) then
				local slain = "";
				i, j = string.find(ItemName, QST_TOOLTIP_SLAIN);
				if(i ~= nil) then
					ItemName = string.sub(ItemName, 1, i - 2);
					slain = " "..QST_TOOLTIP_SLAIN;
				end
				qData["qItems"][index] = {};
				qData["qItems"][index] = ItemName..slain..": "..NumItems.."/"..NumNeeded;
				QST_ItemList[ItemName] = {};
				QST_ItemList[ItemName]["qTitle"] = QuestTitle;
				QST_ItemList[ItemName]["qItems"] = NumItems.."/"..NumNeeded;
			end
		end
	end
end

function QST_RefreshDisplay()
	QST_QuestList = {};
	local qCount2, qTotal2, aCount2, aTotal2 = QST_DisplayInit("QST_QuestBase");
	local qCount1, qTotal1, aCount1, aTotal1 = QST_DisplayInit("QST_QuestLog");
	QST_InfoText1:SetText(QST_QUEST_TEXT..": "..qCount1 + qCount2.."/"..qTotal1 + qTotal2);
	QST_InfoText2:SetText(QST_QUEST_ACTIVE..": "..aCount1 + aCount2.."/"..aTotal1 + aTotal2);
	QST_SortQuestList(QST_Options.SortOrder);
	QST_ShowDisplay();
end

function QST_DisplayInit(qDB)
	QST_ShowHeader = true;
	local qBase = getglobal(qDB);
	local questCount, questTotal, activeCount, activeTotal = 0,0,0,0;
	local mapContinent, mapZone, _, mapName = MetaMap_GetCurrentMapInfo();
	QST_HeaderText:SetText(mapName);

	for index, qData in qBase do
		local showThis = false;
		local continent, zone;
		questTotal = questTotal +1;
		if(qData.qIndex) then activeTotal = activeTotal +1; end
		if(qData.qArea) then
			if(QST_GetLandMark(qData.qZone)) then showThis = true;
			elseif(qData.qArea == mapName) then showThis = true;
			else continent, zone = MetaMap_NameToZoneID(qData.qArea); end
		else
			continent, zone = MetaMap_NameToZoneID(qData.qZone);
		end
		if(mapName == "World") then showThis = true; end
		if(continent == mapContinent and mapName == "Kalimdor") then showThis = true; end
		if(continent == mapContinent and mapName == "Eastern Kingdoms") then showThis = true; end
		if(continent == mapContinent and zone == mapZone) then showThis = true; end
		if(not QST_Options.ShowPlayers and not qData["qPlayer"][QST_SelectedPlayer]) then showThis = false; end
		if(QST_Options.ShowActive and not qData.qIndex) then showThis = false; end
		if(qData.qZone == mapName and not QST_Options.ShowAll) then QST_ShowHeader = false; end
		if(qData.qArea and qData.qArea == mapName and not QST_Options.ShowAll) then QST_ShowHeader = false; end
			if(showThis or QST_Options.ShowAll) then
			if(string.find(string.lower(qData.qTitle),string.lower(QST_LastSearch),1,true)~=nil
				or string.find(string.lower(qData.qDesc),string.lower(QST_LastSearch),1,true)~=nil
				or string.find(string.lower(qData.qObj),string.lower(QST_LastSearch),1,true)~=nil
				or string.find(string.lower(qData.qZone),string.lower(QST_LastSearch),1,true)~=nil
				or string.find(string.lower(qData.qNote),string.lower(QST_LastSearch),1,true)~=nil) then
				tinsert(QST_QuestList, {qTitle = qData.qTitle, qZone = qData.qZone, qLevel = qData.qLevel, qLogtime = qData.qLogtime, qDB = qDB, qIndex = index});
				if(qData["qPlayer"][QST_SelectedPlayer]) then
					if(string.find(qData["qPlayer"][QST_SelectedPlayer]["qStatus"], "Active")) then activeCount = activeCount +1; end
				end
				questCount = questCount + 1;
			end
		end
	end
	return questCount, questTotal, activeCount, activeTotal;
end

function QST_SortQuestList(sort)
	local tmpList = {};
	local name = "";
	local tmp = MetaMap_sortType;
	MetaMap_sortType = METAMAP_SORTBY_NAME;
	for index, value in QST_QuestList do
		if(sort == "title") then
			name = value.qZone..value.qTitle..value.qLevel;
		elseif(sort == "level") then
			name = value.qZone..value.qLevel..value.qTitle;
		else
			name = value.qZone..value.qLogtime;
		end
		tinsert(tmpList, {qTitle = value.qTitle, qZone = value.qZone, qLevel = value.qLevel, qLogtime = value.qLogtime, qDB = value.qDB, qIndex = value.qIndex, name = name});
	end
	QST_QuestList = {};
  table.sort(tmpList, MetaMap_SortCriteria);
	for index, value in tmpList do
		tinsert(QST_QuestList, {qTitle = value.qTitle, qZone = value.qZone, qLevel = value.qLevel, qLogtime = value.qLogtime, qDB = value.qDB, qIndex = value.qIndex});
	end
	tmpList = nil;
	MetaMap_sortType = tmp;
end

function QST_ShowDisplay()
	local questInfo = "";
	local buttonID = 1;
	local setHeader = "";
	local ScrollHeight = 0;

	for index, qList in QST_QuestList do
		local qData = getglobal(qList.qDB)[qList.qIndex];
		if(QST_ShowHeader and setHeader ~= qData.qZone) then
			local area = "";
			local button = QST_CreateButton(buttonID);
			local buttontext = getglobal("QSTButton"..buttonID.."Text");
			if(qData.qArea) then area = " ("..qData.qArea..")"; end
			buttontext:SetText(format(QST_HEADERCOLOUR.."%s|r", qData.qZone..area).."\n");
			if(buttonID == 1) then
				button:SetPoint("TOPLEFT", "QST_ScrollChild", "TOPLEFT", 10, -10)
			else
				button:SetPoint("TOP", getglobal("QSTButton"..buttonID-1), "BOTTOM", 0, 0)
			end
			if(qData.qArea) then
				button.qMap = qData.qArea;
			else
				button.qMap = qData.qZone;
			end
			button.qDB = qList.qDB;
			button.qIndex = qList.qIndex;
			buttontext:SetWidth(QST_BUTTONWIDTH);
			button:SetHeight(buttontext:GetHeight() + QST_Options.Padding);
			button:SetParent("QST_ScrollChild");
			button:Show();
			ScrollHeight = ScrollHeight + button:GetHeight();
			setHeader = qData.qZone;
			buttonID = buttonID +1;
		end
		local button = QST_CreateButton(buttonID);
		local buttontext = getglobal("QSTButton"..buttonID.."Text");
		if(buttonID == 1) then
			button:SetPoint("TOPLEFT", "QST_ScrollChild", "TOPLEFT", 10, -10)
		else
			button:SetPoint("TOP", getglobal("QSTButton"..buttonID-1), "BOTTOM", 0, 0)
		end
		local col = GetDifficultyColor(qData.qLevel);
		local cText = "|c%02X%02X%02X%02X%s|r";
		local R = col.r * 255; local G = col.g * 255; local B = col.b * 255;
		questInfo = questInfo.."  ";
		if(qData.qTag == ELITE) then
			questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."+] ");
		elseif(qData.qTag == QST_DUNGEON) then
			questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."d] ");
		elseif(qData.qTag == RAID) then
			questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."r] ");
		elseif(qData.qTag == QST_PVP) then
			questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."p] ");
		else
			questInfo = questInfo..format(cText, 255, R, G, B, "["..qData.qLevel.."] ");
		end
		questInfo = questInfo..format(QST_TITLECOLOUR.."%s|r", qData.qTitle).."\n";
		if(qData.qItems) then
			for x, value in qData.qItems, 1 do
				questInfo = questInfo.."    "..QST_GetColourString(value)..value.."\n";
			end
		else
			questInfo = questInfo.."    "..format(QST_OVERVIEWCOLOUR.."%s|r", qData.qObj).."\n";
		end
		if(qData.qIndex) then
			for index, name in QST_FilterMenu do
				if(qData.qPlayer[name[1]]) then
					getglobal("QSTButton"..buttonID.."Status"):SetText(qData.qPlayer[name[1]].qStatus);
				end
			end
		else
			getglobal("QSTButton"..buttonID.."Status"):SetText(QST_QUEST_HISTORY);
		end
		if(strlen(qData.qNote) > 0) then
			getglobal("QSTButton"..buttonID.."Noted"):SetText(QST_HEADERCOLOUR..QST_NOTE_FLAG);
		end
		button.qDB = qList.qDB;
		button.qIndex = qList.qIndex;
		buttontext:SetText(questInfo);
		buttontext:SetWidth(QST_BUTTONWIDTH);
		button:SetHeight(buttontext:GetHeight() + QST_Options.Padding);
		button:SetParent("QST_ScrollChild");
		buttontext:Show();
		button:Show();
		questInfo = "";
		buttonID = buttonID + 1;
		ScrollHeight = ScrollHeight + button:GetHeight();
	end
	for i=buttonID, QST_ButtonTotal, 1 do
		getglobal("QSTButton"..i):Hide()
	end
	QST_ScrollChild:SetHeight(ScrollHeight);
	QST_ScrollFrame:UpdateScrollChildRect()
end

function QST_FilterMenuInit()
	for index, menuItem in QST_FilterMenu do
		local check = nil;
		local spacer = nil;
		if(menuItem[3]) then spacer = 1; end
		if(menuItem[2] == "player") then
			if(QST_Options.ShowPlayers) then
				check = 1;
			elseif(menuItem[1] == QST_SelectedPlayer) then
				check = 1;
			end
		elseif(QST_Options.SortOrder == string.gsub(menuItem[2], "SortOrder", "")) then
			check = 1;
		elseif(QST_Options[menuItem[2]]) then
			check = 1;
		end
		local info = {};
		info.isTitle = spacer;
		info.notClickable = spacer;
		info.checked = check;
		info.text = menuItem[1];
		info.value = menuItem[2];
		info.func = QST_FilterMenuOnClick;
		UIDropDownMenu_AddButton(info);
	end
end

function QST_FilterMenuOnClick()
	if(this.value == "player") then
		QST_SelectedPlayer = this:GetText();
		QST_Options.ShowPlayers = false;
	elseif(string.find(this.value, "SortOrder")) then
		QST_Options.SortOrder = string.gsub(this.value, "SortOrder", "");
		QST_SortQuestList(QST_Options.SortOrder);
		QST_ShowDisplay();
		return;
	else
		QST_ToggleOptions(this.value);
	end
  UIDropDownMenu_SetText(this:GetText(), QST_FilterSelect);
	QST_RefreshDisplay();
end

function QSTButton_OnEnter()
	local qData = getglobal(this.qDB)[this.qIndex];
	if(not qData or QST_RewardFrame:IsVisible() or QST_EditorFrame:IsVisible()) then return; end
	WorldMapTooltip:SetOwner(QST_DisplayFrame, "ANCHOR_BOTTOMRIGHT", 0, QST_DisplayFrame:GetHeight()* QST_DisplayFrame:GetEffectiveScale());
	if(IsControlKeyDown()) then
		WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
		WorldMapTooltip:AddLine(qData.qNote, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
	elseif(IsShiftKeyDown()) then
		WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
		if(qData.qNPC[1]) then
			WorldMapTooltip:AddDoubleLine("QuestStart:", QST_COMPLETEDCOLOUR..qData.qNPC[1].qName, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
		end
		if(qData.qNPC[2]) then
			WorldMapTooltip:AddDoubleLine("QuestEnd:", QST_COMPLETEDCOLOUR..qData.qNPC[2].qName, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
		end
		WorldMapTooltip:AddLine(" ", 0, 0, 0, 0);
		for name, value in qData.qPlayer do
			WorldMapTooltip:AddDoubleLine(name, value.qStatus, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
		end
	else
		WorldMapTooltip:SetText(qData.qTitle, 1, 1, 1, 1);
		WorldMapTooltip:AddLine(qData.qObj, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
		WorldMapTooltip:AddLine(" ", 0, 0, 0, 0);
		if(qData.qDesc) then
			WorldMapTooltip:AddLine(qData.qDesc, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1);
		end
		WorldMapTooltip:AddLine(qData.qTag, 1, 0, 1, 1);
		if(qData.qIndex) then
			SelectQuestLogEntry(qData.qIndex);
			for i = 1, GetNumPartyMembers() do
				if(IsUnitOnQuest(qData.qIndex, "party"..i)) then
					WorldMapTooltip:AddLine(QST_COMPLETEDCOLOUR..UnitName("party"..i));
				else
					WorldMapTooltip:AddLine(QST_INITIALCOLOUR..UnitName("party"..i));
				end
			end
		end
	end
	WorldMapTooltip:SetPoint("TOPRIGHT", QST_DisplayFrame, "TOPRIGHT", 0, 0);
	WorldMapTooltip:Show();
end

function QSTButton_OnClick(button)
	local qData = getglobal(this.qDB)[this.qIndex];
	QST_EditorFrame:Hide()
	if(this.qMap) then
		local continent, zone = MetaMap_NameToZoneID(this.qMap, 1);
		if(continent == 0 and zone > 0) then
			MetaMap_CurrentMap = zone;
			MetaMap_Toggle(true);
		else
			SetMapZoom(continent, zone);
		end
		return;
	end
	if(IsShiftKeyDown() and ChatFrameEditBox:IsVisible()) then
		if(button == "LeftButton") then
			if(qData.qTag == ELITE) then
				ChatFrameEditBox:Insert("["..qData.qLevel.."+] "..qData.qTitle.." ");
			elseif(qData.qTag == QST_DUNGEON) then
				ChatFrameEditBox:Insert("["..qData.qLevel.."d] "..qData.qTitle.." ");
			elseif(qData.qTag == RAID) then
				ChatFrameEditBox:Insert("["..qData.qLevel.."r] "..qData.qTitle.." ");
			elseif(qData.qTag == QST_PVP) then
				ChatFrameEditBox:Insert("["..qData.qLevel.."p] "..qData.qTitle.." ");
			else
				ChatFrameEditBox:Insert("["..qData.qLevel.."] "..qData.qTitle.." ");
			end
		else
			local ChatItems = "";
			if(qData.qItems) then
				for i, value in qData.qItems, 1 do
					ChatItems = ChatItems.."{"..qData.qItems[i].."} ";
				end
			else
				ChatItems = ChatItems.."{"..qData.qObj.."} ";
			end
			ChatFrameEditBox:Insert(ChatItems);
		end
		return;
	elseif(IsControlKeyDown() and qData.qIndex) then
		if(button == "LeftButton") then
			SelectQuestLogEntry(qData.qIndex);
			if(GetQuestLogPushable() and GetNumPartyMembers() > 0) then
				QuestLogPushQuest();
			end
		else
			SelectQuestLogEntry(qData.qIndex);
			SetAbandonQuest();
			StaticPopup_Show("ABANDON_QUEST", GetAbandonQuestName());
			QST_AbandonQuest = GetAbandonQuestName();
		end
		return;
	else
		if(button == "LeftButton") then
			QST_RewardFrameShow(this.qDB, this.qIndex);
		else
			QST_QuestNoteShow(this.qDB, this.qIndex);
		end
	end
end

function QST_RewardFrameShow(qDB, qIndex)
	local qData = getglobal(qDB)[qIndex];
	local height = 100;
	local rewardCount, choiceCount, spellCount = 0,0,0;
	local receiveText = getglobal("QST_RewardItemReceiveText");
	local chooseText = getglobal("QST_RewardItemChooseText");
	
	if(qData.qChoice) then
		chooseText:Show();
		height = height + 14
		for i, choice in qData.qChoice do	
			local _, _, itemLink, itemName = string.find(choice.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
			rewardItem = getglobal("QST_RewardItem"..i);
			rewardItem.type = "choice";
			rewardItem.rewardType = "item";
			rewardItem.link = choice.qLink;
			rewardItem.itemID = itemLink;
			rewardItem:Show();
			SetItemButtonCount(rewardItem, choice.qAmount);
			SetItemButtonTexture(rewardItem, choice.qTex);
			if(qData.qIndex and qData.qPlayer[QST_ActivePlayer]) then
				SelectQuestLogEntry(qData.qIndex);
				_, _, _, _, isUsable = GetQuestLogChoiceInfo(i);
				if(isUsable) then
					SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
				else
					SetItemButtonTextureVertexColor(rewardItem, 0.9, 0, 0);
				end
			else
				SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
			end
			if(i > 1) then
				if(mod(i,2) == 1) then
					rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..(i - 2), "BOTTOMLEFT", 0, -2);
					height = height + 40
				else
					rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..(i - 1), "TOPRIGHT", 20, 0);
				end
			else
				rewardItem:SetPoint("TOPLEFT", "QST_RewardItemChooseText", "BOTTOMLEFT", 20, -5);
				height = height + 40
			end
			choiceCount = i;
		end
	else
		chooseText:Hide();
	end
	if(qData.qReward or qData.qSpell or (qData.qMoney and qData.qMoney > 0)) then
		height = height + 16;
		receiveText:Show();
		if(qData.qChoice) then
			local index = choiceCount;
			if(mod(index, 2) == 0 ) then
				index = index - 1;
			end
			receiveText:SetText(TEXT(REWARD_ITEMS));
			receiveText:SetPoint("TOPLEFT", "QST_RewardItem"..index, "BOTTOMLEFT", -20, -5);
		else 
			receiveText:SetText(TEXT(REWARD_ITEMS_ONLY));
			receiveText:SetPoint("TOPLEFT", "QST_RewardFrame", "TOPLEFT", 20, -45);
		end
		if(qData.qReward) then
			getglobal("QST_RewardItemReceiveText"):Show();
			for i, reward in qData.qReward do
				local _, _, itemLink, itemName = string.find(reward.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
				rewardItem = getglobal("QST_RewardItem"..(i + choiceCount));
				rewardItem.type = "reward";
				rewardItem.rewardType = "item";
				rewardItem.link = reward.qLink;
				rewardItem.itemID = itemLink;
				rewardItem:Show();
				SetItemButtonCount(rewardItem, reward.qAmount);
				SetItemButtonTexture(rewardItem, reward.qTex);
				if(qData.qIndex and qData.qPlayer[QST_ActivePlayer]) then
					SelectQuestLogEntry(qData.qIndex);
					_, _, _, _, isUsable = GetQuestLogRewardInfo(i);
					if(isUsable) then
						SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
					else
						SetItemButtonTextureVertexColor(rewardItem, 0.9, 0, 0);
					end
				else
					SetItemButtonTextureVertexColor(rewardItem, 1.0, 1.0, 1.0);
				end
				if(i > 1) then
					if(mod(i,2) == 1) then
						rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((i + choiceCount) - 2), "BOTTOMLEFT", 0, -2);
						height = height + 40
					else
						rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((i + choiceCount) - 1), "TOPRIGHT", 20, 0);
					end
				else
					rewardItem:SetPoint("TOPLEFT", "QST_RewardItemReceiveText", "BOTTOMLEFT", 20, -5);
					height = height + 40
				end
				rewardCount = i;
			end
		end
		if(qData.qSpell) then
			local _, _, itemLink, itemName = string.find(qData.qSpell.qLink, "|H(item:%d+:%d+:%d+:%d+)|h%[([^]]+)%]|h|r$");
			rewardItem = getglobal("QST_RewardItem"..(rewardCount + choiceCount + 1));
			rewardItem.rewardType = "spell";
			rewardItem.name = itemName;
			rewardItem.itemID = itemLink;
			rewardItem:Show();
			SetItemButtonTexture(rewardItem, qData.qSpell.qTex);
			if(rewardsCount > 0) then
				if(mod(rewardCount,2) == 0) then
					rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((rewardCount + choiceCount) - 1), "BOTTOMLEFT", 0, -2);
					height = height + 40
				else
					rewardItem:SetPoint("TOPLEFT", "QST_RewardItem"..((rewardCount + choiceCount)), "TOPRIGHT", 20, 0);
				end
			else
				rewardItem:SetPoint("TOPLEFT", "QST_RewardItemReceiveText", "BOTTOMLEFT", 20, -5);
				height = height + 40
			end
			spellCount = 1;
		end
	else	
		receiveText:Hide();
	end
	local totalRewards = rewardCount + choiceCount + spellCount;
	for i=totalRewards + 1, 10, 1 do
		getglobal("QST_RewardItem"..i):Hide();
	end
	if(totalRewards > 0 or (qData.qMoney and qData.qMoney > 0)) then
		MoneyFrame_Update("QST_RewardMoneyFrame", qData.qMoney);
		local x, y = GetCursorPosition();
		x = x / UIParent:GetEffectiveScale();
		y = y / UIParent:GetEffectiveScale();
		QST_RewardFrame:SetPoint("LEFT", "UIParent", "BOTTOMLEFT", x-20, y);
		QST_RewardFrame:SetHeight(height);
		QST_RewardFrame:Show();
	end
end

function QST_RewardItemOnClick()
	if(IsControlKeyDown()) then
		if(this.rewardType ~= "spell") then
			MetaMap_ToggleDR(1);
			DressUpItemLink(this.itemID);
			DressUpFrame:Show();
			DressUpItemLink(this.itemID);
		end
	elseif(IsShiftKeyDown()) then
		if(ChatFrameEditBox:IsVisible()) then
			ChatFrameEditBox:Insert(this.link);
		end
	end
end

function QST_QuestNoteShow(qDB, qIndex)
	local qData = getglobal(qDB)[qIndex];
	QST_SaveNote.qDB = qDB;
	QST_SaveNote.qIndex = qIndex;
	QST_NotesEditBox:SetText(qData.qNote);
	QST_EditorFrame:Show();
	QST_NotesEditBox:SetFocus();
end

function QST_QuestNoteUpdate()
	local qData = getglobal(this.qDB)[this.qIndex];
	qData.qNote = QST_NotesEditBox:GetText();
	QST_EditorFrame:Hide();
	QST_ShowDisplay();
end

function QST_QuestAcceptOnClick()
	local x, y = GetPlayerMapPosition("player");
	local questStart = UnitName("target");
	if(questStart == nil) then questStart = "Quest Item"; end
	QST_AcceptQuest = {};
	QST_AcceptQuest.Title = QST_QuestDetail;
	QST_AcceptQuest.NPC = questStart;
	QST_AcceptQuest.zone = GetRealZoneText();
	QST_AcceptQuest.xPos = x;
	QST_AcceptQuest.yPos = y;
	QST_QuestDetail = nil;
	Orig_QuestAcceptOnClick();
end

function QST_QuestCompleteOnClick()
	local qIndex, qLog;
	local x, y = GetPlayerMapPosition("player");
	local questStart = UnitName("target");
	if(questStart == nil) then questStart = "Unknown"; end
	for index, qQuests in QST_QuestLog do
		if(qQuests.qTitle == GetTitleText() and qQuests.qPlayer[QST_ActivePlayer]) then
			qIndex = index; qLog = qQuests; break;
		end
	end
	if(qIndex) then
		if(QST_Options.SaveHistory) then
			qLog.qIndex = nil;
			qLog.qPlayer[QST_ActivePlayer].qStatus = QST_COMPLETEDCOLOUR..QST_QUEST_DONE;
			qLog.qNPC[2] = {};
			qLog.qNPC[2]["qName"] = questStart;
			qLog.qNPC[2]["qZone"] = GetRealZoneText;
			qLog.qNPC[2]["qX"] = x;
			qLog.qNPC[2]["qY"] = y;
			for index, qQuests in QST_QuestBase do
				if(qLog.qTitle == qQuests.qTitle and qLog.qObj == qQuests.qObj) then
					qBase = qQuests; break;
				end
			end
			if(qBase) then
				qBase.qNote = qBase.qNote.."\n\n"..qLog.qNote;
				qBase.qPlayer[QST_ActivePlayer] = {};
				qBase.qPlayer[QST_ActivePlayer] = qLog.qPlayer[QST_ActivePlayer];
				qBase.qNPC[2] = qLog.qNPC[2];
			else
				if(not QST_Options.SaveDesc) then
					qLog.qDesc = nil;
				end
				if(not QST_Options.SaveRew) then
					qLog.qReward = nil; qLog.qChoice = nil; qLog.qSpell = nil;
				end
				QST_QuestBase[table.getn(QST_QuestBase)+1] = qLog;
			end
		end
		QST_QuestLog[qIndex] = nil;
	end
	Orig_RewardCompleteOnClick();
end

function QST_AbandonQuestOnClick()
	local qIndex, qLog;
	for index, qQuests in QST_QuestLog do
		if(qQuests.qTitle == QST_AbandonQuest and qQuests.qPlayer[QST_ActivePlayer]) then
			qIndex = index; qLog = qQuests; break;
		end
	end
	if(qIndex) then
		if(QST_Options.SaveHistory) then
			qLog.qIndex = nil;
			qLog.qPlayer[QST_ActivePlayer].qStatus = QST_INITIALCOLOUR..QST_QUEST_ABANDON;
			for index, qQuests in QST_QuestBase do
				if(qLog.qTitle == qQuests.qTitle and qLog.qObj == qQuests.qObj) then
					qBase = qQuests; break;
				end
			end
			if(qBase) then
				qBase.qNote = qBase.qNote.."\n\n"..qLog.qNote;
				qBase.qPlayer[QST_ActivePlayer] = {};
				qBase.qPlayer[QST_ActivePlayer] = qLog.qPlayer[QST_ActivePlayer];
			else
				if(not QST_Options.SaveDesc) then
					qLog.qDesc = nil;
				end
				if(not QST_Options.SaveRew) then
					qLog.qReward = nil; qLog.qChoice = nil; qLog.qSpell = nil;
				end
				QST_QuestBase[table.getn(QST_QuestBase)+1] = qLog;
			end
		end
		QST_QuestLog[qIndex] = nil;
	end
	Orig_AbandonQuest();
end

function QST_afTooltip(unit)
	QST_Orig_afTooltip(unit);
	QST_EnhanceTooltip();
end

function QST_ContainerButtonOnEnter()
	QST_Orig_ContainerButtonOnEnter();
	QST_EnhanceTooltip();
end

function QST_SanityButtonOnEnter()
	QST_OrigSanityButtonOnEnter();
	QST_EnhanceTooltip();
end

function QST_EnhanceTooltip()
	if(GameTooltip == nil) then return; end
	if(not QST_ItemList) then return; end
	if(getglobal('GameTooltipTextLeft1'):GetText() ~= nil) then
		local itemName = string.gsub(getglobal('GameTooltipTextLeft1'):GetText(),"|c........(.*)|?r?","%1");
		if(QST_ItemList[itemName] == nil) then return; end
		if(getglobal('GameTooltipTextLeft2'):GetText() ~= ITEM_BIND_QUEST) then
			GameTooltip:AddLine(ITEM_BIND_QUEST, 1, 1, 1, 1);
			GameTooltip:SetHeight(GameTooltip:GetHeight() + 14);
		end
		GameTooltip:AddLine(QST_ItemList[itemName].qTitle..": "..QST_ItemList[itemName].qItems, 1, 0, 1, 1);
		length = getglobal(GameTooltip:GetName() .. "TextLeft" .. GameTooltip:NumLines()):GetStringWidth();
		length = length + 22;
		GameTooltip:SetHeight(GameTooltip:GetHeight() + 14);
		if(length > GameTooltip:GetWidth()) then
			GameTooltip:SetWidth(length);
		end
	end
end

function QST_HintTooltip()
	WorldMapTooltip:SetOwner(this, "ANCHOR_TOPLEFT");
	WorldMapTooltip:SetText(QST_TTHINT_H0, 0.2, 0.5, 1, 1);
	WorldMapTooltip:AddLine(QST_TTHINT_H1, 0, 1, 0, 1);
	WorldMapTooltip:AddDoubleLine("Shift-onEnter", QST_TTHINT_T0, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Ctrl-onEnter", QST_TTHINT_T1, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Left-Click", QST_TTHINT_T2, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Right-Click", QST_TTHINT_T3, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Shift-Left", QST_TTHINT_T4, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Shift-Right", QST_TTHINT_T5, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Ctrl-Left", QST_TTHINT_T6, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Ctrl-Right", QST_TTHINT_T7, 1, 1, 1, 1);
	WorldMapTooltip:AddLine(QST_TTHINT_H2, 0, 1, 0, 1);
	WorldMapTooltip:AddDoubleLine("Shift-Left", QST_TTHINT_T8, 1, 1, 1, 1);
	WorldMapTooltip:AddDoubleLine("Ctrl-Left", QST_TTHINT_T9, 1, 1, 1, 1);
	WorldMapTooltip:Show();
end

function QST_ToggleOptions(key, value)
	if(value) then
		QST_Options[key] = value;
	else
		QST_Options[key] = not QST_Options[key];
	end
	return QST_Options[key];
end

function QST_CreateButton(id)
	local button;
	if(getglobal("QSTButton"..id)) then
		button = getglobal("QSTButton"..id);
		button.qMap = nil;
		button.qTitle = nil;
		button.qData = nil;
		getglobal("QSTButton"..id.."Text"):SetText("");
		getglobal("QSTButton"..id.."Status"):SetText("");
		getglobal("QSTButton"..id.."Noted"):SetText("");
	else
		button = CreateFrame("Button" ,"QSTButton"..id, QST_ScrollChild, "QST_ButtonTemplate");
		button:SetWidth(QST_ScrollChild:GetWidth());
		button:SetID(id);
		QST_ButtonTotal = QST_ButtonTotal +1;
	end
	return button;
end

function QST_GetLandMark(zoneText)
	for landmarkIndex = 1, GetNumMapLandmarks(), 1 do
		local name = GetMapLandmarkInfo(landmarkIndex);
		if(strlower(name) == strlower(zoneText)) then return true; end
	end
	return false;
end

function QST_GetColourString(Text)
	local i, j, ItemName, NumItems, NumNeeded = string.find(Text, "(.*):%s*([-%d]+)%s*/%s*([-%d]+)%s*$");
	local colour = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
	local colourInitial = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
	local colourMid = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
	local colourComplete = {a = 1.0, r = 1.0, g = 1.0, b = 1.0};
	colourInitial.r, colourInitial.g, colourInitial.b, colourInitial.a = QST_TextToRGB(QST_INITIALCOLOUR);
	colourMid.r, colourMid.g, colourMid.b, colourMid.a = QST_TextToRGB(QST_MIDDLECOLOUR);
	colourComplete.r, colourComplete.g, colourComplete.b, colourComplete.a = QST_TextToRGB(QST_COMPLETEDCOLOUR);

	local colourDelta1 = {
		a = (colourMid.a - colourInitial.a),
		r = (colourMid.r - colourInitial.r),
		g = (colourMid.g - colourInitial.g),
		b = (colourMid.b - colourInitial.b)
	};
	local colourDelta2 = {
		a = (colourComplete.a - colourMid.a),
		r = (colourComplete.r - colourMid.r),
		g = (colourComplete.g - colourMid.g),
		b = (colourComplete.b - colourMid.b)
	};
	if (NumItems ~= nil) then
		if ((NumItems / NumNeeded) < 0.5) then
			colour.r = colourInitial.r + ((NumItems / (NumNeeded / 2)) * colourDelta1.r);
			colour.g = colourInitial.g + ((NumItems / (NumNeeded / 2)) * colourDelta1.g);
			colour.b = colourInitial.b + ((NumItems / (NumNeeded / 2)) * colourDelta1.b);
		else
			colour.r = colourMid.r + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.r);
			colour.g = colourMid.g + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.g);
			colour.b = colourMid.b + (((NumItems - (NumNeeded / 2)) / (NumNeeded / 2)) * colourDelta2.b);
		end
	else
		i, j, ItemName, NumItems, NumNeeded = string.find(Text, "(.*):%s*([-%a]+)%s*/%s*([-%a]+)%s*$");
		if (Items ~= nil) then
			if (NumItems == NumNeeded) then
				return QST_COMPLETEDCOLOUR;
			else
				return QST_INITIALCOLOUR;
			end
		else
			return QST_SPECIALCOLOUR;
		end
	end

	if(colour.r > 1.0) then colour.r = 1.0; end
	if(colour.g > 1.0) then colour.g = 1.0; end
	if(colour.b > 1.0) then colour.b = 1.0; end
	if(colour.r < 0.0) then colour.r = 0.0; end
	if(colour.g < 0.0) then colour.g = 0.0; end
	if(colour.b < 0.0) then colour.b = 0.0; end

	return QST_RGBtoText(colour.r, colour.g, colour.b, colour.a);
end

function QST_TextToRGB(strColour)
	local i = 3;
	local iAlpha = tonumber(string.sub(strColour, i, i + 1), 16);
	local iRed = tonumber(string.sub(strColour, i + 2, i + 3), 16);
	local iGreen = tonumber(string.sub(strColour, i + 4, i + 5), 16);
	local iBlue = tonumber(string.sub(strColour, i + 6, i + 7), 16);
	iAlpha = iAlpha / 255;
	iRed = iRed / 255;
	iGreen = iGreen / 255;
	iBlue = iBlue / 255;
	return iRed, iGreen, iBlue, iAlpha;
end

function QST_RGBtoText(iRed, iGreen, iBlue, iAlpha)
	local strColour;
	iAlpha = floor(iAlpha * 255);
	iRed = floor(iRed * 255);
	iGreen = floor(iGreen * 255);
	iBlue = floor(iBlue * 255);
	strColour = format("|c%2x%2x%2x%2x", iAlpha, iRed, iGreen, iBlue);
	return strColour;
end
