--if not Cartographer and not Cartographer3 then
--	ChatFrame1:AddMessage("|cffff0000Zygor Guides Viewer requires Cartographer or Cartographer3")
--end


--local Rock = nil  -- keep the partial Rock compatibility for now, but don't actually use it.
--local Cartographer_Notes = nil
--local Cartographer = nil
--local Cartographer3 = nil

ZygorGuidesViewer = LibStub("AceAddon-3.0"):NewAddon("ZygorGuidesViewer", "AceConsole-3.0","AceEvent-3.0","AceTimer-3.0")
ZGV = ZygorGuidesViewer

ZGV.revision = tonumber(string.sub("$Revision: 162 $", 12, -3))
ZGV.version = "3.0." .. ZGV.revision
ZGV.date = string.sub("$Date: 2009-04-15 22:02:40 -0400 (Wed, 15 Apr 2009) $", 8, 17)

local L = ZygorGuidesViewer_L("Main")
local DL = ZygorGuidesViewer_L("Detect")

ZYGORGUIDESVIEWER_COMMAND = "zygor"

BINDING_HEADER_ZYGORGUIDES = L["title"]
BINDING_NAME_ZYGORGUIDES_OPENGUIDE = L["toggle_window"]

local _,_,_,ver = GetBuildInfo();
local WotLK = (ver>=30000);

local me = ZygorGuidesViewer

me.L = L


local BZ = LibStub("LibBabble-Zone-3.0")
local BZL = BZ:GetUnstrictLookupTable()


me.icons = {
	["hilite"] = {	text = L["map_highlight"],		path = "Interface\\AddOns\\ZygorGuidesViewer\\Skin\\highlightmap",	width = 32, height = 32, alpha=1 },
	["hilitesquare"] = {	text = L["map_highlight"],		path = "Interface\\AddOns\\ZygorGuidesViewer\\Skin\\highlightmap_square",	width = 32, height = 32, alpha=1 },
}

me.CartographerDatabase = { }

me.StepLimit = 500;


local function ns(s)
	if s==true then return "true" end
	if s==false then return "false" end
	if s==nil then return "nil" end
	return s
end


local lua51
function me:OnInitialize() 
	
	self.db = LibStub("AceDB-3.0"):New("ZygorGuidesViewerSettings")

	self.db:RegisterDefaults({
		char = {
			starting = true,
			section = 1,
			step = 1,
		},
		profile = {
			debug = false,
			autosizemini = true,
			minimode = false,
			visible = true,
			hidecompleted = false,
			iconAlpha = 1,
			iconScale = .5,
			minicons = true,
			filternotes = true,
			minimapnotedesc = true,
			autoskip = true,
			showgoals = true,
			waypointaddon = "cart2",
			opacitymain = 1.0,
			opacitymini = 0.5
		}
	})
	
	--self.db:SetProfile("char/"..UnitName("player").." - "..GetRealmName())

	self.options = {
		type='group',
		name = L["name"],
		desc = L["desc"],
		handler = self,
		args = {
			desc = {
				order = 1,
				type = "description",
				name = L["desc"],
			},
			show = {
				name = L["opt_visible"],
				desc = L["opt_visible_desc"],
				type = 'toggle',
				get = "IsVisible",
				set = "SetVisible",
				order = 1.5,
				width = "double",
			},
			autoskip = {
				name = L["opt_autoskip"],
				desc = L["opt_autoskip_desc"],
				type = 'toggle',
				get = "IsAutoskip",
				set = "ToggleAutoskip",
				order = 2,
				width = "double",
			},
			main = {
				name = L["opt_group_main"],
				type = "group",
				inline = true,
				order = 3,
				args = {
					opacitymain = {
						name = L["opt_opacitymain"],
						desc = L["opt_opacitymain_desc"],
						type = 'range',
						get = "GetOpacityMain",
						set = "SetOpacityMain",
						min = 0,
						max = 1.0,
						isPercent = true,
						step = 0.01,
						bigStep = 0.1,
						--stepBasis = 0,
						--width="double",
						order=1,
					},
				},
			},
			mini = {
				name = L["opt_group_mini"],
				type = "group",
				inline = true,
				order = 4,
				args = {
					opacitymini = {
						name = L["opt_opacitymini"],
						desc = L["opt_opacitymini_desc"],
						type = 'range',
						get = "GetOpacityMini",
						set = "SetOpacityMini",
						min = 0,
						max = 1.0,
						isPercent = true,
						step = 0.01,
						bigStep = 0.1,
						--stepBasis = 0,
						--width="double",
						order=2,
					},
					autosizemini = {
						name = L["opt_autosizemini"],
						desc = L["opt_autosizemini_desc"],
						type = 'toggle',
						get = "IsAutosizeMini",
						set = "ToggleAutosizeMini",
						order=3,
						width="double",
					},
					miniresizeup = {
						name = L["opt_miniresizeup"],
						desc = L["opt_miniresizeup_desc"],
						type = 'toggle',
						get = "IsMiniResizeUp",
						set = "ToggleMiniResizeUp",
						order=3,
						width="double",
					},
					showgoals = {
						name = L["opt_showgoals"],
						desc = L["opt_showgoals_desc"],
						type = 'toggle',
						get = "IsShowgoals",
						set = "ToggleShowgoals",
						order=5,
						width="double",
					},
					minimapnotedesc = {
						name = L["opt_minimapnotedesc"],
						desc = L["opt_minimapnotedesc_desc"],
						type = 'toggle',
						get = "IsMiniWaypointDesc",
						set = "ToggleMiniWaypointDesc",
						order = 6,
						width="double",
					},
				},
			},
			debug = {
				hidden = true,
				name = L["opt_debug"],
				desc = L["opt_debug_desc"],
				type = 'toggle',
				get = "IsDebugMode",
				set = "ToggleDebugMode",
				order=-10,
			},
			--[[
			mapicons = {
				name = "Show map icons",
				desc = "Show icons on the world map",
				type = 'toggle',
				set = "ToggleShowingMapIcons",
				get = "IsShowingMapIcons",
				order = 1,
			},
			toggle = {
				name = Cartographer.L["Enabled"],
				desc = Cartographer.L["Suspend/resume this module."],
				type  = 'toggle',
				order = -1,
				get   = function() return Cartographer:IsModuleActive(self) end,
				set   = function() Cartographer:ToggleModuleActive(self) end,
			}	
			]]--
		},
	}
	self.optionsmap = {
		name = L["opt_group_map"],
		desc = L["opt_group_map_desc"],
		type = 'group',
		order = 1,
		--hidden = true,
		handler = self,
		args = {
			desc = {
				order = 1,
				type = "description",
				name = L["opt_group_map_desc"],
			},
			waypoints = {
				name = L["opt_group_map_waypointing"],
				desc = L["opt_group_map_waypointing_desc"],
				type = 'select',
				values = {
					--builtin=L["opt_group_addons_builtin"],
					none=L["opt_group_addons_none"],
					tomtom=L["opt_group_addons_tomtom"],
					cart2=L["opt_group_addons_cart2"],
					--cart3=L["opt_group_addons_cart3"],
					--metamap=L["opt_group_addons_metamap"],
				},
				get = "GetWaypointAddon",
				set = "SetWaypointAddon",
				order = 1,
			},
			minicons = {
				name = "Show minimap icons",
				desc = "Show icons on the minimap",
				type = 'toggle',
				get = "IsShowingMinimapIcons",
				set = "ToggleShowingMinimapIcons",
				disabled = function() return self.db.profile.waypointaddon=="none" end,
				order = 3,
				width="double",
			},
			transparency = {
				name = "Icon alpha",
				desc = "Alpha transparency of map note icons",
				type = 'range',
				min = 0.1,
				max = 1,
				step = 0.01,
				bigStep = 0.05,
				isPercent = true,
				get = "GetIconAlpha",
				set = "SetIconAlpha",
				disabled = function() return not self:IsShowingMinimapIcons() or (self.db.profile.waypointaddon~="cart2") end,
				order = 4
			},
			scale = {
				name = "Icon size",
				desc = "Size of the icons on the map",
				type = 'range',
				min = 0.5,
				max = 2,
				step = 0.01,
				bigStep = 0.05,
				isPercent = true,
				get = "GetIconScale",
				set = "SetIconScale",
				disabled = function() return not self:IsShowingMinimapIcons() or (self.db.profile.waypointaddon~="cart2") end,
				order = 5
			},
		}
	}
	self.optionsdebug = {
		name = L["opt_debugging"],
		hidden = function() return not self.db.profile.debug end,
		desc = L["opt_debugging_desc"],
		type = 'group',
		order=-9,
		handler = self,
		args = {
			test = {
				type = 'execute',
				name = 'test',
				desc = 'Test whatever\'s being tested.',
				func = "Test",
				order=21,
			},
		},
	}
	
	self.optionsprofile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)

	self.CurrentStepNum = self.db.char.step
	self.CurrentSectionNum = self.db.char.section

	self.QuestCacheTime = 0
	self.QuestCacheUndertimeRepeats = 0
	self.StepCompletion = {}
	self.recentlyCompletedQuests = {}
	self.recentlyAcceptedQuests = {}
	self.LastSkip = 0
	self.AutoskipTemp = true

	self.TomTomWaypoints = {}
	
	--LibSimpleOptions.AddOptionsPanel("Zygor's Guide",function(self) MakeOptionsControls(self,ZGV.options,ZGV) end)
	--LibSimpleOptions.AddSuboptionsPanel("Zygor's Guide",ZGV.options.args.map.name, function(self) MakeOptionsControls(self,ZGV.options.args.map,ZGV) end)
	--LibSimpleOptions.AddSuboptionsPanel("Zygor's Guide",ZGV.options.args.addons.name, function(self) MakeOptionsControls(self,ZGV.options.args.addons,ZGV) end)
	--LibSimpleOptions.AddSlashCommand("Zygor's Guide","/zygoropt")

	LibStub("AceConfigDialog-3.0"):SetDefaultSize("ZygorGuidesViewer", 600, 400)
	LibStub("AceConfig-3.0"):RegisterOptionsTable("ZygorGuidesViewer", self.options, ZYGORGUIDESVIEWER_COMMAND );
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ZygorGuidesViewer", self.options.name)
	LibStub("AceConfig-3.0"):RegisterOptionsTable("ZygorGuidesViewer-Map", self.optionsmap, "zgmap");
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ZygorGuidesViewer-Map", self.optionsmap.name, self.options.name)
	LibStub("AceConfig-3.0"):RegisterOptionsTable("ZygorGuidesViewer-Debug", self.optionsdebug, "zgdebug");
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ZygorGuidesViewer-Debug", self.optionsdebug.name, self.options.name)
	LibStub("AceConfig-3.0"):RegisterOptionsTable("ZygorGuidesViewer-Profile", self.optionsprofile, "zgprofile");
	LibStub("AceConfigDialog-3.0"):AddToBlizOptions("ZygorGuidesViewer-Profile", self.optionsprofile.name, self.options.name)

	self:Echo(L["initialized"]);
	lua51 = loadstring("return function(...) return ... end") and true or false
end

function me:OnEnable()
	self:Debug("enabling")

	local faction = UnitFactionGroup("player")

	if not IsAddOnLoaded("ZygorGuides"..faction) then
		loaded,reason = LoadAddOn("ZygorGuides"..faction)
		if not loaded then
			self:Echo ("Unable to load the "..faction.." Guide: "..reason)
		end
	end
	
	self.Guide = getglobal("ZygorGuidesViewer_"..faction.."Guide")

	if self.Guide then
		self.MapNotes = _G["ZygorGuides_"..faction.."Mapnotes"]

		ZYGORGUIDESVIEWERFRAME_TITLE = "Zygor's 1-80 "..faction.." Leveling Guide";
		self:Echo (faction.." guide loaded.")
		--[[
		if not self:IsWaypointAddonReady() then
			-- autodetect another waypointing addon
			self:Debug("detecting another addon")
			do
				local addons = {"cart3","cart2","tomtom","none"}
				local i,a
				for i,a in pairs(addons) do
					if self:IsWaypointAddonReady(a) then
						self:Debug("activating "..a)
						self:SetWaypointAddon(nil,a)
						break
					end
				end
			end
		else
			self:Debug("map addon OK")
		end

		self:RegisterNotes()
		]]

		--self:ScheduleRepeatingTimer("UpdateMinimapArrow", 0.1)
		self:ScheduleRepeatingTimer("TryToCompleteStep", 1)
		self.notetimer = self:ScheduleRepeatingTimer("RegisterNotes", 1)
		self:RegisterEvent("MINIMAP_UPDATE_ZOOM")

		ZygorGuidesViewerMapArrow:SetScale(0.66)
		ZygorGuidesViewerMapArrow:SetFrameLevel(10)

		-- retrieve profile settings
		if not self.db.char['section'] or not self.Guide[self.db.char['section']] or self.db.char["starting"] then
			self.db.char['section'],self.db.char['step'] = self:FindDefaultSection(0)
		end
		self:SetSection(self.db.char["section"])
		if #self.CurrentSection.steps<self.db.char["step"] or not self.db.char["step"] then 
			self.db.char['step'] = 1
		end
		self:FocusStep(self.db.char['step'])
	else
		self:Echo (UnitFactionGroup("player").." guide missing.")
	end

	self.db.char["starting"] = false

	if self.db.profile["visible"] then self:ToggleFrame() end

	ZygorGuidesViewerMapIcon:Show()
	self:SetOpacityMain(nil,self:GetOpacityMain())
	self:SetOpacityMini(nil,self:GetOpacityMini())

	self:Debug("enabled")

	eventlistener = self.RegisterEvent
		
	eventlistener(self,"QUEST_WATCH_UPDATE")
	eventlistener(self,"QUEST_LOG_UPDATE")
	eventlistener(self,"QUEST_COMPLETE")
	--eventlistener(self,"QUEST_FINISHED") --unused
	--self:AddEventListener("QUEST_PROGRESS")
	--eventlistener(self,"QUEST_ITEM_UPDATE") --unused
	eventlistener(self,"UNIT_QUEST_LOG_CHANGED")
	eventlistener(self,"CHAT_MSG_SYSTEM")
	eventlistener(self,"UI_INFO_MESSAGE")

end

function me:OnDisable()
--	self:UnregisterAllEvents()

	UnsetWaypointAddon();

	ZygorGuidesViewerMapIcon:Hide()
	ZygorGuidesViewerFrame:Hide()
	ZygorGuidesViewerMiniFrame:Hide()
end

function me:SetSection(num)
	self:Debug("SetSection "..num)
	if num<1 then num=1 end
	if num>#self.Guide then num=#self.Guide end
	self.CurrentSectionNum = num
	self.db.char["section"] = num
	self.CurrentSection = self.Guide[num]
	self:UpdateMainFrame()
end

function me:FindDefaultSection(mode)
	-- mode decides the in-depth analysis performed.
	-- 0/nil = just the starting section for the player's race
	-- 1 = that and follow to the first step matching player's level

	if not mode then mode = 0 end

	local x,race = UnitRace("player")
	local x,class = UnitClass("player")

	if (class=="DeathKnight") then
		race="DeathKnight"
	end

	local findDefault = function(race)
		local i,sec
		for i,sec in pairs(self.Guide) do
			if sec.defaultfor == race then
				return i
			end
		end
		return 1
	end


	if mode == 0 then
		return findDefault(race),1
	elseif mode > 0 then
		local defsec
		local level = UnitLevel("player")
		if level<=13 then
			defsec = findDefault(race)
			for j,step in pairs(self.Guide[defsec].steps) do
				if step.level == level then
					return defsec,j
				end
			end
		else
			local i,sec,j,step
			for i,sec in pairs(self.Guide) do
				for j,step in pairs(sec.steps) do
					if step.level == level then
						return i,j
					end
				end
			end
		end
	end
	return 1,1
end

local math_floor = math.floor
local function round(num, digits)
	-- banker's rounding
	local mantissa = 10^digits
	local norm = num*mantissa
	norm = norm + 0.5
	local norm_f = math_floor(norm)
	if norm == norm_f and (norm_f % 2) ~= 0 then
		return (norm_f-1)/mantissa
	end
	return norm_f/mantissa
end
function me:Test (arg1,arg2)
	local a={GetMapZones(GetCurrentMapContinent())}
	local x,y = GetPlayerMapPosition("player")
	local id = round(x*10000, 0) + round(y*10000, 0)*10001
	self:Print("You're in "..a[GetCurrentMapZone()].." at Cart2 coords "..id)
end

function me:Echo (s)
	--if not self.db.profile.silent then 
	self:Print(ns(s))
	--end
end

function me:Debug (s)
	if self.db.profile.debug then
		self.DebugI = (self.DebugI or 0) + 1
		self:Echo('|cffa0a0a0#' .. self.DebugI .. ': ' .. ns(s))
	end
end

function me:getXY(id)
	self:Debug(id)
	return (id % 10001)/10000, math.floor(id / 10001)/10000
end

function me:FocusStep(num)
	if not num then return end
	self:Debug("FocusStep "..num)
	self.CurrentStepNum = num
	self.db.char["step"] = num
	self.CurrentStep = self.CurrentSection["steps"][num]
	self:PrepareStepCompletion()
	self:HighlightCurrentStep()
	self:UpdateMainFrame()
	self:UpdateMiniFrame()
	self:UpdateCartographerExport()
	self:SetWaypoint()
	self:UpdateMinimapArrow(true)
	if (self:IsStepComplete() and self.LastSkip~=0) then self.AutoskipTemp=false else self.AutoskipTemp=true end
	self.LastSkip=0
end

function me:TryToCompleteStep()
	while self.CurrentStep and self.AutoskipTemp and self.db.profile.autoskip and self:IsStepComplete() do
		self.recentlyCompletedQuests = {} -- forget it! We're skipping the step, already.
		self.recentlyAcceptedQuests = {}
		self.recentlyHomeChanged = false
		self.recentlyDiscoveredFlightpath = false
		self:SkipStep(1)
	end
	self:UpdateMiniFrame()
end

function me:GetTextualCompletion()
	if not self.db.profile.showgoals then return "" end
	local s = "|n---"
	if (#self.StepCompletion > 0) then
		for i,v in pairs(self.StepCompletion) do
			s = s .. "|n" .. self:GetTextualCompletionGoal(i)
		end
	end
	if (self.CurrentStep.requirement) then
		s = s .. "|n---|n"
		if self:PlayerMeetsRequirements() then
			s = s .. "|cff88ffaa"
		else
			s = s .. "|cffff8888"
		end
		s = s .. L["stepreq"]..table.concat(self.CurrentStep.requirement,L["stepreqor"])
	end
	return s
end

function me:GetTextualCompletionGoal(goalIndex)
	local goal = self.StepCompletion[goalIndex]
	local desc = ""
--	self:Debug("GetTextualCompletionGoal goalindex "..goalIndex)
	if goal.questaccepted then
		local i,j,title,id = string.find(goal.questaccepted,"(.*)##([0-9]+)$")
		desc = L["stepgoal_questaccepted"]:format(title or goal.questaccepted)
	end
	if goal.questturnedin then
		local i,j,title,id = string.find(goal.questturnedin,"(.*)##([0-9]+)$")
		desc = L["stepgoal_questturnedin"]:format(title or goal.questturnedin)
	end
	if goal.questgoal then
		local quest,qgoal,count = goal.questgoal[1],goal.questgoal[2],goal.questgoal[3]
		if not count then error("Step "..self.CurrentStepNum.." section "..self.CurrentSectionNum.." has bad questgoal.") end
		if quest=="*" then
			quest = self:ImproviseQuestTitle(qgoal)
		end
		local questIndex
		local questF = self:FindData(self.quests,"title",quest)
		if questF then questIndex=questF.index end
		if questIndex and questIndex>0 then
			local goals,goalsNamed = self:GetQuestLeaderBoards(questIndex)
			local questGoal = goalsNamed[qgoal]
			if questGoal then
				desc = L["stepgoal_questgoal"]:format(questGoal.leaderboard)
			else
				desc = qgoal .. ": ?"
			end
		else
			desc = qgoal .. ": 0"
		end
	end
	if goal.level then
		local percent
		if (UnitLevel("player")<goal.level-1) then
			percent = 0
		else
			percent = floor(UnitXP("player")/UnitXPMax("player") * 100)
			if self:IsStepGoalComplete(goalIndex) then
				percent = 100
			end
		end
		desc = L["stepgoal_level"]:format(goal.level,percent)
	end
	if goal.home then
		desc = L["stepgoal_home"]:format(goal.home)
	end
	if goal.flightpath then
		desc = L["stepgoal_flightpath"]:format(goal.flightpath)
	end
	if goal.location then
		if goal.location.x then
			desc = L["stepgoal_location"]:format(goal.location.mapzone, goal.location.x, goal.location.y)
		else
			desc = L["stepgoal_location_onlyzone"]:format(goal.location.mapzone or "?")
		end
	end
	if goal.item then
		desc = L["stepgoal_item"]:format(goal.item[1],GetItemCount(goal.item[1]),goal.item[2])
	end

	local complete,ext = self:IsStepGoalComplete(goalIndex)
	if complete then
		desc = "|cffbbffbb" .. desc .. " " .. L["stepgoal_complete"] .. "|r"
	elseif ext then
		desc = "|cffffbbbb" .. desc .. "|r"
	else
		desc = "|cffaaaaaa" .. desc .. "|r"
	end
	return desc
end

-- DEPRECATED
function me:GetTextualCompletionGoalShort(goalIndex)
	local goal = self.StepCompletion[goalIndex]
	local completecolor = " |cffbbffbb"
	local incompletecolor = " |cffffbbbb"

	local complete,ext = self:IsStepGoalComplete(goalIndex)

	--	self:Debug("GetTextualCompletionGoal goalindex "..goalIndex)
	if goal.questaccepted or goal.questturnedin or goal.home or goal.location or goal.flightpath then
		return (complete and completecolor..L["stepgoalshort_complete"] or incompletecolor..L["stepgoalshort_incomplete"]) .."|r"
	end
	if goal.questgoal then
		local quest,goal,count = goal.questgoal[1],goal.questgoal[2],goal.questgoal[3]
		if quest=="*" then
			quest = self:ImproviseQuestTitle(goal)
		end
		local questIndex = self.questIndicesByTitle[quest]
		if questIndex and questIndex>0 then
			local goals,goalsNamed = self:GetQuestLeaderBoards(questIndex)
			local questGoal = goalsNamed[goal]
			if questGoal then
				return (complete and completecolor..L["stepgoalshort_complete"] or incompletecolor..L["stepgoalshort_questgoal"]:format(questGoal.num,questGoal.needed)) .."|r"
			else
				return ""
			end
		else
			return ""
		end
	end
	if goal.level then
		local percent = floor(UnitXP("player")/UnitXPMax("player") * 100)
		if self:IsStepGoalComplete(goalIndex) then
			percent = 100
		end
		return (complete and completecolor..L["stepgoalshort_complete"] or incompletecolor..L["stepgoal_level"]:format(percent)) .."|r"
	end
	return ""
end

function me:ImproviseQuestTitle(goal)
	if not self.quests then return "" end
	for i,q in pairs(self.quests) do
		if q.goals then
			for j,g in pairs(q.goals) do
				if g.item==goal then
					return q.title
				end
			end
		end
	end
	return ""
end

-- returns: true = complete, false = incomplete
-- second return: true = completable, false = incompletable
function me:IsStepGoalComplete(goalIndex)
	local goal = self.StepCompletion[goalIndex]
	if not goal then return false end
	if goal.questaccepted then
		--[[
		-- Check for "turn in, accept" pair of the same quest - a workaround for "turn in part 1, accept part 2" steps
		--  Deprecated by introduction of questacceptedid
		if self.questIndicesByTitle[goal.questaccepted] and goalIndex>1 then
			local i
			for i = goalIndex-1, 1, -1 do
				if self.StepCompletion[i].questturnedin==goal.questaccepted and not self:IsStepGoalComplete(i) then return false,true end
			end
		end
		]]--
		local i,j,title,id = string.find(goal.questaccepted,"(.*)##([0-9]+)$")
		if id then
			id = tonumber(id)
			return (self:FindData(self.quests,"id",id) or self.recentlyAcceptedQuests[id]) and true or false, true
		else
			return (self:FindData(self.quests,"title",goal.questaccepted) or self.recentlyAcceptedQuests[goal.questaccepted]) and true or false, true
		end
	end
	if goal.questturnedin then
		local i,j,title,id = string.find(goal.questturnedin,"(.*)##([0-9]+)$")
		if id then
			id = tonumber(id)
			local q = self:FindData(self.quests,"id",id)
			return self.recentlyCompletedQuests[id], q and (q.complete or #q.goals==0)
		else
			local q = self:FindData(self.quests,"title",goal.questturnedin)
			return self.recentlyCompletedQuests[goal.questturnedin], q and (q.complete or #q.goals==0)
		end
	end
	if goal.questgoal then
		local quest,goal,count = goal.questgoal[1],goal.questgoal[2],goal.questgoal[3]
		if quest=="*" then
			quest = self:ImproviseQuestTitle(goal)
		end
		local questData = self:FindData(self.quests,"title",quest)
		if questData and questData.index>0 then
			local goals,goalsNamed = self:GetQuestLeaderBoards(questData.index)
			local questGoal = goalsNamed[goal]
			--self:Debug("IsStepComplete: quest goal "..goal)
			if questGoal then
				if questGoal.complete then
					return true
				else
				--self:Debug("Not yet completed: "..questGoal.num.."/"..questGoal.needed)
					return false, true
				end
			else
				--self:Debug("No goal "..goal)
				return false, false
			end
		else
			--self:Debug("No quest "..quest)
			return false, false
		end
	end
	if goal.level then
		return UnitLevel("player")>=tonumber(goal.level), true
	end
	if goal.location then
		if GetZoneText()~=goal.location.mapzone then return false,false end
		if goal.location.x then
			local x,y = GetPlayerMapPosition("player")
			local dist = goal.location.dist/100 or 0.01
			return abs(x-goal.location.x/100)<=dist and abs(y-goal.location.y/100)<=dist , true
		else
			return true,true
		end
	end
	if goal.home then
		--return GetBindLocation("player")==goal.home, true  -- didn't work well
		return self.recentlyHomeChanged, true
	end
	if goal.flightpath then
		--return GetBindLocation("player")==goal.home, true  -- didn't work well
		return self.recentlyDiscoveredFlightpath, true
	end
	if goal.item then
		return GetItemCount(goal.item[1])>=goal.item[2], true
	end
	return false,false
end

function me:PlayerMeetsRequirements(step)
	if not step then step=self.CurrentStep end
	if not step.requirement then return true end
	local r,race = UnitRace("player")
	local c,class = UnitClass("player")
	local raceclass = strlower(race.." "..class)
	local match = false
	for i,v in pairs(step.requirement) do if string.find(raceclass,strlower(v)) then match=true end end
	return match
end

function me:IsStepComplete()
	--self:Debug("Step complete check")
	if not self.CurrentStep then return false end
	if self.CurrentStep.requirement and not self:PlayerMeetsRequirements() then return true end
	if (#self.StepCompletion > 0) then
		for i,v in pairs(self.StepCompletion) do
			if not self:IsStepGoalComplete(i) then
				return false
			end
		end
		return true
	else
		return false
	end
end

function me:PrepareStepCompletion()
	self.StepCompletion = {}
	if self.CurrentStep.completion then
		for i,v in pairs(self.CurrentStep.completion) do
			if v.questaccepted then
				v.questaccepted = string.gsub(v.questaccepted," part [1-9]$","")
			end
			if v.questturnedin then
				v.questturnedin = string.gsub(v.questturnedin," part [1-9]$","")
			end
			if v.location and not v.location.mapzone and self.CurrentStep.mapnote and self.iconsregistered then
				self:Debug("Dist only!")
				v.location.mapzone = self.CurrentStep.mapzone
				v.location.x,v.location.y = self:getXY(self.CurrentStep.mapnote)
				v.location.x,v.location.y = v.location.x*100,v.location.y*100
			end
			self.StepCompletion[#self.StepCompletion+1] = v
		end
	end
	self:Dump(self.StepCompletion)
end

function me:FindMatchingGoal(s)
	local verb,pronoun
	quest = string.match(s,"Accept.*`(.*)'")
	if quest then
		quest = string.gsub(quest," part [1-9]$","")
		for i=1, #self.CurrentStep.completion, 1 do if self.CurrentStep.completion[i].questaccepted == quest then return i end end
	end
	quest = string.match(s,"Turn in.*`(.*)'")
	if quest then
		quest = string.gsub(quest," part [1-9]$","")
		for i=1, #self.CurrentStep.completion, 1 do if self.CurrentStep.completion[i].questturnedin == quest then return i end end
	end
	level = tonumber(string.match(s,"Grind to level ([0-9]+)"))
	if level then
		for i=1, #self.CurrentStep.completion, 1 do if self.CurrentStep.completion[i].level == level then return i end end
	end
	return nil
end

function me:RegisterNotes()
	-- use for pre-registering. Cartographer needs that, while TomTom does not.
	if self.iconsregistered then return end
	if not self.iconregistryretries then self.iconregistryretries=0 end
	if self.iconregistryretries==3 then
		self:Print(L["waypointaddon_fail"]:format(self.optionsmap.args.waypoints.values[self.db.profile.waypointaddon]))
	end
	if self.iconregistryretries>3 then return end
	self.iconregistryretries = self.iconregistryretries + 1

	if not self:IsWaypointAddonReady() then return end

	self:Print(L["waypointaddon_connecting"]:format(self.optionsmap.args.waypoints.values[self.db.profile.waypointaddon]))

	if self.db.profile.waypointaddon=="tomtom" then
		if not self.db.profile.filternotes then
			self:Print("Creating all waypoints for TomTom. This may take a while.")
			local contid,zoneid
			for zone in pairs(self.MapNotes) do
				local zoneTr = BZL[zone]
				contid,zoneid = self:GetMapZoneNumbers(zoneTr)
				self:Debug("contid="..ns(contid).." zoneid="..ns(zoneid).." for "..ns(zoneTr))
				if contid and zoneid and (type(self.MapNotes[zone])=="table") then
					if (TomTom:GetMapFile(contid,zoneid)) then
						for note,mapnote in pairs(self.MapNotes[zone]) do
							x,y = self:getXY(note)
							--self:Debug("x="..ns(x).." y="..ns(y))
							if x and y then
								--self:Debug(GetCurrentMapContinent().." "..ns(note).." "..ns(zone).." x"..ns(x).." y"..tostring(y))
								self.TomTomWaypoints[#self.TomTomWaypoints+1] = TomTom:AddZWaypoint(
									contid,zoneid,x*100,y*100,
									self.MapNotes[zone][note].title, --desc
									false, --persistent
									true, true, --minimap,world
									nil,true, --callbacks,silent
									(zone==self.CurrentStep.mapzone and note==self.CurrentStep.mapnote) --arrow
								)
							end
						end
					else
						self:Print("No map data for continent id "..ns(contid)..", zone id "..ns(zoneid)..", zone "..ns(zone)..", please report.")
					end
				end
			end
		end
	elseif self.db.profile.waypointaddon=="cart2" then
		self:Debug("registering database "..#self.MapNotes)
		Cartographer_Notes:RegisterNotesDatabase('ZygorGuides', self.MapNotes, self)
		self:Debug("registered database")

		self:Debug("registering icons")
		if not self.iconsregistered then
			for k,v in pairs(self.icons) do
				Cartographer_Notes:RegisterIcon(k, v)
			end
		end
	end

	self:Print(L["waypointaddon_connected"]:format(self.optionsmap.args.waypoints.values[self.db.profile.waypointaddon]))
	self:Debug("registered icons")
	self.iconsregistered = true
	self.iconregistryretries = 0

	self:SetWaypoint()
end

function me:ClearTomTomWaypoints()
	for i=1,#self.TomTomWaypoints,1 do
		TomTom:ClearWaypoint(self.TomTomWaypoints[i])
	end
	self.TomTomWaypoints = {}
end

function me:GetMapZoneNumbers(zonename)
	if zonename=="Plaguelands: The Scarlet Enclave" then return -7777,1 end
	for cont in pairs{GetMapContinents()} do
		for zone,name in pairs{GetMapZones(cont)} do
			if name==zonename then
				return cont,zone
			end
		end
	end
	return 0
end

-- only for TomTom support, Astrolabe bundled
function me:GetMapZoneFile(zonename)
	for cont in pairs{GetMapContinents()} do
		for zone,name in pairs{GetMapZones(cont)} do
			if name==zonename then
				return DongleStub("Astrolabe-0.4").ContinentList[cont][zone]
			end
		end
	end
	return ""
end


function me:SetWaypoint()
	local x,y
	if self:IsWaypointAddonEnabled("tomtom") then
		self:Debug("placing TomTom waypoint")
		TomTom.profile.persistence.cleardistance = 0
		if self.CurrentStep.mapnote then
			if self.db.profile.filternotes then
				-- only one shown, just delete it and show another
				-- first, quietly remove all waypoints
				self:ClearTomTomWaypoints()
				local contid,zoneid
				contid,zoneid = self:GetMapZoneNumbers(BZL[self.CurrentStep.mapzone])
				self:Debug("contid:"..ns(contid).." zoneid:"..ns(zoneid))
				local mapnote = self.MapNotes[self.CurrentStep.mapzone][self.CurrentStep.mapnote]
				if mapnote then
					x,y = self:getXY(self.CurrentStep.mapnote)
					self.TomTomWaypoints[1] = TomTom:AddZWaypoint(contid,zoneid,x*100,y*100,mapnote.title,false,true,true,nil,true,true)
				else
					self:Print("Mapnote for current step not found.")
				end
			else
				-- all shown? need to search.
				x,y = self:getXY(self.CurrentStep.mapnote)
				x = x*100
				y = y*100
				local uid,data
				local zonefile = self:GetMapZoneFile(self.CurrentStep.mapzone)
				--self:Debug(zonefile)
				--self:Debug(type(TomTom.waypoints[zonefile]))
				if type(TomTom.waypoints[zonefile])~="table" then return end
				for uid in pairs(TomTom.waypoints[zonefile]) do
					data = TomTom.waypoints[uid]
					if (type(data)=="table") and (math.abs(data.x-x) + math.abs(data.y-y) < 0.5) then
						TomTom:SetCrazyArrow(uid, TomTom.profile.arrow.arrival, self.MapNotes[self.CurrentStep.mapzone][self.CurrentStep.mapnote].title, false,true,true,nil,true,true)
						return
					end
				end
				--else
				TomTomCrazyArrow:Hide()
				return
			end
		else
			if self.db.profile.filternotes then
				self:ClearTomTomWaypoints()
			end
			TomTomCrazyArrow:Hide()
		end

	elseif self:IsWaypointAddonEnabled("cart2") then
		self:Debug("Setting waypoint")
		local mapnote,mapzone = self.CurrentStep["mapnote"],self.CurrentStep["mapzone"]
		--self:Debug(self.CurrentStep.mapnote)
		local queue = Cartographer_Waypoints.Queue
		for i,v in ipairs(queue) do
			if v and v.Db=="ZygorGuides" then
				table.remove(queue,i)
			end
		end
		if mapnote and mapzone and Cartographer_Notes.externalDBs then
			Cartographer_Waypoints:SetNoteAsWaypoint(mapzone,mapnote)
		end
	end
end

local rotateMinimap = GetCVar("rotateMinimap") == "1"
--local maxdist = 0.001186
local MinimapSize = { -- radius of minimap
	indoor = {
		[0] = 150,
		[1] = 120,
		[2] = 90,
		[3] = 60,
		[4] = 40,
		[5] = 25,
	},
	outdoor = {
		[0] = 233 + 1/3,
		[1] = 200,
		[2] = 166 + 2/3,
		[3] = 133 + 1/6,
		[4] = 100,
		[5] = 66 + 2/3,
	},
}

local opx,opy = 0,0
local ozoom = -1

function me:UpdateMinimapArrow(force)
	local px,py = GetPlayerMapPosition("player")
	local diffPos = (opx~=px or opy~=py)

	local zoom=Minimap.GetZoom()
	local diffZoom = (zoom~=ozoom)

	opx,opy = px,py
	ozoom = zoom

	if not diffPos and not diffZoom and not force then return end

	ZygorGuidesViewerMapArrow:Hide()
	ZygorGuidesViewerMapArrowFrame:Hide()
	if not self.CurrentStep then return end

	local maxdist = MinimapSize.outdoor[Minimap.GetZoom()]
	local mapnote = self.CurrentStep["mapnote"]
	local mapzone = self.CurrentStep["mapzone"]

	if not mapnote or not mapzone then return end

	local zones = {GetMapZones(GetCurrentMapContinent())}
	if zones[GetCurrentMapZone()] ~= self.CurrentStep["mapzone"] then return end

	if not self:IsWaypointAddonEnabled("cart2") then return end
	
	local nx,ny = self:getXY(mapnote)
	--	self:Echo(nx..' '..ny)
	local dist = Cartographer:GetDistanceToPoint(nx,ny,mapzone)
	if not dist then return end

	local diffX, diffY = px - (nx or 0), py - (ny or 0)
	--local dist = diffX*diffX + diffY*diffY

	if dist>=maxdist then
		ZygorGuidesViewerMapArrow:Show()
		ZygorGuidesViewerMapArrowFrame:Show()

		rad = -math.atan2(diffY*2/3, diffX) -(rotateMinimap and -MiniMapCompassRing:GetFacing() or 0)
		--	local direction = (rotateMinimap and -MiniMapCompassRing:GetFacing() or 0) + 1.5708
		--	local val = ((rad - direction)*_54_div_math_pi + 108) % 108

		--	ChatFrame1:AddMessage(rad)
		ZygorGuidesViewerMapArrow:SetPosition(0.085 - 0.07 * math.cos(rad), 0.085 - 0.07 * math.sin(rad), 0)
		ZygorGuidesViewerMapArrow:SetFacing(rad+1.57075)
		ZygorGuidesViewerMapArrowFrame:ClearAllPoints()
		ZygorGuidesViewerMapArrowFrame:SetPoint("CENTER","ZygorGuidesViewerMapArrow","CENTER",-60 * math.cos(rad), -60 * math.sin(rad), 0)
	end
end

function me:UpdateCartographerExport()
	if ((self.db.profile.waypointaddon~="cart2") and (self.db.profile.waypointaddon~="cart3")) or (not self.iconsregistered) then return end

	Cartographer_Notes:MINIMAP_UPDATE_ZOOM()
	Cartographer_Notes:UpdateMinimapIcons()
	Cartographer_Notes:RefreshMap()
end

function me:InitializeDropDown()
	if not self.Guide then return end
	
	for i = 1, #self.Guide, 1 do
--		ChatFrame1:AddMessage(section)
		local info = {}
		info.text = self.Guide[i]["sectiontitle"]
		info.value = i
		info.func = ZGVFSectionDropDown_Func
		if (self.CurrentSectionNum == i) then
			info.checked = 1;
		else
			info.checked = nil;
		end
		info.button = 1;
--		if (i == 1) then
--			info.isTitle = 1;
--		end
		UIDropDownMenu_AddButton(info);
	end
	if WotLK then
		UIDropDownMenu_SetText(ZygorGuidesViewerFrame_SectionDropDown, self.CurrentSection["sectiontitle"]);
	else
		UIDropDownMenu_SetText(self.CurrentSection["sectiontitle"], ZygorGuidesViewerFrame_SectionDropDown);
	end
end

function me:SectionChange(name)
	ZygorGuidesViewer:SetSection(this.value)
	ZygorGuidesViewer:FocusStep(1)
	if WotLK then
		UIDropDownMenu_SetText(ZygorGuidesViewerFrame_SectionDropDown, ZygorGuidesViewer.CurrentSection["sectiontitle"]);
	else
		UIDropDownMenu_SetText(ZygorGuidesViewer.CurrentSection["sectiontitle"], ZygorGuidesViewerFrame_SectionDropDown);
	end
end

function me:HighlightCurrentStep()
	if not ZygorGuidesViewerFrame or not ZygorGuidesViewerFrame:IsVisible() or not self.CurrentStepNum then return end
	ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrame:ClearAllPoints()
	ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrame:SetPoint("TOP","ZygorGuidesViewerFrameStep"..self.CurrentStepNum,"TOP",0,0)
	ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrame:SetAlpha(0.7)
	ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrame:SetHeight(getglobal("ZygorGuidesViewerFrameStep"..self.CurrentStepNum):GetHeight()+4)
	if self:PlayerMeetsRequirements() then
		ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrameTexture:SetVertexColor(1.0,1.0,1.0)
	else
		ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrameTexture:SetVertexColor(1.0,0.5,0.5)
	end
end

function me:UpdateMiniFrame()
	if self.Guide and ZygorGuidesViewerMiniFrame then

		if not ZygorGuidesViewerMiniFrame:IsVisible() then return end

		-- layout goes first
		ZygorGuidesViewerMiniFrame_TitleBar:ClearAllPoints()
		ZygorGuidesViewerMiniFrame_TitleBarDragger:SetAllPoints(ZygorGuidesViewerMiniFrame_TitleBar)
		ZygorGuidesViewerMiniFrame_TitleBar_Divider:ClearAllPoints()
		ZygorGuidesViewerMiniFrame_Text:ClearAllPoints()
		if not self.db.profile.miniresizeup then
			-- regular layout
			ZygorGuidesViewerMiniFrame_TitleBar:SetPoint("TOPLEFT",0,0);
			ZygorGuidesViewerMiniFrame_TitleBar:SetPoint("TOPRIGHT",0,0);
			ZygorGuidesViewerMiniFrame_TitleBar_Divider:SetPoint("BOTTOMLEFT",3,0);
			ZygorGuidesViewerMiniFrame_TitleBar_Divider:SetPoint("BOTTOMRIGHT",-3,0);
			ZygorGuidesViewerMiniFrame_Text:SetPoint("TOPLEFT",ZygorGuidesViewerMiniFrame_TitleBar,"BOTTOMLEFT",5,-3);
			ZygorGuidesViewerMiniFrame_Text:SetPoint("TOPRIGHT",ZygorGuidesViewerMiniFrame_TitleBar,"BOTTOMRIGHT",-5,-3);
		else
			-- bottom-up layout
			ZygorGuidesViewerMiniFrame_TitleBar:SetPoint("BOTTOMLEFT",0,0);
			ZygorGuidesViewerMiniFrame_TitleBar:SetPoint("BOTTOMRIGHT",0,0);
			ZygorGuidesViewerMiniFrame_TitleBar_Divider:SetPoint("TOPLEFT",3,0);
			ZygorGuidesViewerMiniFrame_TitleBar_Divider:SetPoint("TOPRIGHT",-3,0);
			ZygorGuidesViewerMiniFrame_Text:SetPoint("BOTTOMLEFT",ZygorGuidesViewerMiniFrame_TitleBar,"TOPLEFT",5,3);
			ZygorGuidesViewerMiniFrame_Text:SetPoint("BOTTOMRIGHT",ZygorGuidesViewerMiniFrame_TitleBar,"TOPRIGHT",-5,3);
		end

		ZygorGuidesViewerMiniFrame_TitleBarDragger:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerBottom:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerTop:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerLeft:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerRight:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerBottomLeft:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerTopRight:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerTopLeft:EnableMouse(not self.db.profile["frameslocked"])
		ZygorGuidesViewerMiniFrame_ResizerBottomRight:EnableMouse(not self.db.profile["frameslocked"])

		-- lock button
		if self.db.profile["frameslocked"] then
			ZygorGuidesViewerMiniFrame_TitleBar_LockButton:SetNormalTexture("Interface\\AddOns\\ZygorGuidesViewer\\Skin\\lock-closed-up")
			ZygorGuidesViewerMiniFrame_TitleBar_LockButton:SetPushedTexture("Interface\\AddOns\\ZygorGuidesViewer\\Skin\\lock-closed-down")
		else
			ZygorGuidesViewerMiniFrame_TitleBar_LockButton:SetNormalTexture("Interface\\AddOns\\ZygorGuidesViewer\\Skin\\lock-open-up")
			ZygorGuidesViewerMiniFrame_TitleBar_LockButton:SetPushedTexture("Interface\\AddOns\\ZygorGuidesViewer\\Skin\\lock-open-down")
		end

		local steptext
		local entries = {}
		local goal
		local i
		for i=1,#self.CurrentStep,1 do
			entries[i] = self.CurrentStep[i]
--			goal = self:FindMatchingGoal(entries[i])
--			if goal then entries[i] = entries[i] .. self:GetTextualCompletionGoalShort(goal) end
		end
		steptext = table.concat(entries,"|n")
		steptext = string.gsub(steptext,"\t([a-z]+\. )","\t|cffffff88%1|r")
		steptext = string.gsub(steptext,"\t","    ")
		ZygorGuidesViewerMiniFrame_Text:SetText(string.gsub(steptext,"\t","  "))
		ZygorGuidesViewerMiniFrame_TitleBar_SectionTitle:SetText(self.CurrentSection["sectiontitle"])
		if self.CurrentStep.mapnote and self.CurrentStep.mapzone and self.db.profile.minimapnotedesc then
			local mapnote = self.MapNotes[self.CurrentStep.mapzone][self.CurrentStep.mapnote]
			if mapnote then
				local title = "|cffff77ff"..mapnote.title.."|r " or ""
				local info2 = mapnote.info2 and "|n|cffffbb77("..mapnote.info2..")|r " or ""
				local info = mapnote.info
				ZygorGuidesViewerMiniFrame_Divider2:Show()
				ZygorGuidesViewerMiniFrame_Divider2:ClearAllPoints()
				ZygorGuidesViewerMiniFrame_Divider2:SetPoint("TOPLEFT",ZygorGuidesViewerMiniFrame_Text,"TOPLEFT",0,-ZygorGuidesViewerMiniFrame_Text:GetHeight()-5)
				ZygorGuidesViewerMiniFrame_Divider2:SetPoint("RIGHT",ZygorGuidesViewerMiniFrame,"RIGHT",-5,0)

				ZygorGuidesViewerMiniFrame_Text:SetText(ZygorGuidesViewerMiniFrame_Text:GetText().."|n|n"..title.."|n"..info..info2)
			else
				ZygorGuidesViewerMiniFrame_Divider2:Hide()
			end
		else
			ZygorGuidesViewerMiniFrame_Divider2:Hide()
		end

		local s = self:GetTextualCompletion()
		if s then
			ZygorGuidesViewerMiniFrame_Text:SetText(ZygorGuidesViewerMiniFrame_Text:GetText()..s)
		end

		if (self.db.profile.autosizemini) then
			if ZygorGuidesViewerMiniFrame:GetLeft() then  -- sometimes it's missing.
				if not self.db.profile.miniresizeup then
					ZygorGuidesViewerMiniFrame:ClearAllPoints()
					ZygorGuidesViewerMiniFrame:SetPoint("TOPLEFT",nil,"TOPLEFT",ZygorGuidesViewerMiniFrame:GetLeft(),ZygorGuidesViewerMiniFrame:GetTop()-ZygorGuidesViewerMiniFrame:GetParent():GetHeight())
				else
					ZygorGuidesViewerMiniFrame:ClearAllPoints()
					ZygorGuidesViewerMiniFrame:SetPoint("BOTTOMLEFT",nil,"BOTTOMLEFT",ZygorGuidesViewerMiniFrame:GetLeft(),ZygorGuidesViewerMiniFrame:GetBottom())
				end
			end
			ZygorGuidesViewerMiniFrame:SetHeight(ZygorGuidesViewerMiniFrame_Text:GetHeight()+35)
		end
		if self.CurrentStep then
			--[[
			if self.CurrentStep.mapzone then
				ZygorGuidesViewerMiniFrame_LocationLabel:Show()
				ZygorGuidesViewerMiniFrame_LocationText:Show()
				ZygorGuidesViewerMiniFrame_LocationText:SetText(self.CurrentStep["mapzone"])
			else
				ZygorGuidesViewerMiniFrame_LocationLabel:Hide()
				ZygorGuidesViewerMiniFrame_LocationText:Hide()
			end
			if self.CurrentStep.level then
				ZygorGuidesViewerMiniFrame_LevelLabel:Show()
				ZygorGuidesViewerMiniFrame_LevelText:Show()
				ZygorGuidesViewerMiniFrame_LevelText:SetText(self.CurrentStep.level)
			else
				ZygorGuidesViewerMiniFrame_LevelLabel:Hide()
				ZygorGuidesViewerMiniFrame_LevelText:Hide()
			end
			ZygorGuidesViewerMiniFrame_StepLabel:Show()
			--]]
			ZygorGuidesViewerMiniFrame_TitleBar_StepText:Show()
			ZygorGuidesViewerMiniFrame_TitleBar_StepText:SetText(self.CurrentStepNum)
		else
			--ZygorGuidesViewerMiniFrame_StepLabel:Hide()
			--ZygorGuidesViewerMiniFrame_StepText:Hide()
		end
		ZygorGuidesViewerMiniFrame_MissingText:Hide()
		ZygorGuidesViewerMiniFrame_TitleBar_PrevButton:Show()
		ZygorGuidesViewerMiniFrame_TitleBar_NextButton:Show()
	else
		local faction = UnitFactionGroup("player")
		ZygorGuidesViewerMiniFrame_SectionTitle:Hide()
		--ZygorGuidesViewerMiniFrame_LocationLabel:Hide()
		--ZygorGuidesViewerMiniFrame_LevelLabel:Hide()
		--ZygorGuidesViewerMiniFrame_StepLabel:Hide()
		ZygorGuidesViewerMiniFrame_TitleBar_PrevButton:Hide()
		ZygorGuidesViewerMiniFrame_TitleBar_NextButton:Hide()
		ZygorGuidesViewerMiniFrame_MissingText:Show()
		ZygorGuidesViewerMiniFrame_MissingText:SetText(L['missing_text_short'])
	end
--	ZygorGuidesViewerMiniFrame:SetFrameLevel(30)
end

function me:ToggleFilterNotes()
	self.db.profile["filternotes"] = not self.db.profile["filternotes"]
	if self:IsWaypointAddonEnabled("cart2") then
		self:UpdateCartographerExport()
	elseif self:IsWaypointAddonEnabled("tomtom") then
		--self.db.profile["filternotes"] = true
		if self.db.profile["filternotes"] then
			StaticPopupDialogs["TOMTOM_REMOVE_ALL_CONFIRM"].OnAccept()
		else
			self.iconsregistered = false
			self.iconregistryretries = 0
			-- wait for the timer to catch up
		end
		self:SetWaypoint()
	end
	self:UpdateMainFrame()
end

function me:IsWaypointAddonReady(addon)
	if not addon then addon = self.db.profile.waypointaddon end
	if addon=="cart2" then
		return (Cartographer_Notes and Cartographer_Notes:IsActive() and Cartographer_Notes.externalDBs) and true or false
	elseif addon=="cart3" then
		return (Cartographer3 and Cartographer3.db)
	elseif addon=="tomtom" then
		return (TomTom and TomTom.defaults)
	elseif addon=="metamap" then
		return false
	elseif addon=="builtin" then
		return false
	else
		return true
	end
end

function me:IsWaypointAddonEnabled(addon)
	if not addon then addon = self.db.profile.waypointaddon end
	return self.db.profile.waypointaddon==addon and self:IsWaypointAddonReady(addon) and self.iconsregistered
end

function me:UpdateMainFrame()
	if not ZygorGuidesViewerFrame then return end

	if not ZygorGuidesViewerFrame:IsVisible() then return end

	if self.Guide then
		ZygorGuidesViewerFrameStepsScrollFrame:Show()
		ZygorGuidesViewerFrameStepsScrollFrameChildHighlightFrame:Show()
		ZygorGuidesViewerFrame_HideCompleted:Show()
	else
		local faction = UnitFactionGroup("player")
		ZygorGuidesViewerFrame_EmptyText:SetText(L['missing_text_long'])
		ZygorGuidesViewerFrame_SectionLabel:Hide()
		ZygorGuidesViewerFrame_SectionDropDown:Hide()
		ZygorGuidesViewerFrame_LocationLabel:Hide()
		ZygorGuidesViewerFrame_HideCompleted:Hide()
		ZygorGuidesViewerFrame_LevelLabel:Hide()
		ZygorGuidesViewerFrame_StepLabel:Hide()
		return
	end
		
	local Section = self.CurrentSection
	if not Section then return end

	local stepnum,stepdata

	ZygorGuidesViewerFrame_HideCompletedText:SetText("show all dots")
	ZygorGuidesViewerFrame_HideCompleted:SetChecked(not self.db.profile["filternotes"])

	local last = nil
	local frame,frametext
	local stepnum,stepdata
	for stepnum,stepdata in pairs(Section["steps"]) do
		if stepnum>self.StepLimit then break end

		frame = getglobal("ZygorGuidesViewerFrameStep"..stepnum)
		if not frame then
			frame = CreateFrame ("Button", "ZygorGuidesViewerFrameStep"..stepnum, ZygorGuidesViewerFrameStepsScrollFrameChild, "ZGVFStep")
			frame:SetWidth(290)
		end

	--	if self.db.profile["hidecompleted"] and stepnum<self.CurrentStepNum then
	--		frame:Hide()
	--	else
		text = getglobal("ZygorGuidesViewerFrameStep"..stepnum.."_Text")
		if stepnum<self.CurrentStepNum then
			frame:SetAlpha(0.4)
		else
			frame:SetAlpha(1.0)
		end
		steptext = table.concat(stepdata,"|n")
		steptext = string.gsub(steptext,"\t([a-z]+\. )","\t|cffffff88%1|r")
		steptext = string.gsub(steptext,"\t","    ")

		steptext = stepnum .. ". " .. steptext

		if stepdata.requirement then
			local s
			if self:PlayerMeetsRequirements(stepdata) then
				s = "|cff88cc88"
			else
				s = "|cff880000"
			end
			steptext = s .. "(" .. table.concat(stepdata.requirement,L["stepreqor"]) .. ")|r|n" .. steptext
		end

		text:SetText(steptext)
		text:SetWidth(290)
		frame:Show()
		frame:SetHeight(text:GetStringHeight())
		frame:ClearAllPoints()
		if last then
			frame:SetPoint("TOPLEFT",getglobal("ZygorGuidesViewerFrameStep"..last),"BOTTOMLEFT",0,-15)
		else
			frame:SetPoint("TOPLEFT","ZygorGuidesViewerFrameStepsScrollFrameChild","TOPLEFT",0,-15)
		end
		frame.step = stepnum
		frame:SetScript("OnClick", function (this)
			if not ZygorGuidesViewer:PlayerMeetsRequirements(ZygorGuidesViewer.CurrentSection.steps[this.step]) then return end
			ZygorGuidesViewer.LastSkip = 1
			ZygorGuidesViewer:FocusStep(this.step)
		end)
		--text:Show()
		last = stepnum
	--	end
	end

	for stepnum = last+1, self.StepLimit, 1 do
		--local text = getglobal("ZygorGuidesViewerFrame_StepText" .. stepnum)
		--if text then text:Hide() end
		frame = getglobal("ZygorGuidesViewerFrameStep"..stepnum)
		if frame then frame:Hide() end
	end

	if self.CurrentStep then
		if self.CurrentStep.mapzone then
			ZygorGuidesViewerFrame_LocationLabel:Show()
			ZygorGuidesViewerFrame_LocationText:Show()
			ZygorGuidesViewerFrame_LocationText:SetText(self.CurrentStep["mapzone"])
		else
			ZygorGuidesViewerFrame_LocationLabel:Hide()
			ZygorGuidesViewerFrame_LocationText:Hide()
		end
		if self.CurrentStep.level then
			ZygorGuidesViewerFrame_LevelLabel:Show()
			ZygorGuidesViewerFrame_LevelText:Show()
			ZygorGuidesViewerFrame_LevelText:SetText(self.CurrentStep.level)
		else
			ZygorGuidesViewerFrame_LevelLabel:Hide()
			ZygorGuidesViewerFrame_LevelText:Hide()
		end
		ZygorGuidesViewerFrame_StepLabel:Show()
		ZygorGuidesViewerFrame_StepText:Show()
		ZygorGuidesViewerFrame_StepText:SetText(self.CurrentStepNum)
	else
		ZygorGuidesViewerFrame_StepLabel:Hide()
		ZygorGuidesViewerFrame_StepText:Hide()
	end

	self:HighlightCurrentStep()
	self:ScrollToCurrentStep()

	-- hide/show scroll filler : GetVerticalScrollRange() won't return proper values yet... just base it on emptiness for now.
--[[
	self:Debug(ZygorGuidesViewerFrameStepsScrollFrameChild:GetHeight())
	if ZygorGuidesViewerFrameStepsScrollFrameChild:GetHeight()<200 then
		ZygorGuidesViewerFrame_ScrollFill:Hide()
	else
		ZygorGuidesViewerFrame_ScrollFill:Show()
	end
]]--
end

function me:ScrollToCurrentStep()
	if self.ForceScrollToCurrentStep then
		self:Debug("Scrolling to: ")
		self:Debug(self.CurrentStepNum)
		self.ForceScrollToCurrentStep = false
		if self.CurrentStepNum then
			local height=0, stepnum
			for stepnum = 1, self.CurrentStepNum-1, 1 do
				height = height + getglobal("ZygorGuidesViewerFrameStep"..stepnum):GetHeight()+15
			end
--			self:Debug(height)
		--	local y = 624 - getglobal("ZygorGuidesViewerFrameStep"..self.CurrentStepNum):GetTop()+ZygorGuidesViewerFrameStepsScrollFrame:GetVerticalScroll()
		--	if y < 0 then y = 0 end
			local current = ZygorGuidesViewerFrameStepsScrollFrame:GetVerticalScroll()
--			self:Debug(current)
			if current<height and height-current+getglobal("ZygorGuidesViewerFrameStep"..self.CurrentStepNum):GetHeight()<ZygorGuidesViewerFrameStepsScrollFrame:GetHeight() then return end
			local range = ZygorGuidesViewerFrameStepsScrollFrame:GetVerticalScrollRange()
			if height > range and range>0 then height = range end
			ZygorGuidesViewerFrameStepsScrollFrame:SetVerticalScroll(height)
		end
	else
		self.ForceScrollToCurrentStep = true
	end
end

function me:IsVisible()
	return ( ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible())
end

function me:SetVisible(info,onoff)
	if not onoff and self:IsVisible() then self:ToggleFrame() end
	if onoff and not self:IsVisible() then self:ToggleFrame() end
end

function me:ToggleFrame()
	if ( ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible()) then
		ZygorGuidesViewerMiniFrame:Hide()
		HideUIPanel(ZygorGuidesViewerFrame)
	else
		if self.db.profile["minimode"] then
			ZygorGuidesViewerMiniFrame:Show()
		else
			ShowUIPanel(ZygorGuidesViewerFrame)
		end
	end
end

function me:ToggleMini()
	self.db.profile["minimode"] = not self.db.profile["minimode"]
	if self.db.profile["minimode"] then
		HideUIPanel(ZygorGuidesViewerFrame)
		local x,y = ZygorGuidesViewerFrame:GetRight(),ZygorGuidesViewerFrame:GetTop()
		ZygorGuidesViewerMiniFrame:ClearAllPoints()
		ZygorGuidesViewerMiniFrame:SetPoint("TOPRIGHT","UIParent","BOTTOMLEFT", x,y-11)
		ZygorGuidesViewerMiniFrame:Show()
	else
		ZygorGuidesViewerMiniFrame:Hide()
		ZygorGuidesViewerFrame:ClearAllPoints()
		local x,y = ZygorGuidesViewerMiniFrame:GetRight(),ZygorGuidesViewerMiniFrame:GetTop()
		ZygorGuidesViewerFrame:SetPoint("TOPRIGHT","UIParent","BOTTOMLEFT", x,y+11)
		ShowUIPanel(ZygorGuidesViewerFrame)
	end		
end

function me:ToggleFramesLocked()
	self.db.profile["frameslocked"] = not self.db.profile["frameslocked"]
	self:UpdateMiniFrame()
end

function me:SkipStep(delta,click)
	repeat
		local i = self.CurrentStepNum+delta
		if i<1 then
			if self.CurrentSectionNum==1 then return end		-- first section? bail.
			if self.CurrentSection.defaultfor then return end		-- no skipping back from a starter section.

			local x,race = UnitRace("player")
			local default = self:FindDefaultSection()

			self:SetSection(self.CurrentSectionNum-1)
			if self.CurrentSection.defaultfor and self.CurrentSection.defaultfor ~= race then		-- wrong default section? move to ours.
				self:SetSection(default)
			end
			i=#(self.CurrentSection["steps"])
		end
		if i>#self.CurrentSection["steps"] then
			if self.CurrentSectionNum==#self.Guide then return end
			self:SetSection(self.CurrentSection.nextsection or self.CurrentSectionNum+1)
			i=1
		end
		if (click) then self.LastSkip = delta else self.LastSkip=0 end
		self:FocusStep(i)
	until self:PlayerMeetsRequirements()
end

function me:OpenOptions()
	--self:OpenConfigMenu()
	InterfaceOptionsFrame_OpenToCategory(L['name'])
end

function me:Print(s)
	ChatFrame1:AddMessage(L['name']..": "..ns(s))
end
















function me:IsDebugMode()
	return self.db.profile.debug
end
function me:ToggleDebugMode()
	self.db.profile.debug = not self.db.profile.debug
end

function me:IsAutosizeMini()
	return self.db.profile.autosizemini
end
function me:ToggleAutosizeMini()
	self.db.profile.autosizemini = not self.db.profile.autosizemini
	self:UpdateMiniFrame()
end

function me:IsMiniResizeUp()
	return self.db.profile.miniresizeup
end
function me:ToggleMiniResizeUp()
	self.db.profile.miniresizeup = not self.db.profile.miniresizeup
	self:UpdateMiniFrame()
end

function me:IsShowgoals()
	return self.db.profile.showgoals
end
function me:ToggleShowgoals()
	self.db.profile.showgoals = not self.db.profile.showgoals
	self:UpdateMiniFrame()
end

function me:IsAutoskip()
	return self.db.profile.autoskip
end
function me:ToggleAutoskip()
	self.db.profile.autoskip = not self.db.profile.autoskip
end

function me:IsMiniWaypointDesc()
	return self.db.profile.minimapnotedesc
end
function me:ToggleMiniWaypointDesc()
	self.db.profile.minimapnotedesc = not self.db.profile.minimapnotedesc
	self:UpdateMiniFrame()
end

function me:GetOpacityMain()
	return self.db.profile.opacitymain or 1.0
end
function me:SetOpacityMain(info,n)
	self.db.profile.opacitymain = n
	self:Debug(n)
	ZygorGuidesViewerFrame:SetAlpha(n)
end

function me:GetOpacityMini()
	return self.db.profile.opacitymini or 1.0
end
function me:SetOpacityMini(info,n)
	self.db.profile.opacitymini = n
	ZygorGuidesViewerMiniFrame_Border:SetAlpha(n)
	ZygorGuidesViewerMiniFrame_Background:SetAlpha(n)
end

function me:GetIconScale()
	return self.db.profile.iconScale
end
function me:SetIconScale(info,value)
	self.db.profile.iconScale = value
	if not self:IsWaypointAddonEnabled("cart2") then return end
	Cartographer_Notes:MINIMAP_UPDATE_ZOOM()
	Cartographer_Notes:UpdateMinimapIcons()
end

function me:GetIconAlpha()
	return self.db.profile.iconAlpha
end
function me:SetIconAlpha(info,value)
	self.db.profile.iconAlpha = value
	if not self:IsWaypointAddonEnabled("cart2") then return end
	Cartographer_Notes:MINIMAP_UPDATE_ZOOM()
	Cartographer_Notes:UpdateMinimapIcons()
end

function me:IsShowingMinimapIcons()
	return self.db.profile.minicons
end
function me:ToggleShowingMinimapIcons()
	self.db.profile.minicons = not self.db.profile.minicons
	if not self:IsWaypointAddonEnabled("cart2") then return end
	Cartographer_Notes:MINIMAP_UPDATE_ZOOM()
	Cartographer_Notes:UpdateMinimapIcons()
end

function me:IsShowingMapIcons()
	return self.db.profile.mapicons
end
function me:ToggleShowingMapIcons()
	self.db.profile.mapicons = not self.db.profile.mapicons
end





-- icon handlers:

function me:GetNoteScaling(zone,id,data)
	return self.db.profile.iconScale
end

function me:IsNoteHidden(zone,id,data)
	return self.db.profile.filternotes and (not self.CurrentStep or not self.CurrentStep.mapnote or (id~=self.CurrentStep.mapnote) or (zone~=self.CurrentStep.mapzone))
end

function me:IsMiniNoteHidden(zone,id,data)
	return not self.db.profile.minicons or (self.db.profile.filternotes and ((id~=self.CurrentStep.mapnote) or (zone~=self.CurrentStep.mapzone)))
end

function me:GetNoteTransparency(zone,id,data)
	return self.db.profile.iconAlpha
end

function me:GetNoteIcon(zone,id,data)
--	return (not self.db.profile.filternotes and self.CurrentStep and (id==self.CurrentStep.mapnote) and (zone==self.CurrentStep.mapzone)) and "hilite" or data.icon
	return (self.CurrentStep and (id==self.CurrentStep.mapnote) and (zone==self.CurrentStep.mapzone)) and (data.icon=="Square" and "hilitesquare" or "hilite") or data.icon
end


function me:GetWaypointAddon()
	return self.db.profile.waypointaddon
end
function me:SetWaypointAddon(info,value)
	if value~="none" and not self:IsWaypointAddonReady(value) then
		local msg = {
			["cart2"]="Cartographer2 not found.",
			["cart3"]="Cartographer3 not found.",
			["tomtom"]="TomTom not found.",
			["metamap"]="MetaMap not found.",
			["builtin"]="Built-in waypointing not implemented."
		}
		self:Print(msg[value])
		return
	end
	if (value~=self.db.profile.waypointaddon) then
		self:UnsetWaypointAddon()
	end
	self.db.profile.waypointaddon = value
	self.iconsregistered = false
	self.iconregistryretries = 0
	self:Print(L["waypointaddon_set"]:format(self.optionsmap.args.waypoints.values[value]))
--[[
	if (self.db.profile.waypointaddon=="none") then
		self.optionsmap.args.minicons.disabled = true
	else
		self.optionsmap.args.minicons.disabled = false
	end
	LibStub("AceConfigRegistry-3.0"):NotifyChange("ZygorGuidesViewer")
]]--
end

function me:UnsetWaypointAddon()
	if not self:IsWaypointAddonEnabled() then return end --nothing to do here, move along
	local value = self.db.profile.waypointaddon
	if (value=="cart2") then
		self:Print("Disconnecting Cartographer2...")
		Cartographer_Notes:UnregisterNotesDatabase('ZygorGuides')
	elseif value=="cart3" and not (Cartographer3 and Cartographer3.db) then
		self:Print("Cartographer3 not connected.")
		return
	elseif value=="tomtom" and not (TomTom and TomTom.defaults) then
		StaticPopupDialogs["TOMTOM_REMOVE_ALL_CONFIRM"].OnAccept()
		TomTomCrazyArrow:Hide()
	elseif value=="metamap" then
		self:Print("MetaMap not connected.")
		return
	elseif value=="builtin" then
		self:Print("Built-in waypointing not enabled.")
		return
	end
end


function me:MINIMAP_UPDATE_ZOOM()
	self:UpdateMinimapArrow(false)
end

function me:CHAT_MSG_SYSTEM(event,text)
	self:Debug("CHAT_MSG_SYSTEM")
	text = ns(text)
	local zone = string.match(text,DL["home"])
	if zone then self.recentlyHomeChanged = true end
	local quest = string.match(text,DL["accepted"])
	if quest then self:NewQuestEvent(quest,self:GetQuestId(quest)) end
	local quest = string.match(text,DL["complete"])
	if quest then self:LostQuestEvent(quest,self:GetQuestId(quest),true) end
end

--function me:UI_INFO_MESSAGE(sender,event,text)
function me:UI_INFO_MESSAGE(event,text)
	text = ns(text)
	local zone = string.match(text,DL["flightpath"])
	if zone then self.recentlyDiscoveredFlightpath = true end
end

function me:QUEST_WATCH_UPDATE(index)
--	self:Debug("QUEST_WATCH_UPDATE: "..ns(sender)..", "..ns(event)..", "..ns(index))
--	self:Debug('QUEST_WATCH_UPDATE '..index);
	if (GetNumQuestLeaderBoards(index)>1) then
		self:Debug('QUEST_WATCH_UPDATE quest has multiple goals, queuing for QUEST_LOG_UPDATE');
		-- there's many goals here, we need to know which was completed. Queue it.
		self.questWatchUpdated[#self.questWatchUpdated+1] = index;
		-- ... and wait for a QUEST_LOG_UPDATE
	else
		self:Debug('QUEST_WATCH_UPDATE quest has one goal, but queuing for QUEST_LOG_UPDATE anyway');
		self.questWatchUpdated[#self.questWatchUpdated+1] = index;
--		self:Debug('Updating quest list from WATCH_UPDATE');
--		self:CacheQuestLog()
--		self:RecordData(index,'goal',1)
--		self:RefreshPlugins()
	end
end

function me:QUEST_COMPLETE(arg1)
	self:Debug('QUEST_COMPLETE: '..ns(arg1));
	self.completingQuest = true
--	self:RecordData(self.questIndicesByTitle[GetTitleText()], 'finish', QuestFrameNpcNameText:GetText());
end

me.questWatchUpdated = {}

function me:QUEST_FINISHED(arg1)
--	self:Debug('QUEST_FINISHED: '..ns(arg1));
end

function me:QUEST_ITEM_UPDATE(arg1)
--	self:Debug('QUEST_ITEM_UPDATE: '..ns(arg1));
end

function me:UNIT_QUEST_LOG_CHANGED(unit)
	self:Debug('UNIT_QUEST_LOG_CHANGED for '..ns(unit));
	self.QuestLogChanged = true
end

function me:QUEST_LOG_UPDATE(arg1)
--	self:Debug('QUEST_LOG_UPDATE: '..ns(arg1));
--	if 1 then self:Debug('**BREAK**'); return end
	local recorded = nil
	self:CacheQuestLog()
	if self.QuestLogChanged or recorded then
		self:Debug('Quest log changed.')
		self.QuestLogChanged = nil
	end

	self:TryToCompleteStep()
--	self:Debug('QUEST_LOG_UPDATE done.')
end

function me:FindData(array,what,data)
	if not (type(array)=="table") then return nil end
	local i,d
	for i,d in pairs(array) do if d[what]==data then return d end end
end

function me:CacheQuestLog()
	--self:Debug('CacheQuestLog: '..zone..'/'..subzone);
	--if not zone or zone=='' then return nil end

--	if 1 then self:Debug('**BREAK**'); return end
	local time = GetTime()
	if time - self.QuestCacheTime < 1 then
		self.QuestCacheUndertimeRepeats = self.QuestCacheUndertimeRepeats + 1
		if self.QuestCacheUndertimeRepeats > 10 then return end
	else
		-- overtime; everything in order.
		self.QuestCacheUndertimeRepeats = 0
		self.QuestCacheTime = time
	end
	self:Debug("CacheQuestLog after ".. time - self.QuestCacheTime)

	local iNumEntries, iNumQuests = GetNumQuestLogEntries(); -- this SUCKS. Entries can be muddled by collapsing the quest log, and NumQuests is useless anyway.
	iNumEntries = 50 -- WHAT EVER.

	local collecting = true;
	self.oldquests={}
	local oldn=0
	if self.quests then for qi,q in pairs(self.quests) do self.oldquests[qi]=q; end end
	self.quests = {}

	--local selected = GetQuestLogSelection();

	for i = 1, iNumEntries, 1 do
		local strQuestLogTitleText, strQuestLevel, strQuestTag, numPlayers, isHeader, isCollapsed, isComplete = GetQuestLogTitle(i);

		if not isHeader and strQuestLogTitleText then
			local goals,goalsNamed = self:GetQuestLeaderBoards(i);

			local id = self:GetQuestId(i)
			local quest = {
				title = strQuestLogTitleText,
				level = strQuestLevel,
				--objective = obj,
				--description = desc,
				complete = isComplete,
				goals = goals,
				goalsNamed = goalsNamed,
				id = id,
				index = i
			};
			self.quests[i] = quest
			if not self:FindData(self.oldquests,"id",id) then self:NewQuestEvent(strQuestLogTitleText,id) end
		end
	end

	if self.oldquests then for qi,q in pairs(self.oldquests) do if not self:FindData(self.quests,"id",q.id) then self:LostQuestEvent(q.title,q.id) end end end

	return self.quests;
end

function me:GetQuestId(indexortitle)
	local link
	self:Debug("Getting id of quest "..indexortitle)
	if (type(indexortitle)=="number") then
		link = GetQuestLink(indexortitle)
		if link then
			a,a,id = string.find(link, "quest:([%d]+):");
			return tonumber(id)
		end
	else
		for i = 1, 50, 1 do
			link = GetQuestLink(i)
			self:Debug(link)
			if link then
				a,a,id,title = string.find(link, "quest:([%d]+):.+%[(.+)%]");
				self:Debug(id)
				if title==indexortitle then
					return tonumber(id)
				end
			end
		end
	end
end

function me:ParseLeaderBoard(leaderboard)
	local i, j, strItemName, iNumItems, iNumNeeded = string.find(leaderboard, "(.*):%s*([%d]+)%s*/%s*([%d]+)");
	if (strItemName) then
		return strItemName, iNumItems, iNumNeeded;
	else
		return leaderboard, 1, 1
	end
end

function me:CleanGoal(goal)
	local cleanGoal = string.match(goal,"(.*) slain")
	if cleanGoal then return cleanGoal else return goal end
end

function me:GetQuestLeaderBoards(questindex)
	local iGoals = GetNumQuestLeaderBoards(questindex);
	local goals = {}
	local goalsNamed = {}
	if tonumber(iGoals)>0 then
		for g = 1, iGoals, 1 do
			local leaderboard,type,complete = GetQuestLogLeaderBoard(g,questindex)
			local item,num,needed = self:ParseLeaderBoard(leaderboard);
			goals[g] = { item=item, num=num, needed=needed, type=type, complete=complete, leaderboard=leaderboard };
			goalsNamed[item] = goals[g]
		end
	end
	return goals,goalsNamed
end

function me:FindCompletedGoal(questindex,a,b)
	self:Debug("Finding completed goal: "..questindex..","..ns(a)..","..ns(b));
	local newgoals = self:GetQuestLeaderBoards(questindex);
	for oldg,oldgoal in pairs(self.quests[questindex].goals) do
		self:Debug('#'..oldg..' '..oldgoal.item..' = '..oldgoal.num..'->'..newgoals[oldg].num);
		if (oldgoal.num<newgoals[oldg].num) then
			self:Debug('#'..oldg..' changed');
			return oldg, oldgoal.item;
		end
	end
	return 1;
end

function me:NewQuestEvent(questTitle,id)
	if not id or not questTitle then return end
	self.recentlyAcceptedQuests[questTitle]=true
	self.recentlyAcceptedQuests[id]=true
	self:Debug("New Quest: "..questTitle.." id "..id)
end

function me:LostQuestEvent(questTitle,id,surelyComplete)
	self:Debug("Lost Quest: "..ns(questTitle).." id "..ns(id).." complete? "..ns(surelyComplete))
	if (self.completingQuest or surelyComplete) then
		self.recentlyCompletedQuests[questTitle]=true
		if id then self.recentlyCompletedQuests[id]=true end
		self.completingQuest = nil
	end
end

function me:GetQuestTag (questindex)
	local title, level = GetQuestLogTitle(questindex);
	return title .. ' [' .. level .. ']';
end

function me:Dump(s)
	if self.db.profile.debug then
		--AceLibrary("AceConsole-3.0"):Debug(s)
	end
end

function me:loc(s)
	if type(s)=="table" then
		return s[GetLocale()]
	else
		return s
	end
end



function me:Frame_OnShow()
	PlaySound("igQuestLogOpen");
	--ZygorGuidesViewerFrame_Filter()
	if UnitFactionGroup("player")=="Horde" then
		ZygorGuidesViewerFrameTitleAlliance:Hide()
	else
		ZygorGuidesViewerFrameTitleHorde:Hide()
	end
	ZygorGuidesViewerFrame_Update()
	self.db.profile.visible = (ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible()) and true or false;
end

function me:Frame_OnHide()
	PlaySound("igQuestLogClose");
	self.db.profile.visible = (ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible()) and true or false;
end

function me:MiniFrame_OnShow()
	PlaySound("igQuestLogOpen");
	--ZygorGuidesViewerFrame_Filter()
	self:UpdateMiniFrame()
	self.db.profile.visible = (ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible()) and true or false;
end

function me:MiniFrame_OnHide()
	PlaySound("igQuestLogClose");
	self.db.profile.visible = (ZygorGuidesViewerFrame:IsVisible() or ZygorGuidesViewerMiniFrame:IsVisible()) and true or false;
end


-------------------
--Guides' functions

ZGVGF = {}
ZGVGF.cl = function (x,y) return "|cffffaaaa"..x..","..y.."|r" end
ZGVGF.cq = function (q) return "|cffaaaaff`"..q.."'|r" end
ZGVGF.cn = function (q) return "|cffccffcc"..q.."|r" end
ZGVGF.ce = function (q) return "|cffffcccc"..q.."|r" end

local LM = ZygorGuidesViewer_L("G_main")
local LI = ZygorGuidesViewer_L("G_item")
local LC = ZygorGuidesViewer_L("G_char")
local LQ = ZygorGuidesViewer_L("G_quest")
local LS = ZygorGuidesViewer_L("G_string")

ZGVGF.accept = function (q) return LM["accept"]:format(ZGVGF.cq(LQ[q])) end
ZGVGF.turn_in = function (q) return LM["turn in"]:format(ZGVGF.cq(LQ[q])) end
ZGVGF.talk_to = function (c) return LM["talk to"]:format(ZGVGF.cn(LC[c])) end
ZGVGF.grind = function (n) return LM["grind"]:format(n) end
ZGVGF.grind2 = function (n) return LM["grind2"] end
ZGVGF.get = function (s) return LM["get"]:format(s) end
ZGVGF.get_n = function (n,s) return LM["get #"]:format(n,s) end
ZGVGF.go_to = function (s) return LM["go to"]:format(s) end
ZGVGF.go_n_to = function (s) return LM["go north to"]:format(s) end
ZGVGF.go_ne_to = function (s) return LM["go northeast to"]:format(s) end
ZGVGF.go_e_to = function (s) return LM["go east to"]:format(s) end
ZGVGF.go_se_to = function (s) return LM["go southeast to"]:format(s) end
ZGVGF.go_s_to = function (s) return LM["go south to"]:format(s) end
ZGVGF.go_sw_to = function (s) return LM["go southwest to"]:format(s) end
ZGVGF.go_w_to = function (s) return LM["go west to"]:format(s) end
ZGVGF.go_nw_to = function (s) return LM["go northwest to"]:format(s) end
ZGVGF.also_at = function (s) return LM["also at"]:format(s) end
ZGVGF.slain = function (s) return LM["slain"]:format(LC[s]) end
ZGVGF.kill = function (s) return LM["kill"]:format(ZGVGF.ce(LC[s])) end
ZGVGF.kill_n = function (n,s) return LM["kill #"]:format(n,ZGVGF.ce(LC[s])) end
ZGVGF.get_flight_path = function (s) return LM["get flight path"]:format(LS[s]) end

function GetPlayerBearing()
	local obj; -- Remains an upvalue
	do
		local t = {Minimap:GetChildren()}; -- Becomes garbage
		for k, v in pairs(t) do
			if v:IsObjectType("Model") and not v:GetName() and v:GetModel() == "interface\\minimap\\minimaparrow.m2" then
				obj = v; break;
			end
		end
	end
	if not obj then return; end

	-- If we've found what we were looking for, rewrite function to skip the search next time.
	GetPlayerBearing = function() return (1-obj:GetFacing()/math.pi/2)*360; end
	return GetPlayerBearing();
end
