--
--	Create by iWOW (http://wow.365wave.com)
--   Modify by Alfred (http://statue.sayya.org/wowdb/)
--	Modify by 独孤傲雪 (http://www.ngacn.com)
----------------------------------------------------------------------------------------------------
-- Local QuestHistory Variables
----------------------------------------------------------------------------------------------------
local HC_QuestLibrary_Initing = false;
local QuestLibrary_QuestData = nil;
local QuestLibrary_Inited = false;
local QuestLibraryMN_MinDiff = 7;
local MapNote_Colors = {
	[0] = {r = 1.0, g = 0.82, b = 0.0};
	[1] = {r = 0.55, g = 0.46, b = 0.04};
	[2] = {r = 0.87, g = 0.06, b = 0.0};
	[3] = {r = 0.56, g = 0.0, b = 0.0};
	[4] = {r = 0.18, g = 0.7, b = 0.2};
	[5] = {r = 0.0, g = 0.39, b = 0.05};
	[6] = {r = 0.42, g = 0.47, b = 0.87};
	[7] = {r = 0.25, g = 0.35, b = 0.66};
	[8] = {r = 1.0, g = 1.0, b = 1.0};
	[9] = {r = 0.65, g = 0.65, b = 0.65};
};
----------------------------------------------------------------------------------------------------
-- Global QuestHistory Variables
----------------------------------------------------------------------------------------------------

QUESTLIBRARY_VERSION = "1.9.2";
----------------------------------------------------------------------------------------------------
-- SetHook Functions
----------------------------------------------------------------------------------------------------
local SetHook = BLibrary("BHook");
local QustLibarary_Eventer = BLibrary("BEvent");
QuestLibraryMN_Data_Notes = BLibrary("BUtils"); -- 这里面的数据统统标记
local EnableQuestLibrary = false;
local questLibraryLoaded = false;
----------------------------------------------------------------------------------------------------
-- On_Foo Functions
----------------------------------------------------------------------------------------------------
function QuestLibrary_OnLoad()
	-- Set up slash commands
	SLASH_QuestLibrary1 = "/qlib";
	SLASH_QuestLibrary2 = "/questlibrary";
	SlashCmdList["QuestLibrary"] = QuestLibrary_SlashCmd; 
end
--------------
-- toggle
--------------
function QuestLibrary_Toggle(switch)
	if (switch) then
		EnableQuestLibrary = true;
		if (not questLibraryLoaded ) then
			hooksecurefunc("QuestLog_UpdateQuestDetails", QuestLibrary_QL_Detail_Update);
			hooksecurefunc("QuestLog_Update", QuestLibrary_QL_Update);
		end		
		QustLibarary_Eventer:RegisterEvent("WORLD_MAP_UPDATE");
		QustLibarary_Eventer.PLAYER_ENTERING_WORLD(); 
		QuestLibraryWorldMapDeleteButton:Show();
		questLibraryLoaded = true;
	else
		EnableQuestLibrary = false;
		--SetHook:Unhook("QuestLog_UpdateQuestDetails");
		--SetHook:Unhook("QuestLog_Update");
		QustLibarary_Eventer:UnregisterEvent("WORLD_MAP_UPDATE");
		QuestLibraryQLNPCFrame:Hide();
		QuestLogDetailScrollFrame:Show();
		QuestLogFrameAbandonButton:Show();
		QuestFramePushQuestButton:Show();
		QuestLibraryQLACQueryButton:Hide();
		QuestLibraryWorldMapDeleteButton:Hide();
		for i = 1, 10 do
			local button = getglobal("QuestLibraryQLObjectButton"..i);
			button:Hide();
		end
	end
end
----------------------------------------------------------------------------------------------------
-- Internal Functions
----------------------------------------------------------------------------------------------------

function HC_CountQuestNpcDB()
	local HC_QuestNpcDBCount=0;
	for j,o in pairs(BaseNpcDB) do
		HC_QuestNpcDBCount = HC_QuestNpcDBCount+1;
	end
	return HC_QuestNpcDBCount;
end

function HC_PrintLogString( msg )
	if( DEFAULT_CHAT_FRAME and msg) then
		DEFAULT_CHAT_FRAME:AddMessage( msg, 1.0, 1.0, 1.0 );
	end
end

QustLibarary_Eventer:Init{
	name = "iWOWQuery",
	func = function()		
		
		if ( QuestLibraryMN_Data_Notes ) then
			QuestLibraryWorldMapDeleteButton:Show();
		end

		if (not QuestLibraryOptions) then 
			QuestLibraryOptions = {}; 
		end
		-- 缺省保证上下相差15级的任务不被载入
		if (not QuestLibraryOptions.RemoveQuestAcceptLevelHigher) then 
			QuestLibraryOptions.RemoveQuestAcceptLevelHigher = 15;
		end		
		if (not QuestLibraryOptions.RemoveQuestLevelBelow) then 
			QuestLibraryOptions.RemoveQuestLevelBelow = 15;
		end
		-- 无用npc判断
		if (not QuestLibraryOptions.RemoveUnusedNPC) then 
			QuestLibraryOptions.RemoveUnusedNPC = true;
		end
		-- 阵营判断
		if (not QuestLibraryOptions.RemoveOpponentFaction) then 
			QuestLibraryOptions.RemoveOpponentFaction = true;
		end
	end
}

function QustLibarary_Eventer.PLAYER_ENTERING_WORLD()		
	local playerName = UnitName("player");
	
	if ( not HC_QuestLibrary_Initing and not QuestLibrary_Inited and playerName ~= UKNOWNBEING and playerName ~= UNKNOWNOBJECT) then
		HC_QuestLibrary_Initing = true;		
		-- 去掉非本阵营和等级相差过大的任务	
		QuestLibrary_RemoveOpponentFaction(false);		
		-- 去掉无用的npc
		QuestLibrary_RemoveUnusedNPC(false);		
		QuestLibrary_Inited = true;
	end
	if QuestLibrary_Inited then
		QustLibarary_Eventer:UnregisterEvent("PLAYER_ENTERING_WORLD");
	end	
end

function QustLibarary_Eventer.WORLD_MAP_UPDATE()
	MapNote_WorldMapButton_Update();
end

function QuestLibrary_SlashCmd(msg)
	if (msg) then
		local cmd = gsub(msg, "%s*([^%s]+).*", "%1");
		local info ="|cff00ffffQuestLibrary "..QUESTLIBRARY_VERSION..": |r|cffffffff";
		if( cmd == "Information" or cmd == "info" ) then
			HC_PrintLogString(info);				
			HC_PrintLogString("  BaseQuestDB size: "..#BaseQuestDB);
			HC_PrintLogString("  BaseNpcDB size: "..HC_CountQuestNpcDB());
			if ( QuestLibraryOptions.RemoveOpponentFaction == true ) then
				HC_PrintLogString("  RemoveOpponentFaction: Yes");
			else
				HC_PrintLogString("  RemoveOpponentFaction: No");
			end
			if ( QuestLibraryOptions.RemoveUnusedNPC == true ) then
				HC_PrintLogString("  RemoveUnusedNPC: Yes");
			else
				HC_PrintLogString("  RemoveUnusedNPC: No");
			end
			return;
		elseif( cmd == "RemoveUnusedNPC" or cmd == "rmun" ) then
			QuestLibrary_RemoveUnusedNPC(1);
			return;		
		elseif( cmd == "RemoveOpponentFaction" or cmd == "rmof" ) then
			QuestLibrary_RemoveOpponentFaction(true);
			return;
		elseif( cmd == "RemoveAllUnused" or cmd == "rmall" ) then
			QuestLibrary_RemoveOpponentFaction(true);
			QuestLibrary_RemoveCompletedQuest(true);
			QuestLibrary_RemoveUnusedNPC(true);
			return;
		end
		HC_PrintLogString(info.."Usage: /questlibrary [command] OR /qlib [command]");
		HC_PrintLogString("|cffffffff/qlib info  - Information");
		HC_PrintLogString("|cffffffff/qlib rmof  - RemoveOpponentFaction");		
		HC_PrintLogString("|cffffffff/qlib rmun  - RemoveUnusedNPC");
		HC_PrintLogString("|cffffffff/qlib rmall - RemoveAllUnused");
	end
end

----------------------------------------------------------------------------------------------------
-- Reduce Database Functions
----------------------------------------------------------------------------------------------------

function QuestLibrary_ToggleBoolean ( bool, msg)
	if( bool == false ) then
		HC_PrintLogString("QuestLibrary: "..msg.." On");
		bool = true;
	else
		HC_PrintLogString("QuestLibrary: "..msg.." Off");
		bool = false;
	end
	return bool;
end
-- 去除无用的任务信息
function QuestLibrary_RemoveOpponentFaction( toggle )
	if ( toggle == true ) then
		QuestLibraryOptions.RemoveOpponentFaction=QuestLibrary_ToggleBoolean(QuestLibraryOptions.RemoveOpponentFaction, "RemoveOpponentFaction");
	end
	if ( QuestLibraryOptions.RemoveOpponentFaction 
		or QuestLibraryOptions.RemoveQuestAcceptLevelHigher > 0 
		or QuestLibraryOptions.RemoveQuestLevelBelow > 0 ) then
		--HC_PrintLogString("QuestLibrary - Removing Opponent Faction Quests");
		local pf=1,qf;
		if (UnitFactionGroup("player")=="Horde") then
			pf=2;
		end
		local index=1;
		while BaseQuestDB[index] do
			_,_,qf = string.find(BaseQuestDB[index].q,"[^`]*`[^`]*`%d*`%d*`(%d*)`.*");
			qf = tonumber(qf);
			if (qf==pf and QuestLibraryOptions.RemoveOpponentFaction == true ) then
				table.remove(BaseQuestDB,index);
			else
				-- 地点,名字,接受等级,任务等级,阵营,任务目标
				local _,_,qc,qt,qla,ql,qf,qo = string.find(BaseQuestDB[index].q,"([^`]*)`([^`]*)`(%d*)`(%d*)`(%d*)`(.*)");
				local level = UnitLevel("player");
				qla = tonumber(qla);
				ql = tonumber(ql);
				if ( qla and QuestLibraryOptions.RemoveQuestAcceptLevelHigher > 0 and qla > QuestLibraryOptions.RemoveQuestAcceptLevelHigher+level ) then
					table.remove(BaseQuestDB,index);
				else
					if ( ql and QuestLibraryOptions.RemoveQuestLevelBelow > 0 and ql < level-QuestLibraryOptions.RemoveQuestLevelBelow ) then
						table.remove(BaseQuestDB,index);
					else
						index = index + 1;
					end
				end				
			end
		end
		--HC_PrintLogString("QuestLibrary - BaseQuestDB size "..#BaseQuestDB);
	end
end
-- 去除无用的npc信息
function QuestLibrary_RemoveUnusedNPC( force )
	if ( toggle == true ) then
		QuestLibraryOptions.RemoveUnusedNPC=QuestLibrary_ToggleBoolean(QuestLibraryOptions.RemoveUnusedNPC, "RemoveUnusedNPC");
	end
	if ( QuestLibraryOptions.RemoveUnusedNPC == true ) then
		--HC_PrintLogString("QuestLibrary - Cleaning NPC database");
		for i, q in pairs(BaseQuestDB) do
			if ( q.os ~= nil ) then
				for j, o in pairs(q.os) do
					if ( o.n ~= nil ) then
						for npccode in string.gmatch(o.n,"(%d+)") do
							npccode = tonumber(npccode);
							if ( BaseNpcDB[npccode] ~= nil ) then
								BaseNpcDB[npccode]["f"] = true;
							end
						end
					end
				end
			end
		end
		for j, o in pairs(BaseNpcDB) do
			if ( o.f == nil or o.f ~= true ) then
				BaseNpcDB[j] = nil;
			else
				o["f"] = nil;
			end
		end
		local HC_QuestNpcDBCount = HC_CountQuestNpcDB();
		--HC_PrintLogString("QuestLibrary - BaseNpcDB size "..HC_QuestNpcDBCount);
	end
end


local function QuestLibrary_DoNothing()
end
--=============================================================================
-- 函数: GetQuestByIndex
-- 描述: 通过任务的index获得任务的信息
-- 参数: index [输入] - BaseQuestDB里的索引号
-- 返回: 任务信息
--=============================================================================
local function QuestLibrary_GetQuestByIndex(index)
	local questData = nil;

	if ( not index ) then
		index = 1;
	end

	local _,_,qc,qt,qla,ql,qf,qo = string.find(BaseQuestDB[index].q,"([^`]*)`([^`]*)`(%d*)`(%d*)`(%d*)`(.*)");
	qla = tonumber(qla);
	ql = tonumber(ql);
	qf = tonumber(qf);
	questData = {};
	questData.c = qc;	-- 地点
	questData.t = qt;	-- 任务
	questData.la = qla;	-- 接受等级
	questData.l = ql;	-- 任务等级
	questData.o = qo;	-- 任务目标
	-- 特殊任务,例如精英,团队任务
	if (BaseQuestDB[index].y) then
		questData.y = BaseQuestDB[index].y;
	end
	-- 起始npc
	if (BaseQuestDB[index].pa) then
		-- 名字,地点,坐标
		_,_,questData.g, questData.az, questData.pas = string.find(BaseQuestDB[index].pa,"([^`]*)`([^`]*)`(.*)");
		questData.pa = {};
		for x,y in string.gmatch(questData.pas,"(%d+),(%d+)") do
			x,y = tonumber(x),tonumber(y);
			local haspos=false;
			local insertpos=#questData.pa+1;
			for k,v in pairs(questData.pa) do
				if (v.x==x and v.y==y) then
					haspos = true;
					break;
				elseif (v.x>x or (v.x==x and v.y>y)) then
					insertpos = k;
					break;
				end
			end
			if (not haspos) then
				table.insert(questData.pa,insertpos,{x=x,y=y});
			end
		end
		questData.pas = nil;
	end
	-- 结束npc
	if (BaseQuestDB[index].pc) then
		-- 名字, 地点, 坐标 
		_,_,questData.w, questData.cz, questData.pcs = string.find(BaseQuestDB[index].pc,"([^`]*)`([^`]*)`(.*)");
		questData.pc = {};
		for x, y in string.gmatch(questData.pcs,"(%d+),(%d+)") do
			x,y = tonumber(x),tonumber(y);
			local haspos = false;
			local insertpos = #questData.pc+1;
			for k,v in pairs(questData.pc) do
				if (v.x == x and v.y == y) then
					haspos = true;
					break;
				elseif (v.x > x or (v.x == x and v.y > y)) then
					insertpos = k;
					break;
				end
			end
			if (not haspos) then
				table.insert(questData.pc, insertpos, {x=x, y=y});
			end
		end
		questData.pcs = nil;
	end
	-- 任务物品和npc
	if (BaseQuestDB[index].os) then
		questData.os = {};
		for j, o in pairs(BaseQuestDB[index].os) do
			local osd = {};
			osd.t = o.t;	-- 物品
			if (o.n) then	-- npc
				osd.npc = {};
				for npccode in string.gmatch(o.n,"(%d+)") do
					npccode = tonumber(npccode);
					if (BaseNpcDB[npccode]) then
						-- npc名字, 级别, 最低等级, 最高等级
						local _,_,n,c,ll,lm = string.find(BaseNpcDB[npccode].n,"([^`]*)`([^`]*)`(%d*)`(%d*)");
						ll,lm = tonumber(ll),tonumber(lm);
						-- 任务物品(怪物)可能分布于不同的地方
						for xi in pairs(BaseNpcDB[npccode].x) do
							-- 地点, 坐标
							local _,_,a,pos = string.find(BaseNpcDB[npccode].x[xi],"([^`]*)`(.*)");
							if (not osd.npc[a]) then
								osd.npc[a] = {};
							end
							local npcindex=nil;
							for k, v in pairs(osd.npc[a]) do
								if (v.n == n and v.c == c) then
									npcindex = k;
									break;
								end
							end
							if (not npcindex) then
								table.insert(osd.npc[a],{n=n,c=c});
								npcindex = #osd.npc[a];
							else
								npcindex = nil;
							end
							if (npcindex) then
								osd.npc[a][npcindex].ll = ll;
								osd.npc[a][npcindex].lm = lm;
								if (not osd.npc[a][npcindex].pos) then
									osd.npc[a][npcindex].pos = {};
								end
								for x, y in string.gmatch(pos, "(%d+),(%d+)") do
									x, y = tonumber(x),tonumber(y);
									table.insert(osd.npc[a][npcindex].pos, {x=x, y=y});
								end
							end
						end
					end
				end
				for area, npclist in pairs(osd.npc) do
					table.sort(osd.npc[area],function(n1,n2) return (n1.ll or -1) < (n2.ll or -1) or (n1.ll == n2.ll and (n1.lm or -1)<(n2.lm or -1)); end);
				end
			end
			table.insert(questData.os,osd);
		end
	end
	return questData;
end
--=============================================================================
-- 函数: GetQuest
-- 描述: 通过任务名, 等级, 目标描述来获得任务信息
-- 参数: title, level, objective, objective2
-- 返回: 任务信息 或 nil
--=============================================================================
local function QuestLibrary_GetQuest(title, level, objective, objective2)
	if ( not title ) then
		title = "";
	else
		title = string.gsub(title, "%s*%[.+%]%s*", "");  -- remove level pattern [??] which added by some addon		
	end
	if ( not level ) then
		level = "";
	else
		level = tonumber(level);
	end
	if ( not objective ) then
		objective = "";
	end
	
	for i, q in pairs(BaseQuestDB) do
		local _, _, qt, ql, qo = string.find(q.q,"[^`]*`([^`]*)`%d*`(%d*)`%d*`(.*)");
		ql = tonumber(ql);
		if (qt==title and (qo==objective or qo==objective2)) then
			return QuestLibrary_GetQuestByIndex(i);
		end
	end
	return nil;
end

----------------------------------------------------------------------------------------------------
-- MapNotes Hooked Functions
----------------------------------------------------------------------------------------------------
-- 获得当前地图名
local function QuestLibrary_GetMapKey()
	local map = GetMapInfo();
	if ( not map ) then
		if ( GetCurrentMapContinent() == WORLDMAP_COSMIC_ID ) then
			map = "Cosmic";	-- 宇宙地图
		else
			map = "WorldMap";	-- 世界地图
		end
	end

	map = "WM "..map;
	return map;
end
-- 从地名获得地图的id
local function QuestLibrary_MapNotes_GetZoneID(zone)
    local continentNames, i, val = { GetMapContinents() };

    for i, val in pairs(continentNames) do
        local zoneNames = { GetMapZones(i) };

        for j in pairs(zoneNames) do
            if (zoneNames[j] == zone) then
                return i, j;
            end
        end
    end
end

-- Converts the zone into the continent/zone numbers used by MapNotes
local function QuestLibrary_MapNotes_GetZone(zone)
	if (QUESTLIBRARY_GETZONE[zone]) then
		return "WM "..QUESTLIBRARY_GETZONE[zone];
	else
		return;
	end
end
-- 给地图做标记
local function QuestLibrary_Make_MapNote(zonename, title, tcolor, info1, i1color, info2, i2color, icon, x, y)
	local rl =  0;
	if ( QuestLibraryMN_Data_Notes ) then		
		local key = QuestLibrary_MapNotes_GetZone(zonename); -- 修改后的地图名

		local xpos,ypos=(x+0)/100, (y+0)/100;	-- 坐标
		-- 保证无相邻的标记
		local c, hasnear = 1, false;
		if (not QuestLibraryMN_Data_Notes[key]) then
			QuestLibraryMN_Data_Notes[key] = {};
		end
		for l, value in pairs(QuestLibraryMN_Data_Notes[key]) do
			-- 和已有的标记距离比较
			if (abs(value.xPos - xpos) <= 0.0009765625 * QuestLibraryMN_MinDiff and abs(value.yPos - ypos) <= 0.0013020833 * QuestLibraryMN_MinDiff) then
				hasnear = true;
				break;
			end
			c = l+1;
		end
		
		if ( not hasnear and c < 100) then
			-- 标记的信息
			QuestLibraryMN_Data_Notes[key][c] = {};
			QuestLibraryMN_Data_Notes[key][c].name = title;
			QuestLibraryMN_Data_Notes[key][c].ncol = tcolor;
			QuestLibraryMN_Data_Notes[key][c].inf1 = info1;
			QuestLibraryMN_Data_Notes[key][c].in1c = i1color;
			QuestLibraryMN_Data_Notes[key][c].inf2 = info2;
			QuestLibraryMN_Data_Notes[key][c].in2c = i2color;
			QuestLibraryMN_Data_Notes[key][c].creator = "QuestLibrary";
			QuestLibraryMN_Data_Notes[key][c].icon = icon;
			QuestLibraryMN_Data_Notes[key][c].xPos = xpos;
			QuestLibraryMN_Data_Notes[key][c].yPos = ypos;
		elseif (hasnear) then
			rl = 1;
		else
			rl = 2;
		end		
		MapNote_WorldMapButton_Update();		
	end
	-- rl: 0,表示可以标记, 1表示有邻近的, 2表示没有	
	return rl;
end
-- 通过地图名删除标记
local function QuestLibrary_DeleteMapNotes(key)
	if ( QuestLibraryMN_Data_Notes and QuestLibraryMN_Data_Notes[key]) then
		local i = 1;		
		local old_SetMapZoom = SetMapZoom;
		SetMapZoom = QuestLibrary_DoNothing;
		-- 大陆, 地区
		local orgCont, orgZone = GetCurrentMapContinent(), GetCurrentMapZone();		
		-- 删除任务查询的标记
		while (QuestLibraryMN_Data_Notes[key][i]) do
			if (QuestLibraryMN_Data_Notes[key][i].creator == "QuestLibrary") then
				QuestLibraryMN_DeleteNote(i, key);
			else
				i = i + 1;
			end
		end
		old_SetMapZoom(orgCont, orgZone);
		SetMapZoom = old_SetMapZoom;
		
		MapNote_WorldMapButton_Update();		
	end
end
-- 点击标记
local function QuestLibrary_QuestNPCTree_OnClick(postable)
	if (not postable or not QuestLibrary_QuestData) then
		return;
	end

	local MapNoteError = 0;
	-- [任务名称][任务目标][]获得: XX 位于XX
	local text = "["..QuestLibrary_QuestData.t..QUESTLIBRARY_TEXT_TOOLTIP1..postable.t..QUESTLIBRARY_TEXT_TOOLTIP2..postable.n..QUESTLIBRARY_TEXT_TOOLTIP3..postable.a;
	
	if ( QuestLibraryMN_Data_Notes and postable.pos and postable.pos[1] and not IsShiftKeyDown()) then
		local key = QuestLibrary_MapNotes_GetZone(postable.a);	--修改后的地图名
		QuestLibrary_DeleteMapNotes(key);	--删除该地图上的任务标记信息
	end

	for k, pos in pairs(postable.pos) do
		text = text.." <"..pos.x..","..pos.y..">";
		if (not (IsShiftKeyDown() and ChatFrameEditBox:IsVisible()) and MapNoteError < 2) then
			MapNoteError = QuestLibrary_Make_MapNote(postable.a, postable.n, 0, QUESTLIBRARY_TEXT_TOOLTIP4..QuestLibrary_QuestData.t, 4, postable.t, 8, 1, pos.x, pos.y);
			if (MapNoteError==2) then	-- 有相邻的标记
				-- 区域XX中的地图标记过多，请删除部分。
				HC_PrintLogString(QUESTLIBRARY_TEXT_TOOLTIP5..postable.a..QUESTLIBRARY_TEXT_TOOLTIP6);
			end
		end
	end

	if ( IsShiftKeyDown() ) then
		if ( ChatFrameEditBox:IsVisible() ) then
			ChatFrameEditBox:Insert(text);
		end
	elseif ( QuestLibraryMN_Data_Notes and postable.pos and postable.pos[1]) then
		local continent, zone = QuestLibrary_MapNotes_GetZoneID(postable.a);
		ToggleWorldMap();	-- 开启地图
		SetMapZoom(continent, zone);	-- 跳转到任务地图
	end
end
-- 返回查询的信息
local function QuestLibrary_GenEarthTreeTable(id)
	if (QuestLibrary_QuestData 
		and QuestLibrary_QuestData.os 
		and QuestLibrary_QuestData.os[id] 
		and QuestLibrary_QuestData.os[id].npc) then

		local eTree={};
		local objtext = QuestLibrary_QuestData.os[id].t;	-- 物品or怪物
		objtext = string.gsub(objtext,QUESTLIBRARY_QUESTOBJECT_FILTER1,""); -- 已杀死
		objtext = string.gsub(objtext,QUESTLIBRARY_QUESTOBJECT_FILTER2,""); -- ：.*	
		for z, zn in pairs(QuestLibrary_QuestData.os[id].npc) do
			local areaentry={};
			areaentry.title = z;	-- 地区
			areaentry.children = {};	-- 

			for i, nd in pairs(zn) do
				local npcentry = {};
				npcentry.title = nd.n; -- npc名字
				npcentry.titleColor={r=1, g=1, b=0}; -- 黄色
				
				npcentry.right = "";
				if (nd.c) then	-- 级别（如：普通）
					npcentry.right = npcentry.right..nd.c;
				end
				-- 最低等级和最高等级
				if (nd.ll and nd.lm) then
					if (nd.ll==nd.lm) then
						npcentry.right = npcentry.right.." lv "..nd.ll;
					else
						npcentry.right = npcentry.right.." lv "..nd.ll.."-"..nd.lm;
					end
					npcentry.rightColor = GetDifficultyColor(nd.ll);
				else
					-- 等级未知
					npcentry.right = npcentry.right.." "..UNIT_LETHAL_LEVEL_TEMPLATE;
				end

				npcentry.onClick = QuestLibrary_QuestNPCTree_OnClick;
				-- 任務查詢(任務物品獲得) XX
				-- npc
				-- 地区
				local tltip = QUESTLIBRARY_TEXT_TOOLTIP7..objtext.."|r\n|cFFFF0000"..nd.n.."|r\n"..z.."\n";
				local poscount = 0;
				-- 坐标，5组换行
				for k, v in pairs(nd.pos) do
					if (poscount == 5) then
						poscount = 0;
						tltip = tltip.."\n";
					end
					tltip = tltip.." <"..v.x..","..v.y..">";
					poscount = poscount+1;
				end
				npcentry.tooltip = tltip;
				-- 怪物, 地区, npc, 坐标
				npcentry.value = {t=objtext, a=z, n=nd.n, pos=nd.pos};
				table.insert(areaentry.children,npcentry);
			end
			table.insert(eTree,areaentry);
		end
		return eTree;
	end
	return nil;
end

-- Remove all notes on World Map
function QuestLibrary_WorldMapDeleteButton_OnClick()
	local key = QuestLibrary_GetMapKey();
	QuestLibrary_DeleteMapNotes(key);
end

----------------------------------------------------------------------------------------------------
-- QuestLog Hooked Functions
----------------------------------------------------------------------------------------------------

-- QuestLog
function QuestLibrary_QL_Update(...)
	--OrigFunc();
	if (EnableQuestLibrary) then
		QuestLibraryQLNPCFrame:Hide();
		QuestLogFrameAbandonButton:Show();
		QuestFramePushQuestButton:Show();
		for i=1, QUESTS_DISPLAYED, 1 do
			local questLogTitle = getglobal("QuestLogTitle"..i);
			local questIndex = i + FauxScrollFrame_GetOffset(QuestLogListScrollFrame);
			local questLogTitleText, level, _, _, isHeader = GetQuestLogTitle(questIndex);
			if (not isHeader and questLogTitle and questLogTitleText) then		
				questLogTitle:SetText("["..level.."]"..questLogTitleText);
			end
		end
	end
end

-- QuestLog
-- hook任务log刷新,以显示查询button
function QuestLibrary_QL_Detail_Update(...)
	--OrigFunc();
	if (EnableQuestLibrary) then	
		QuestLibraryQLNPCFrame:Hide();
		QuestLogDetailScrollFrame:Show();
		QuestLogFrameAbandonButton:Show();
		QuestFramePushQuestButton:Show();

		local questID = GetQuestLogSelection();
		local questTitle, level = GetQuestLogTitle(questID);	
		local _, questObjectives = GetQuestLogQuestText();
	
		-- support $playname$
		local pp3 = questObjectives;	
		local _,_,pp1, pp2 = string.find(questObjectives,"(.*)"..UnitName("player").."(.*)");
		if (pp1 and pp2) then 
			pp3 = string.format("%s$playername$%s",pp1, pp2);
		end

		QuestLibrary_QuestData = QuestLibrary_GetQuest(questTitle, level, questObjectives, pp3);
		-- 如果有开始和结束的npc地点
		if (QuestLibrary_QuestData and (QuestLibrary_QuestData.az or QuestLibrary_QuestData.cz)) then
			QuestLibraryQLACQueryButton:Show();
		else
			QuestLibraryQLACQueryButton:Hide();
		end
	
		for i = 1, 10 do
			local button = getglobal("QuestLibraryQLObjectButton"..i);
			local qlstring = getglobal("QuestLogObjective"..i);
			button:Hide();
			if (qlstring:IsVisible() and QuestLibrary_QuestData and QuestLibrary_QuestData.os) then
 				local text = qlstring:GetText();
				text = string.gsub(text,QUESTLIBRARY_QUESTOBJECT_FILTER1,"");
				text = string.gsub(text,QUESTLIBRARY_QUESTOBJECT_FILTER2,"");
 				for k, v in pairs(QuestLibrary_QuestData.os) do
					local objtext = v.t; -- 任务物品或者怪物
					objtext = string.gsub(objtext,QUESTLIBRARY_QUESTOBJECT_FILTER1,"");
					objtext = string.gsub(objtext,QUESTLIBRARY_QUESTOBJECT_FILTER2,"");
 					if (objtext == text and v.npc) then
		 				button:ClearAllPoints();
						button:SetPoint("TOPLEFT","QuestLogObjective"..i,"TOPLEFT");
						button:SetPoint("BOTTOMRIGHT","QuestLogObjective"..i,"BOTTOMRIGHT");
						button:SetID(k);
						button:Show();
						break;
 					end
 				end
			end
		end
	end
end

-- QuestLog
-- 点击显示npc信息
function QuestLibrary_QLObjectButton_OnClick(button, id)
	if (QuestLibrary_QuestData and QuestLibrary_QuestData.os and QuestLibrary_QuestData.os[id] and QuestLibrary_QuestData.os[id].npc) then
		-- 任务信息
		local ETree = QuestLibrary_GenEarthTreeTable(id);
		if (ETree) then
			QuestLogDetailScrollFrame:Hide();
			QuestLogFrameAbandonButton:Hide();
			QuestFramePushQuestButton:Hide();
			-- 这里需要再研究下,树形结构
			EarthTree_LoadEnhanced(QuestLibraryQLNPCTree, ETree);

			EarthTree_ScrollToTop(QuestLibraryQLNPCTree);
			EarthTree_UpdateFrame(QuestLibraryQLNPCTree);
			
			QuestLibraryQLNPCFrame:Show();
		end
	end
end

-- QuestLog
-- 点击做地图标记
local function QuestLibrary_QuestACTree_OnClick(value)
	if (not value or not QuestLibrary_QuestData) then
		return;
	end
	QuestLibrary_QH_SendToMapNotes("LeftButton",value.l);
end

-- QuestLog
-- 
function QuestLibrary_QLACQueryButton_OnClick()
	if (QuestLibrary_QuestData and (QuestLibrary_QuestData.az or QuestLibrary_QuestData.cz)) then
		local ETree = {};
		if (QuestLibrary_QuestData.az) then
			local tltip=QUESTLIBRARY_TEXT_TOOLTIP8;
			if (QuestLibrary_QuestData.g) then
				tltip=tltip.."|cFF00FF00"..QuestLibrary_QuestData.g.."|r\n";
			end
			tltip=tltip..QuestLibrary_QuestData.az.."\n";
			local poscount = 0;
			for k,v in pairs(QuestLibrary_QuestData.pa) do
				if (poscount==5) then
					poscount = 0;
					tltip = tltip.."\n";
				end
				tltip = tltip.." <"..v.x..","..v.y..">";
				poscount = poscount+1;
			end	
			local ctitle = QuestLibrary_QuestData.az;
			if (QuestLibrary_QuestData.pa and QuestLibrary_QuestData.pa[1]) then
				ctitle = ctitle.." ("..QuestLibrary_QuestData.pa[1].x..", "..QuestLibrary_QuestData.pa[1].y..")";
			end
			local entry = {
				title = QUESTLIBRARY_TEXT_ACCEPTQUESTLOCATION,
				children = {
					{
						title = ctitle,
						titleColor = {r=1, g=1, b=0},
						tooltip = tltip,
						right = QuestLibrary_QuestData.g,
						rightColor = {r=0,g=1,b=0},
						onClick = QuestLibrary_QuestACTree_OnClick,
						value = {l="a"},
					}
				},
			};
			table.insert(ETree, entry);
		end
		if (QuestLibrary_QuestData.cz) then
			local tltip=QUESTLIBRARY_TEXT_TOOLTIP9;
			if (QuestLibrary_QuestData.w) then
				tltip=tltip.."|cFF00FF00"..QuestLibrary_QuestData.w.."|r\n";
			end
			tltip=tltip..QuestLibrary_QuestData.cz.."\n";
			local poscount = 0;
			for k,v in pairs(QuestLibrary_QuestData.pc) do
				if (poscount==5) then
					poscount = 0;
					tltip = tltip.."\n";
				end
				tltip = tltip.." <"..v.x..","..v.y..">";
				poscount = poscount+1;
			end
			local ctitle = QuestLibrary_QuestData.cz;
			if (QuestLibrary_QuestData.pc and QuestLibrary_QuestData.pc[1]) then
				ctitle = ctitle.." ("..QuestLibrary_QuestData.pc[1].x..", "..QuestLibrary_QuestData.pc[1].y..")";
			end
			local entry = {
				title = QUESTLIBRARY_TEXT_COMPLETEQUESTLOCATION,
				children = {
					{
						title = ctitle,
						titleColor={r=1,g=1,b=0},
						tooltip = tltip,
						right = QuestLibrary_QuestData.w,
						rightColor={r=0,g=1,b=0},
						onClick = QuestLibrary_QuestACTree_OnClick,
						value = {l="c"},
					}
				},
			};
			table.insert(ETree,entry);
		end		
		QuestLogDetailScrollFrame:Hide();
		QuestLogFrameAbandonButton:Hide();
		QuestFramePushQuestButton:Hide();
		EarthTree_LoadEnhanced(QuestLibraryQLNPCTree, ETree);

		EarthTree_ScrollToTop(QuestLibraryQLNPCTree);
		EarthTree_UpdateFrame(QuestLibraryQLNPCTree);

		QuestLibraryQLNPCFrame:Show();
	end
end
----------------------------------------------------------------------------------------------------
-- 
----------------------------------------------------------------------------------------------------
function QuestLibrary_QH_SendToMapNotes(button, locationType)
	if (QuestLibrary_QuestData) then
		local title;
		local info1,info2 = "","";
		local icon;
		local p;
		local zone;
		if ( locationType == "a" ) then
			title = QuestLibrary_QuestData.t;
			info1 = QUESTLIBRARY_TEXT_ACCEPTQUEST;
			info2 = QuestLibrary_QuestData.g;
			p = QuestLibrary_QuestData.pa;
			zone = QuestLibrary_QuestData.az;
			icon = 2;
		else
			title = QuestLibrary_QuestData.t;
			info1 = QUESTLIBRARY_TEXT_COMPLETEQUEST;
			info2 = QuestLibrary_QuestData.w;
			p = QuestLibrary_QuestData.pc;
			zone = QuestLibrary_QuestData.cz;
			icon = 3;
		end
		if ( QuestLibraryMN_Data_Notes and p and p[1] and not IsShiftKeyDown()) then
			local key = QuestLibrary_MapNotes_GetZone(zone);
			QuestLibrary_DeleteMapNotes(key);
		end
		local text = "["..title.."]"..info1..": "..info2..QUESTLIBRARY_TEXT_TOOLTIP3..zone;
		local MapNoteError = 0;
		for k,v in pairs(p) do
			text = text.." <"..v.x..","..v.y..">";
			if (not (IsShiftKeyDown() and ChatFrameEditBox:IsVisible()) and MapNoteError<2) then
				MapNoteError = QuestLibrary_Make_MapNote(zone,title,0,info1,4,info2,8,icon,v.x,v.y);
				if (MapNoteError==2) then
					HC_PrintLogString(QUESTLIBRARY_TEXT_TOOLTIP5..zone..QUESTLIBRARY_TEXT_TOOLTIP6);
				end
			end
		end
		if ( IsShiftKeyDown() ) then
			if ( ChatFrameEditBox:IsVisible() ) then
				ChatFrameEditBox:Insert(text);
			end
		elseif ( QuestLibraryMN_Data_Notes and p and p[1]) then
			local continent, zone1 = QuestLibrary_MapNotes_GetZoneID(zone);
			ToggleWorldMap();
			SetMapZoom(continent, zone1);
		end
	end
end
--------------
-- 地图标记
--------------
local function MapNote_OnEnter(button, n_id)	
	local x, y = this:GetCenter();
	local x2, y2 = WorldMapButton:GetCenter();
	local anchor = "";
	if x > x2 then
		anchor = "ANCHOR_LEFT";
	else
		anchor = "ANCHOR_RIGHT";
	end

	local currentZone = QuestLibraryMN_Data_Notes[QuestLibrary_GetMapKey()];	
	if (not currentZone) then
		return;
	end
	local cNr = currentZone[n_id].ncol;
	WorldMapTooltip:SetOwner(button, anchor);
	WorldMapTooltip:SetText(currentZone[n_id].name, MapNote_Colors[cNr].r, MapNote_Colors[cNr].g, MapNote_Colors[cNr].b);
	if currentZone[n_id].inf1 ~= nil and currentZone[n_id].inf1 ~= "" then
		cNr = currentZone[n_id].in1c;
		WorldMapTooltip:AddLine(currentZone[n_id].inf1, MapNote_Colors[cNr].r, MapNote_Colors[cNr].g, MapNote_Colors[cNr].b);
	end
	if currentZone[n_id].inf2 ~= nil and currentZone[n_id].inf2 ~= "" then
		cNr = currentZone[n_id].in2c;
		WorldMapTooltip:AddLine(currentZone[n_id].inf2, MapNote_Colors[cNr].r, MapNote_Colors[cNr].g, MapNote_Colors[cNr].b);
	end
	WorldMapTooltip:AddDoubleLine(MAPNOTE_CREATEDBY, currentZone[n_id].creator, 0.79, 0.69, 0.0, 0.79, 0.69, 0.0);
	WorldMapTooltip:SetFrameStrata("TOOLTIP");
	WorldMapTooltip:Show();	
end

function MapNote_OnLeave()
	WorldMapTooltip:Hide();
	GameTooltip:Hide();	
end

local function MapNote_AssignPOI(index)	
	local POI = getglobal( "MapNotePOI" .. index );

	if ( not POI ) then
		POI = CreateFrame("Button", "MapNotePOI" .. index, WorldMapButton);		
		POI:SetWidth(16);
		POI:SetHeight(16);

		local texture = POI:CreateTexture("", "ARTWORK");
		texture:SetAllPoints();
		texture:SetTexture("Interface\\AddOns\\iWOWQuery\\Icon");		

		POI:SetScript("OnEnter", function ()
			MapNote_OnEnter(POI, index);
		end);
		POI:SetScript("OnLeave", MapNote_OnLeave);

		POI:SetFrameLevel(POI:GetFrameLevel() + 2);
		POI:EnableMouse(1);
		POI:SetID(index);
	end

	return POI;
end

function MapNote_WorldMapButton_Update()
	if ( MapNoteFU_Drawing ) then
		return;
	end

	MapNoteFU_Drawing = true;

	local width = WorldMapButton:GetWidth();
	local height = WorldMapButton:GetHeight();
	local key = QuestLibrary_GetMapKey();	
	local currentZone = QuestLibraryMN_Data_Notes[key];
	local xOffset,yOffset = 0;
	local POI, POITexture;
	local nNotes = 1;

	if ( currentZone ) then
		for i, value in ipairs(currentZone) do			
			POI = MapNote_AssignPOI(i);
			local xOffset = currentZone[i].xPos * width;
			local yOffset = -currentZone[i].yPos * height;			
			POI:ClearAllPoints();
			POI:SetPoint("CENTER", "WorldMapDetailFrame", "TOPLEFT", xOffset, yOffset);
			POI:Show();						
			nNotes = nNotes + 1;
		end		
	end

	local otherPOI = getglobal("MapNotePOI"..nNotes);
	while ( otherPOI ) do
		otherPOI:Hide();
		nNotes = nNotes + 1;
		otherPOI = getglobal("MapNotePOI"..nNotes);
	end	

	MapNoteFU_Drawing = nil;
end
-- 删除查询标记
function QuestLibraryMN_DeleteNote(n_id, key)
	local currentZone, lastEntry;	
	key = key or QuestLibrary_GetMapKey();								
	-- 获得地图的标记数
	currentZone = QuestLibraryMN_Data_Notes[key];
	lastEntry = QuestLibraryMN_Data_Notes:GetSize(currentZone);	

	if lastEntry ~= 0 and n_id <= lastEntry then
		currentZone[n_id] = QuestLibraryMN_Data_Notes:Clone(currentZone[lastEntry]);		
		currentZone[lastEntry] = nil;
	end	
	MapNote_WorldMapButton_Update();								
end