--[[
	Quest Keeper

	By sarf & Lash

	This mod allows you to find quests that are appropriate to your level and in the current zone.

	Thanks goes to Thott, for building the kick-ass database that we all know, love and abuse!
	
	Credits to vjeux for his nice list of zone names in CartoPlus - we copied it. Hope you don't mind!
	
	CosmosUI URL:
	http://www.cosmosui.org/forums/viewtopic.php?t=
	
   ]]

-- Constants

-- how many levels below you a quest can be before it gets removed
QUESTKEEPER_TRIM_QUESTS_LEVEL_MIN = -5;

-- how many levels higher than your current levle a quest can be before it gets removed
QUESTKEEPER_TRIM_QUESTS_LEVEL_MAX = 10;

-- if set to false, will not trim quests
QUESTKEEPER_SHOULD_TRIM = true;

-- Variables
QuestKeeper_Enabled = 0;
QuestKeeper_Appropriate_Quests_Level_Min = -5;
QuestKeeper_Appropriate_Quests_Level_Max = 5;
QuestKeeper_CompletedQuests = {};

QuestKeeper_OverridePlayerLevel = false;
QuestKeeper_RelativeLevel = 0;

QuestKeeper_Saved_Hooked_Function = nil;
QuestKeeper_Cosmos_Registered = 0;

-- executed on load, calls general set-up functions
function QuestKeeper_OnLoad()
	QuestKeeper_Register();
	if ( QUESTKEEPER_SHOULD_TRIM ) then
		if ( Cosmos_AfterInit ) then
			Cosmos_AfterInit(Cosmos_Schedule, 10, QuestKeeper_TrimDatabase);
		else
			QuestKeeper_TrimDatabase();
		end
	end
end

-- registers the mod with Cosmos
function QuestKeeper_Register_Cosmos()
	if ( ( Cosmos_RegisterConfiguration ) and ( QuestKeeper_Cosmos_Registered == 0 ) ) then
		Cosmos_RegisterConfiguration(
			"COS_QUESTKEEPER",
			"SECTION",
			TEXT(QUESTKEEPER_CONFIG_HEADER),
			TEXT(QUESTKEEPER_CONFIG_HEADER_INFO)
		);
		Cosmos_RegisterConfiguration(
			"COS_QUESTKEEPER_HEADER",
			"SEPARATOR",
			TEXT(QUESTKEEPER_CONFIG_HEADER),
			TEXT(QUESTKEEPER_CONFIG_HEADER_INFO)
		);
		Cosmos_RegisterConfiguration(
			"COS_QUESTKEEPER_ENABLED",
			"CHECKBOX",
			TEXT(QUESTKEEPER_ENABLED),
			TEXT(QUESTKEEPER_ENABLED_INFO),
			QuestKeeper_Toggle_Enabled_NoChat,
			QuestKeeper_Enabled
		);
		Cosmos_RegisterConfiguration(
			"COS_QUESTKEEPER_QUEST_LEVEL_MIN", -- CVar
			"SLIDER",
			QUESTKEEPER_QUEST_LEVEL_MIN,
			QUESTKEEPER_QUEST_LEVEL_MIN_INFO,
			QuestKeeper_QuestLevelMin,
			1,
			QuestKeeper_Appropriate_Quests_Level_Min,		--Default Value
			QUESTKEEPER_TRIM_QUESTS_LEVEL_MIN,				--Min value
			QUESTKEEPER_TRIM_QUESTS_LEVEL_MAX,				--Max value
			QUESTKEEPER_QUEST_LEVEL_MIN_TEXT,		--Slider Text
			1,				--Slider Increment
			1,				--Slider state text on/off
			"",				--Slider state text append
			1				--Slider state text multiplier
		);
		Cosmos_RegisterConfiguration(
			"COS_QUESTKEEPER_QUEST_LEVEL_MAX", -- CVar
			"SLIDER",
			QUESTKEEPER_QUEST_LEVEL_MAX,
			QUESTKEEPER_QUEST_LEVEL_MAX_INFO,
			QuestKeeper_QuestLevelMax,
			1,
			QuestKeeper_Appropriate_Quests_Level_Max,		--Default Value
			QUESTKEEPER_TRIM_QUESTS_LEVEL_MIN,				--Min value
			QUESTKEEPER_TRIM_QUESTS_LEVEL_MAX,				--Max value
			QUESTKEEPER_QUEST_LEVEL_MAX_TEXT,		--Slider Text
			1,				--Slider Increment
			1,				--Slider state text on/off
			"",				--Slider state text append
			1				--Slider state text multiplier
		);
		if ( Cosmos_RegisterChatCommand ) then
			local QuestKeeperMainCommands = {"/questkeeper","/qk"};
			Cosmos_RegisterChatCommand (
				"QUESTKEEPER_MAIN_COMMANDS", -- Some Unique Group ID
				QuestKeeperMainCommands, -- The Commands
				QuestKeeper_Main_ChatCommandHandler,
				QUESTKEEPER_CHAT_COMMAND_INFO -- Description String
			);
		end
		QuestKeeper_Cosmos_Registered = 1;
	end
end

-- registers the mod with the system, integrating it with slash commands and "master" AddOns
function QuestKeeper_Register()
	if ( Cosmos_RegisterConfiguration ) then
		QuestKeeper_Register_Cosmos();
	else
		SlashCmdList["QUESTKEEPERSLASHMAIN"] = QuestKeeper_Main_ChatCommandHandler;
		SLASH_QUESTKEEPERSLASHMAIN1 = "/questkeeper";
		SLASH_QUESTKEEPERSLASHMAIN2 = "/qk";
		this:RegisterEvent("VARIABLES_LOADED");
	end
end

function QuestKeeper_Extract_NextParameter(msg)
	local params = msg;
	local command = params;
	local index = strfind(command, " ");
	if ( index ) then
		command = strsub(command, 1, index-1);
		params = strsub(params, index+1);
	else
		params = "";
	end
	return command, params;
end

-- Handles chat - e.g. slashcommands - enabling/disabling the QuestKeeper
function QuestKeeper_Main_ChatCommandHandler(msg)
	
	local func = QuestKeeper_Toggle_Enabled;
	
	local toggleFunc = true;
	
	if ( ( not msg) or ( strlen(msg) <= 0 ) ) then
		QuestKeeper_Print(QUESTKEEPER_CHAT_COMMAND_USAGE);
		return;
	end
	
	local commandName, params = QuestKeeper_Extract_NextParameter(msg);
	
	if ( ( commandName ) and ( strlen(commandName) > 0 ) ) then
		commandName = string.lower(commandName);
	else
		commandName = "";
	end
	
	if ( strfind( commandName, "state" ) ) then
		func = QuestKeeper_Toggle_Enabled;
	else
		QuestKeeper_Print(QUESTKEEPER_CHAT_COMMAND_USAGE);
		return;
	end
	
	if ( toggleFunc ) then
		-- Toggle appropriately
		if ( (string.find(params, 'on')) or ((string.find(params, '1')) and (not string.find(params, '-1')) ) ) then
			func(1);
		else
			if ( (string.find(params, 'off')) or (string.find(params, '0')) ) then
				func(0);
			else
				func(-1);
			end
		end
	else
		func();
	end
end

-- Does things with the hooked function
function QuestKeeper_Hooked_Function()
	if ( QuestKeeper_Enabled == 1 ) then
	end
	QuestKeeper_Saved_Hooked_Function();
end

-- Hooks/unhooks functions. If toggle is 1, hooks functions, otherwise it unhooks functions.
--  Hooking functions mean that you replace them with your own functions and then call the 
--  original function at your leisure.
function QuestKeeper_Setup_Hooks(toggle)
	if ( toggle == 1 ) then
		if ( ( Hooked_Function ~= QuestKeeper_Hooked_Function ) and (QuestKeeper_Saved_Hooked_Function == nil) ) then
			QuestKeeper_Saved_Hooked_Function = Hooked_Function;
			Hooked_Function = QuestKeeper_Hooked_Function;
		end
	else
		if ( Hooked_Function == QuestKeeper_Hooked_Function) then
			Hooked_Function = QuestKeeper_Saved_Hooked_Function;
			QuestKeeper_Saved_Hooked_Function = nil;
		end
	end
end

-- Handles events
function QuestKeeper_OnEvent(event)
	if ( event == "VARIABLES_LOADED" ) then
		if ( QuestKeeper_Cosmos_Registered == 0 ) then
			local value = getglobal("COS_QUESTKEEPER_ENABLED_X");
			if (value == nil ) then
				-- defaults to off
				value = 0;
			end
			QuestKeeper_Toggle_Enabled(value);
		end
	end
end

-- Toggles the enabled/disabled state of an option and returns the new state
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function QuestKeeper_Generic_Toggle(toggle, variableName, CVarName, enableMessage, disableMessage, CosmosVarName)
	local oldvalue = getglobal(variableName);
	local newvalue = toggle;
	if ( ( toggle ~= 1 ) and ( toggle ~= 0 ) ) then
		if (oldvalue == 1) then
			newvalue = 0;
		elseif ( oldvalue == 0 ) then
			newvalue = 1;
		else
			newvalue = 0;
		end
	end
	setglobal(variableName, newvalue);
	setglobal(CVarName, newvalue);
	if ( newvalue ~= oldvalue ) then
		local text = "";
		if ( newvalue == 1 ) then
			if ( enableMessage ) then
				text = TEXT(getglobal(enableMessage));
			end
		else
			if ( disableMessage ) then
				text = TEXT(getglobal(disableMessage));
			end
		end
		if ( text ) and ( strlen(text) > 0 ) then
			QuestKeeper_Print(text);
		end
	end
	QuestKeeper_Register_Cosmos();
	if ( QuestKeeper_Cosmos_Registered == 0 ) then 
		RegisterForSave(variableName);
	else
		QuestKeeper_Generic_CosmosUpdateCheckOnOff(CVarName, newvalue);
		QuestKeeper_Generic_CosmosUpdateCheckOnOff(CosmosVarName, newvalue);
	end
	return newvalue;
end

function QuestKeeper_Generic_CosmosUpdateCheckOnOff(varName, value)
	if ( not Cosmos_UpdateValue ) then
		return;
	end
	local name = varName;
	if ( ( not name ) or ( strlen(name) <= 0 ) ) then
		return
	end
	if ( strfind(name, "_X" ) ) then
		name = strsub(name, 1, strlen(name)-2);
	end
	if ( ( name ) and ( strlen(name) > 0 ) ) then
		Cosmos_UpdateValue(name, CSM_CHECKONOFF, value);
	end
end

function QuestKeeper_Generic_CosmosUpdateValue(varName, value)
	if ( not Cosmos_UpdateValue ) then
		return;
	end
	local name = varName;
	if ( ( not name ) or ( strlen(name) <= 0 ) ) then
		return
	end
	if ( strfind(name, "_X" ) ) then
		name = strsub(name, 1, strlen(name)-2);
	end
	if ( ( name ) and ( strlen(name) > 0 ) ) then
		Cosmos_UpdateValue(name, CSM_SLIDERVALUE, value);
	end
end



-- Toggles the enabled/disabled state of the QuestKeeper
--  if toggle is 1, it's enabled
--  if toggle is 0, it's disabled
--   otherwise, it's toggled
function QuestKeeper_Toggle_Enabled(toggle)
	QuestKeeper_DoToggle_Enabled(toggle, true);
end

-- does the actual toggling
function QuestKeeper_DoToggle_Enabled(toggle, showText)
	local newvalue = 0;
	if ( showText ) then
		newvalue = QuestKeeper_Generic_Toggle(toggle, "QuestKeeper_Enabled", "COS_QUESTKEEPER_ENABLED_X", "QUESTKEEPER_CHAT_ENABLED", "QUESTKEEPER_CHAT_DISABLED");
	else
		newvalue = QuestKeeper_Generic_Toggle(toggle, "QuestKeeper_Enabled", "COS_QUESTKEEPER_ENABLED_X");
	end
	QuestKeeper_Setup_Hooks(newvalue);
end

-- toggling - no text
function QuestKeeper_Toggle_Enabled_NoChat(toggle)
	QuestKeeper_DoToggle_Enabled(toggle, false);
end

-- Prints out text to a chat box.
function QuestKeeper_Print(msg,r,g,b,frame,id,unknown4th)
	if ( Print ) then
		Print(msg, r, g, b, frame, id, unknown4th);
	end
	if(unknown4th) then
		local temp = id;
		id = unknown4th;
		unknown4th = id;
	end
				
	if (not r) then r = 1.0; end
	if (not g) then g = 1.0; end
	if (not b) then b = 1.0; end
	if ( frame ) then 
		frame:AddMessage(msg,r,g,b,id,unknown4th);
	else
		if ( DEFAULT_CHAT_FRAME ) then 
			DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b,id,unknown4th);
		end
	end
end

function QuestKeeper_GetRelativeLevel()
	if ( QuestKeeper_OverridePlayerLevel ) then
		return QuestKeeper_RelativeLevel;
	else
		return UnitLevel("player");
	end
end

function QuestKeeper_SetRelativeLevel(level)
	QuestKeeper_OverridePlayerLevel = true;
	QuestKeeper_RelativeLevel = level;
end

function QuestKeeper_DisableRelativeLevel()
	QuestKeeper_OverridePlayerLevel = false;
end

function QuestKeeper_RaceAsNumber(race)
	for k, v in QuestKeeper_Race do
		if ( v == race) then
			return k;
		end
	end
	return 0;
end

function QuestKeeper_ClassAsNumber(class)
	for k, v in QuestKeeper_Class do
		if ( v == class) then
			return k;
		end
	end
	return 0;
end

function QuestKeeper_GetSkillNumberAsName(skillNumber)
	for k, v in QuestKeeper_Skill do
		if ( k == skillNumber) then
			return v;
		end
	end
	return nil;
end

function QuestKeeper_GetSkillNameAsNumber(skill)
	for k, v in QuestKeeper_Skill do
		if ( v == skill) then
			return k;
		end
	end
	return 0;
end

function QuestKeeper_GetPlayerSkillsAsNumber()
	local skillNumber = 0;
	local numSkills = GetNumSkillLines();
	for skillIndex = 1, numSkills do
		local skillName, header, isExpanded, skillRank, numTempPoints, skillModifier, skillMaxRank, isAbandonable, stepCost, rankCost, minLevel, skillCostType = GetSkillLineInfo(skillIndex);
		if ( not header ) then
			skillNumber = skillNumber + QuestKeeper_GetSkillNameAsNumber(skillName);
		end
	end
	return skillNumber;
end

function QuestKeeper_GetPlayerSkillValues()
	local skillsSkillValues = {};
	local skillNumber = 0;
	local numSkills = GetNumSkillLines();
	for skillIndex = 1, numSkills do
		local skillName, header, isExpanded, skillRank, numTempPoints, skillModifier, skillMaxRank, isAbandonable, stepCost, rankCost, minLevel, skillCostType = GetSkillLineInfo(skillIndex);
		if ( not header ) then
			skillsSkillValues[QuestKeeper_GetSkillNameAsNumber(skillName)] = skillRank;
		end
	end
	return skillsSkillValues;
end

function QuestKeeper_RemoveCompletedQuests()
	local completedQuests = QuestKeeper_GetCompletedQuestsIDArray();
	local removeQuest = false;
	for k, v in completedQuests do
		if ( type(v) == "table" ) then
			QuestKeeper_Quests[k] = nil;
		else
			QuestKeeper_Quests[v] = nil;
		end
	end
end

function QuestKeeper_GetCurrentZoneAsNumber()
	local zone = GetZoneText();
	return QuestKeeper_GetZoneAsNumber(zone);
end

function QuestKeeper_GetZoneAsNumber(zone)
	for k, v in QuestKeeper_ZoneList do
		if ( zone == v ) then
			return k;
		end
	end
	return 0;
end

function QuestKeeper_GetQuestTitle(questId)
	return QuestKeeper_GetQuestTitleFromQuestInfo(QuestKeeper_GetQuestInfo(questId));
end

function QuestKeeper_GetQuestTitleFromQuestInfo(questInfo)
	if ( questInfo ) then
		return questInfo.title;
	else
		return "";
	end
end

function QuestKeeper_GetQuestDescription(questInfo)
	local str = "";
	if ( questInfo.races ) then
		if ( questInfo.races == QuestKeeper_Race_Alliance ) then
			str = str.."Alliance only\n";
		elseif ( questInfo.races == QuestKeeper_Race_Horde ) then
			str = str.."Horde only\n";
		elseif ( questInfo.races == QuestKeeper_Race_Both ) then
		else
			local tmpArray = {};
			local tmp = "";
			for k, v in QuestKeeper_Race do
				if ( k and questInfo.races > 0 ) then
					table.insert(tmpArray, v);
				end
			end
			for i = 1, (getn(tmpArray)-1) do
				tmp = tmp..tmpArray[i]..", ";
			end
			if ( getn(tmpArray) > 1 ) then
				tmp = tmp.." and ";
			end
			if ( getn(tmpArray) >= 1 ) then
				tmp = tmp..tmpArray[getn(tmpArray)].." only\n";
			end
			str = str..tmp;
		end
	end
	if ( questInfo.classes ) then
		local tmpArray = {};
		local tmp = "";
		for k, v in QuestKeeper_Class do
			if ( k and questInfo.classes > 0 ) then
				table.insert(tmpArray, v);
			end
		end
		for i = 1, (getn(tmpArray)-1) do
			tmp = tmp..tmpArray[i]..", ";
		end
		if ( getn(tmpArray) > 1 ) then
			tmp = tmp.." and ";
		end
		if ( getn(tmpArray) >= 1 ) then
			tmp = tmp..tmpArray[getn(tmpArray)].." only";
		end
		str = str..tmp;
	end
	if ( questInfo.giver ) then
		local giverInfo = QuestKeeper_Giver[questInfo.giver];
		if ( giverInfo ) then
			str = str..string.format("Given by %s", giverInfo.name);
			if ( giverInfo.zone ) then
				local zoneName = QuestKeeper_ZoneList[giverInfo.zone];
				if ( zoneName ) then
					str = str..string.format(" in %s", zoneName);
					if ( giverInfo.location ) then
						str = str..string.format(" (%d,%d)", giverInfo.location.x, giverInfo.location.y);
					end
					if ( giverInfo.roving ) then
						str = str..format(" roving");
					end
				end
			end
			str = str.."\n";
		end
	end
	if ( questInfo.previousQuests ) then
		local tmp = "";
		for k, v in questInfo.previousQuests do
			tmp = QuestKeeper_GetQuestTitle(v);
			if ( strlen(tmp) > 0 ) then
				str = str.."Requires "..tmp.."\n";
			end
		end
	end
	if ( questInfo.stepNames ) then
		local tmp = "";
		for i = 1, (getn(questInfo.stepNames)-1) do
			tmp = tmp..questInfo.stepNames..", ";
		end
		tmp = tmp..questInfo.stepNames[getn(questInfo.stepNames)].."\n";
		--[[
		for i = 1, getn(questInfo.stepNames) do
			tmp = tmp.."\nStep "..(i+1)..": "..questInfo.stepNames[i];
		end
		]]--
		str = str..tmp;
	end
	return str;
end


function QuestKeeper_GetQuestInfoTooltipArray(questInfo)
	if ( not questInfo ) then
		return nil;
	end
	local tooltipArray = {};
	local questTitle = QuestKeeper_GetQuestTitleFromQuestInfo(questInfo);
	if ( ( not questTitle ) or ( strlen(questTitle) <= 0 ) ) then questTitle = questInfo.title; end
	tooltipArray.name = questTitle;
	tooltipArray.title = questTitle;
	tooltipArray[1] = questTitle;
	local str = "";
	if ( questInfo.stepNames ) then
		local tmp = format("\nChained quest - %d steps", getn(questInfo.stepNames));
		--[[
		for i = 1, (getn(questInfo.stepNames)-1) do
			tmp = tmp..questInfo.stepNames..", ";
		end
		tmp = tmp..questInfo.stepNames[getn(questInfo.stepNames)]..", ";
		]]--
		str = str..tmp;
	end
	tooltipArray.text = str;
	tooltipArray[2] = str;
	tooltipArray.isHeader = questInfo.isHeader;
	return tooltipArray;
end

function QuestKeeper_GetQuestInfoAsText(questInfo)
	local str = "";
	str = str..string.format("[%d] %s\n", questInfo.level, questInfo.title);
	str = str..QuestKeeper_GetQuestDescription(questInfo);
	return str;
end

function QuestKeeper_GetQuestInfo(questId)
	if ( QuestKeeper_Quests[questId] ) then
		return QuestKeeper_Quests[questId];
	else
		return nil;
		--[[
		local quests = QuestKeeper_GetCompletedQuests();
		if ( quests[questId] ) then
			return quests[questId];
		else
			return nil;
		end
		]]--
	end
end


function QuestKeeper_GetCompletedQuestsExpanded()
	local completedQuests = QuestKeeper_GetCompletedQuestsIDArray();
	local expandedQuests = {};
	for k, v in completedQuests do
		expandedQuests[v] = QuestKeeper_Quests[v];
	end
	return expandedQuests;
end

function QuestKeeper_GetQuestsByZone(quests)
	local questsByZone = {};
	local currentQuestGiver = nil;
	local zone = 0;
	for k, v in quests do
		zone = 0;
		if ( ( v.giver ) ) then
			currentQuestGiver = QuestKeeper_Giver[v.giver];
			if ( currentQuestGiver ) then
				zone = currentQuestGiver.zone;
			end
		end
		if ( not questsByZone[zone] ) then
			questsByZone[zone] = {};
		end
		questsByZone[zone][k] = v;
	end
	return questsByZone;
end


function QuestKeeper_GetAppropriateQuests(ignoreZone)
	local playerLevel = QuestKeeper_GetRelativeLevel();
	local playerLevelMin = playerLevel + QuestKeeper_Appropriate_Quests_Level_Min;
	local playerLevelMax = playerLevel + QuestKeeper_Appropriate_Quests_Level_Max;
	local quests = {};
	local completedQuestsIDArray = QuestKeeper_GetCompletedQuestsIDArray();
	local appropriateQuest = true;
	local playerZone = QuestKeeper_GetCurrentZoneAsNumber();
	local playerSkills = QuestKeeper_GetPlayerSkillsAsNumber();
	local playerSkillValues = QuestKeeper_GetPlayerSkillValues();
	
	local ok = true;
	local currentQuestGiver = nil;
	
	for k, v in QuestKeeper_Quests do
		appropriateQuest = true;
		if ( ( v.level ) and ( ( v.level < playerLevelMin ) or ( v.level > playerLevelMax ) ) ) then
			appropriateQuest = false;
		elseif ( ( v.skills ) and ( v.skills and playerSkills == 0 ) ) then
			appropriateQuest = false;
		elseif ( v.skillMinimumValue ) then
			for k, v in v.skillMinimumValue do
				if ( playerSkillValues[k] < v ) then
					appropriateQuest = false;
					break;
				end
			end
		elseif ( ( not ignoreZone ) and ( v.giver ) ) then
			currentQuestGiver = QuestKeeper_Giver[v.giver];
			if ( currentQuestGiver ) then
				if ( currentQuestGiver.zone ~= playerZone ) then
					appropriateQuest = false;
				end
			end
		elseif ( v.previousQuests ) then
			ok = true;
			for ke, value in v.previousQuests do
				ok = false;
				for key, completedId in completedQuestsIDArray do
					if ( completedId == value ) then
						ok = true;
						break;
					end
				end
				if ( not ok ) then
					break;
				end
			end
			if ( not ok ) then
				appropriateQuest = false;
			end
		end
		if ( appropriateQuest ) then
			for key, completedId in completedQuestsIDArray do
				if ( completedId == k ) then
					appropriateQuest = false;
					break;
				end
			end
			if ( appropriateQuest ) then
				quests[k] = v;
			end
		end
	end
	return quests;
end

function QuestKeeper_AddCompletedQuest(playerName, id, questInfo)
	if ( not QuestKeeper_CompletedQuests ) then
		QuestKeeper_CompletedQuests = {};
	end
	if ( not QuestKeeper_CompletedQuests[playerName] ) then
		QuestKeeper_CompletedQuests[playerName] = {};
	end
	QuestKeeper_CompletedQuests[playerName][id] = questInfo;
end

function QuestKeeper_GetCompletedQuests()
	if ( not QuestKeeper_CompletedQuests ) then
		return {};
	else
		local name = UnitName("player");
		if ( QuestKeeper_CompletedQuests[name] ) then
			return QuestKeeper_CompletedQuests[name];
		else
			return {};
		end
	end
end

function QuestKeeper_GetCompletedQuestsIDArray()
	if ( not QuestKeeper_CompletedQuests ) then
		return {};
	else
		local name = UnitName("player");
		if ( QuestKeeper_CompletedQuests[name] ) then
			return QuestKeeper_CompletedQuests[name];
		else
			return {};
		end
	end
end

-- executed after load is complete load, trims all 
function QuestKeeper_TrimDatabase()
	local playerLevel = QuestKeeper_GetRelativeLevel();
	local playerLevelMin = playerLevel + QUESTKEEPER_TRIM_QUESTS_LEVEL_MIN;
	local playerLevelMax = playerLevel + QUESTKEEPER_TRIM_QUESTS_LEVEL_MAX;
	local playerClass = QuestKeeper_ClassAsNumber(UnitClass("player"));
	local playerRace = QuestKeeper_RaceAsNumber(UnitRace("player"));
	local playerSkills = QuestKeeper_GetPlayerSkillsAsNumber();
	local playerSkillValues = QuestKeeper_GetPlayerSkillValues();
	if ( (playerClass <= 0 ) or ( playerRace <= 0 ) ) then
		return;
	end
	local removeQuest = false;
	for k, v in QuestKeeper_Quests do
		removeQuest = false;
		if ( ( v.level ) and ( ( v.level < playerLevelMin ) or ( v.level > playerLevelMax ) ) ) then
			removeQuest = true;
		elseif ( ( v.races ) and ( v.races and playerRace == 0 ) ) then
			removeQuest = true;
		elseif ( ( v.class) and ( v.class and playerClass == 0 ) ) then
			removeQuest = true;
		--[[
		elseif ( ( v.skills ) and ( v.skills and playerSkills == 0 ) ) then
			removeQuest = true;
		elseif ( v.skillMinimumValue ) then
			for k, v in v.skillMinimumValue do
				if ( playerSkillValues[k] < v ) then
					removeQuest = true;
					break;
				end
			end
		]]--
		end
		if ( removeQuest ) then
			QuestKeeper_Quests[k] = nil;
		end
	end
end

function QuestKeeper_GenerateNoLongerPresentQuestsArray(beforeQuestArray, afterQuestArray)
	local differenceQuestArray = {};
	for k, v in beforeQuestArray do
		for key, value in v do
			if ( not afterQuestArray[k][key] ) then
				if ( not differenceQuestArray[k] ) then
					differenceQuestArray[k] = {};
				end
				differenceQuestArray[k][key] = value;
			end
		end
	end
	return differenceQuestArray;
end

function QuestKeeper_GenerateNewQuestsArray(beforeQuestArray, afterQuestArray)
	local differenceQuestArray = {};
	for k, v in afterQuestArray do
		for key, value in v do
			if ( not beforeQuestArray[k][key] ) then
				if ( not differenceQuestArray[k] ) then
					differenceQuestArray[k] = {};
				end
				differenceQuestArray[k][key] = value;
			end
		end
	end
	return differenceQuestArray;
end

function QuestKeeper_GenerateCurrentQuestArray()
	local numEntries, numQuests = GetNumQuestLogEntries();
	local currentQuests = {};
	local currentQuestHeaderTitle = "NoArea";
	for questIndex = 1, numEntries do
		local questLogTitleText, level, questTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(questIndex);
		if ( isHeader ) then
			currentQuestHeaderTitle = questLogTitleText;
		else
			if ( not currentQuests[currentQuestHeaderTitle] ) then
				currentQuests[currentQuestHeaderTitle] = {};
			end
			currentQuests[currentQuestHeaderTitle][format("[%d] %s", level, questLogTitleText)] = 1;
		end
	end
	return currentQuests;
end