local MAJOR_VERSION = "1.0"
local MINOR_VERSION = tonumber(string.sub("$Revision: 1545 $", 12, -3))
if TouristLib and TouristLib.versions[MAJOR_VERSION] and TouristLib.versions[MAJOR_VERSION].minor >= MINOR_VERSION then
	return
end

-------------IRIEL'S-STUB-CODE--------------
local stub = {};

-- Instance replacement method, replace contents of old with that of new
function stub:ReplaceInstance(old, new)
   for k,v in pairs(old) do old[k]=nil; end
   for k,v in pairs(new) do old[k]=v; end
end

-- Get a new copy of the stub
function stub:NewStub()
  local newStub = {};
  self:ReplaceInstance(newStub, self);
  newStub.lastVersion = '';
  newStub.versions = {};
  return newStub;
end

-- Get instance version
function stub:GetInstance(version)
   if (not version) then version = self.lastVersion; end
   local versionData = self.versions[version];
   if (not versionData) then
      message("Cannot find library instance with version '" 
              .. version .. "'");
      return;
   end
   return versionData.instance;
end

-- Register new instance
function stub:Register(newInstance)
   local version,minor = newInstance:GetLibraryVersion();
   self.lastVersion = version;
   local versionData = self.versions[version];
   if (not versionData) then
      -- This one is new!
      versionData = { instance = newInstance,
         minor = minor,
         old = {} 
      };
      self.versions[version] = versionData;
      newInstance:LibActivate(self);
      return newInstance;
   end
   if (minor <= versionData.minor) then
      -- This one is already obsolete
      if (newInstance.LibDiscard) then
         newInstance:LibDiscard();
      end
      return versionData.instance;
   end
   -- This is an update
   local oldInstance = versionData.instance;
   local oldList = versionData.old;
   versionData.instance = newInstance;
   versionData.minor = minor;
   local skipCopy = newInstance:LibActivate(self, oldInstance, oldList);
   table.insert(oldList, oldInstance);
   if (not skipCopy) then
      for i, old in ipairs(oldList) do
         self:ReplaceInstance(old, newInstance);
      end
   end
   return newInstance;
end

-- Bind stub to global scope if it's not already there
if (not TouristLib) then
   TouristLib = stub:NewStub();
end

-- Nil stub for garbage collection
stub = nil;
-----------END-IRIEL'S-STUB-CODE------------

local lib = {}
local events = {}

local loc
do
	local babbleZone = BabbleLib:GetInstance('Zone 1.1')
	loc = {
		BOOTY_BAY = babbleZone:GetLocalized("BOOTY_BAY"),
		DEEPRUN_TRAM = babbleZone:GetLocalized("DEEPRUN_TRAM"),
		IRONFORGE = babbleZone:GetLocalized("IRONFORGE"),
		STORMWIND_CITY = babbleZone:GetLocalized("STORMWIND_CITY"),
		ELWYNN_FOREST = babbleZone:GetLocalized("ELWYNN_FOREST"),
		DUN_MOROGH = babbleZone:GetLocalized("DUN_MOROGH"),
		TIRISFAL_GLADES = babbleZone:GetLocalized("TIRISFAL_GLADES"),
		LOCH_MODAN = babbleZone:GetLocalized("LOCH_MODAN"),
		SILVERPINE_FOREST = babbleZone:GetLocalized("SILVERPINE_FOREST"),
		WESTFALL = babbleZone:GetLocalized("WESTFALL"),
		REDRIDGE_MOUNTAINS = babbleZone:GetLocalized("REDRIDGE_MOUNTAINS"),
		DUSKWOOD = babbleZone:GetLocalized("DUSKWOOD"),
		HILLSBRAD_FOOTHILLS = babbleZone:GetLocalized("HILLSBRAD_FOOTHILLS"),
		WETLANDS = babbleZone:GetLocalized("WETLANDS"),
		ALTERAC_MOUNTAINS = babbleZone:GetLocalized("ALTERAC_MOUNTAINS"),
		ARATHI_HIGHLANDS = babbleZone:GetLocalized("ARATHI_HIGHLANDS"),
		STRANGLETHORN_VALE = babbleZone:GetLocalized("STRANGLETHORN_VALE"),
		BADLANDS = babbleZone:GetLocalized("BADLANDS"),
		SWAMP_OF_SORROWS = babbleZone:GetLocalized("SWAMP_OF_SORROWS"),
		DEADWIND_PASS = babbleZone:GetLocalized("DEADWIND_PASS"),
		HINTERLANDS = babbleZone:GetLocalized("HINTERLANDS"),
		SEARING_GORGE = babbleZone:GetLocalized("SEARING_GORGE"),
		BLASTED_LANDS = babbleZone:GetLocalized("BLASTED_LANDS"),
		BURNING_STEPPES = babbleZone:GetLocalized("BURNING_STEPPES"),
		WESTERN_PLAGUELANDS = babbleZone:GetLocalized("WESTERN_PLAGUELANDS"),
		EASTERN_PLAGUELANDS = babbleZone:GetLocalized("EASTERN_PLAGUELANDS"),
		RATCHET = babbleZone:GetLocalized("RATCHET"),
		GADGETZAN = babbleZone:GetLocalized("GADGETZAN"),
		ORGRIMMAR = babbleZone:GetLocalized("ORGRIMMAR"),
		THUNDER_BLUFF = babbleZone:GetLocalized("THUNDER_BLUFF"),
		UNDERCITY = babbleZone:GetLocalized("UNDERCITY"),
		DUROTAR = babbleZone:GetLocalized("DUROTAR"),
		MULGORE = babbleZone:GetLocalized("MULGORE"),
		DARKSHORE = babbleZone:GetLocalized("DARKSHORE"),
		BARRENS = babbleZone:GetLocalized("BARRENS"),
		STONETALON_MOUNTAINS = babbleZone:GetLocalized("STONETALON_MOUNTAINS"),
		ASHENVALE = babbleZone:GetLocalized("ASHENVALE"),
		THOUSAND_NEEDLES = babbleZone:GetLocalized("THOUSAND_NEEDLES"),
		DESOLACE = babbleZone:GetLocalized("DESOLACE"),
		DUSTWALLOW_MARSH = babbleZone:GetLocalized("DUSTWALLOW_MARSH"),
		FERALAS = babbleZone:GetLocalized("FERALAS"),
		TANARIS = babbleZone:GetLocalized("TANARIS"),
		AZSHARA = babbleZone:GetLocalized("AZSHARA"),
		FELWOOD = babbleZone:GetLocalized("FELWOOD"),
		UN_GORO_CRATER = babbleZone:GetLocalized("UN_GORO_CRATER"),
		SILITHUS = babbleZone:GetLocalized("SILITHUS"),
		WINTERSPRING = babbleZone:GetLocalized("WINTERSPRING"),
		HYJAL = babbleZone:GetLocalized("HYJAL"),
		MOONGLADE = babbleZone:GetLocalized("MOONGLADE"),
		DARNASSUS = babbleZone:GetLocalized("DARNASSUS"),
		TELDRASSIL = babbleZone:GetLocalized("TELDRASSIL"),
		ALTERAC_VALLEY = babbleZone:GetLocalized("ALTERAC_VALLEY"),
		WARSONG_GULCH = babbleZone:GetLocalized("WARSONG_GULCH"),
		ARATHI_BASIN = babbleZone:GetLocalized("ARATHI_BASIN"),
		STOCKADE = babbleZone:GetLocalized("STOCKADE"),
		RAGEFIRE_CHASM = babbleZone:GetLocalized("RAGEFIRE_CHASM"),
		ZUL_FARRAK = babbleZone:GetLocalized("ZUL_FARRAK"),
		DEADMINES = babbleZone:GetLocalized("DEADMINES"),
		WAILING_CAVERNS = babbleZone:GetLocalized("WAILING_CAVERNS"),
		GNOMEREGAN = babbleZone:GetLocalized("GNOMEREGAN"),
		RAZORFEN_KRAUL = babbleZone:GetLocalized("RAZORFEN_KRAUL"),
		BLACKFATHOM_DEEPS = babbleZone:GetLocalized("BLACKFATHOM_DEEPS"),
		SHADOWFANG_KEEP = babbleZone:GetLocalized("SHADOWFANG_KEEP"),
		SCARLET_MONASTERY = babbleZone:GetLocalized("SCARLET_MONASTERY"),
		ULDAMAN = babbleZone:GetLocalized("ULDAMAN"),
		RAZORFEN_DOWNS = babbleZone:GetLocalized("RAZORFEN_DOWNS"),
		MARAUDON = babbleZone:GetLocalized("MARAUDON"),
		ONYXIAS_LAIR = babbleZone:GetLocalized("ONYXIAS_LAIR"),
		BLACKROCK_MOUNTAIN = babbleZone:GetLocalized("BLACKROCK_MOUNTAIN"),
		CAVERNS_OF_TIME = babbleZone:GetLocalized("CAVERNS_OF_TIME"),
		TEMPLE_OF_ATAL_HAKKAR = babbleZone:GetLocalized("TEMPLE_OF_ATAL_HAKKAR"),
		DIRE_MAUL = babbleZone:GetLocalized("DIRE_MAUL"),
		BLACKROCK_DEPTHS = babbleZone:GetLocalized("BLACKROCK_DEPTHS"),
		BLACKROCK_SPIRE = babbleZone:GetLocalized("BLACKROCK_SPIRE"),
		STRATHOLME = babbleZone:GetLocalized("STRATHOLME"),
		MOLTEN_CORE = babbleZone:GetLocalized("MOLTEN_CORE"),
		SCHOLOMANCE = babbleZone:GetLocalized("SCHOLOMANCE"),
		BLACKWING_LAIR = babbleZone:GetLocalized("BLACKWING_LAIR"),
		ZUL_GURUB = babbleZone:GetLocalized("ZUL_GURUB"),
		RUINS_OF_AHN_QIRAJ = babbleZone:GetLocalized("RUINS_OF_AHN_QIRAJ"),
		TEMPLE_OF_AHN_QIRAJ = babbleZone:GetLocalized("TEMPLE_OF_AHN_QIRAJ"),
	}
end

local playerLevel = 1
local playerFaction = UnitFactionGroup("player") or "Horde"
local isHorde = playerFaction == "Horde"

function lib:GetLibraryVersion()
	return MAJOR_VERSION, MINOR_VERSION
end

function lib:LibActivate(stub, oldLib, oldList)
	if oldLib then
		for i = 1, 9 do
			setglobal("SLASH_TOURIST" .. i, nil)
		end
	end
	SLASH_TOURIST1 = "/tourist"
	SLASH_TOURIST2 = "/touristlib"
	local function print(msg)
		DEFAULT_CHAT_FRAME:AddMessage(msg)
	end
	SlashCmdList["TOURIST"] = function(msg)
		msg = string.gsub(string.gsub(msg, "^%s*(.-)%s*$", "%1"), "%s+", " ")
		local _, _, first, rest = string.find(msg, "^(.-) (.*)$")
		if not first then
			if msg ~= "" then
				first = msg
			end
		end
		if first then
			first = string.lower(first)
		end
		if not first then
			print("|cffffff7fTouristLib v" .. MAJOR_VERSION .. "." .. MINOR_VERSION .. ":|r")
			print("  |cffffff7fzone|r - information on a given zone/instance.")
			print("  |cffffff7frecommend|r - list recommended places to go.")
		elseif first == "zone" then
			if not rest then
				print("You must provide a zone.")
			else
				if not self:IsZoneOrInstance(rest) then
					print("Zone/instance '" .. rest .. "' not found")
				else
					local type
					if self:IsBattleground(rest) then
						type = "Battleground"
					elseif self:IsInstance(rest) then
						type = "Instance"
					else
						type = "Zone"
					end
					local faction
					if self:IsAlliance(rest) then
						faction = "Alliance"
					elseif self:IsHorde(rest) then
						faction = "Horde"
					else
						faction = "Contested"
					end
					if self:IsHostile(rest) then
						faction = faction .. " (hostile)"
					elseif self:IsFriendly(rest) then
						faction = faction .. " (friendly)"
					end
					local low, high = self:GetLevel(rest)
					print("|cffffff7f" .. rest .. "|r")
					print("  |cffffff7fType:|r " .. type)
					print("  |cffffff7fFaction:|r " .. faction)
					if low ~= -6 and high ~= -6 then
						print("  |cffffff7fLevels:|r " .. low .. "-" .. high)
					end
					if self:DoesZoneHaveInstances(rest) then
						print("  |cffffff7fInstances:|r")
						for instance in self:IterateZoneInstances(rest) do
							local isBG = self:IsBattleground(instance) and " (BG)" or ""
							local low, high = self:GetLevel(instance)
							local faction = ""
							if self:IsAlliance(instance) then
								faction = " - Alliance"
							elseif self:IsHorde(instance) then
								faction = " - Horde"
							end
							if self:IsHostile(instance) then
								faction = faction .. " (hostile)"
							elseif self:IsFriendly(instance) then
								faction = faction .. " (friendly)"
							end
							print("    " .. instance .. isBG .. " - " .. low .. "-" .. high .. faction)
						end
					end
				end
			end
		elseif first == "recommend" then
			print("|cffffff7fRecomended zones:|r")
			for zone in self:IterateRecommendedZones() do
				local low, high = self:GetLevel(zone)
				local faction = ""
				if self:IsAlliance(zone) then
					faction = " - Alliance"
				elseif self:IsHorde(zone) then
					faction = " - Horde"
				end
				if self:IsHostile(zone) then
					faction = faction .. " (hostile)"
				elseif self:IsFriendly(zone) then
					faction = faction .. " (friendly)"
				end
				print("  " .. zone .. " - " .. low .. "-" .. high .. faction)
			end
			if self:HasRecommendedInstances() then
				print("|cffffff7fRecomended instances:|r")
				for instance in self:IterateRecommendedInstances() do
					local isBG = self:IsBattleground(instance) and " (BG)" or ""
					local low, high = self:GetLevel(instance)
					local faction = ""
					if self:IsAlliance(instance) then
						faction = " - Alliance"
					elseif self:IsHorde(instance) then
						faction = " - Horde"
					end
					if self:IsHostile(instance) then
						faction = faction .. " (hostile)"
					elseif self:IsFriendly(instance) then
						faction = faction .. " (friendly)"
					end
					print("  " .. instance .. isBG .. " - " .. low .. "-" .. high .. faction)
				end
			end
		else
			print("Unknown command '" .. first .. "'")
		end
	end


	if oldLib then
		self.frame = oldLib.frame
		self.frame:UnregisterAllEvents()
		self.lowZones = oldLib.lowZones
		self.highZones = oldLib.highZones
		self.zoneInstances = oldLib.zoneInstances
		self.instances = oldLib.instances
		self.recZones = oldLib.recZones
		self.recInstances = oldLib.recInstances
		for k in pairs(self.lowZones) do
			self.lowZones[k] = nil
		end
		for k in pairs(self.highZones) do
			self.highZones[k] = nil
		end
		for k in pairs(self.zoneInstances) do
			self.zoneInstances[k] = nil
		end
		for k in pairs(self.instances) do
			self.instances[k] = nil
		end
	else
		self.frame = CreateFrame("Frame", "TouristLibFrame", UIParent)
		self.lowZones = {}
		self.highZones = {}
		self.zoneInstances = {}
		self.instances = {}
		self.recZones = {}
		self.recInstances = {}
	end
	self.frame:RegisterEvent("PLAYER_LEVEL_UP")
	self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
	self.frame:SetScript("OnEvent", function()
		events[event](self)
	end)
	-- Eastern Kingdoms
	self.lowZones[loc.BOOTY_BAY] = -6
	self.highZones[loc.BOOTY_BAY] = -6
	self.lowZones[loc.DEEPRUN_TRAM] = -6
	self.highZones[loc.DEEPRUN_TRAM] = -6
	self.lowZones[loc.IRONFORGE] = -6
	self.highZones[loc.IRONFORGE] = -6
	self.lowZones[loc.STORMWIND_CITY] = -6
	self.highZones[loc.STORMWIND_CITY] = -6
	self.lowZones[loc.ELWYNN_FOREST] = 1
	self.highZones[loc.ELWYNN_FOREST] = 10
	self.lowZones[loc.DUN_MOROGH] = 1
	self.highZones[loc.DUN_MOROGH] = 10
	self.lowZones[loc.TIRISFAL_GLADES] = 1
	self.highZones[loc.TIRISFAL_GLADES] = 10
	self.lowZones[loc.LOCH_MODAN] = 10
	self.highZones[loc.LOCH_MODAN] = 20
	self.lowZones[loc.SILVERPINE_FOREST] = 10
	self.highZones[loc.SILVERPINE_FOREST] = 20
	self.lowZones[loc.WESTFALL] = 10
	self.highZones[loc.WESTFALL] = 20
	self.lowZones[loc.REDRIDGE_MOUNTAINS] = 15
	self.highZones[loc.REDRIDGE_MOUNTAINS] = 25
	self.lowZones[loc.DUSKWOOD] = 18
	self.highZones[loc.DUSKWOOD] = 30
	self.lowZones[loc.HILLSBRAD_FOOTHILLS] = 20
	self.highZones[loc.HILLSBRAD_FOOTHILLS] = 30
	self.lowZones[loc.WETLANDS] = 20
	self.highZones[loc.WETLANDS] = 30
	self.lowZones[loc.ALTERAC_MOUNTAINS] = 30
	self.highZones[loc.ALTERAC_MOUNTAINS] = 40
	self.lowZones[loc.ARATHI_HIGHLANDS] = 30
	self.highZones[loc.ARATHI_HIGHLANDS] = 40
	self.lowZones[loc.STRANGLETHORN_VALE] = 30
	self.highZones[loc.STRANGLETHORN_VALE] = 45
	self.lowZones[loc.BADLANDS] = 35
	self.highZones[loc.BADLANDS] = 45
	self.lowZones[loc.SWAMP_OF_SORROWS] = 35
	self.highZones[loc.SWAMP_OF_SORROWS] = 45
	self.lowZones[loc.DEADWIND_PASS] = 37
	self.highZones[loc.DEADWIND_PASS] = 60
	self.lowZones[loc.HINTERLANDS] = 40
	self.highZones[loc.HINTERLANDS] = 50
	self.lowZones[loc.SEARING_GORGE] = 43
	self.highZones[loc.SEARING_GORGE] = 50
	self.lowZones[loc.BLASTED_LANDS] = 45
	self.highZones[loc.BLASTED_LANDS] = 55
	self.lowZones[loc.BURNING_STEPPES] = 50
	self.highZones[loc.BURNING_STEPPES] = 58
	self.lowZones[loc.WESTERN_PLAGUELANDS] = 51
	self.highZones[loc.WESTERN_PLAGUELANDS] = 58
	self.lowZones[loc.EASTERN_PLAGUELANDS] = 53
	self.highZones[loc.EASTERN_PLAGUELANDS] = 60

	-- Kalimdor
	self.lowZones[loc.RATCHET] = -6
	self.highZones[loc.RATCHET] = -6
	self.lowZones[loc.GADGETZAN] = -6
	self.highZones[loc.GADGETZAN] = -6
	self.lowZones[loc.ORGRIMMAR] = -6
	self.highZones[loc.ORGRIMMAR] = -6
	self.lowZones[loc.THUNDER_BLUFF] = -6
	self.highZones[loc.THUNDER_BLUFF] = -6
	self.lowZones[loc.UNDERCITY] = -6
	self.highZones[loc.UNDERCITY] = -6
	self.lowZones[loc.DUROTAR] = 1
	self.highZones[loc.DUROTAR] = 10
	self.lowZones[loc.MULGORE] = 1
	self.highZones[loc.MULGORE] = 10
	self.lowZones[loc.DARKSHORE] = 10
	self.highZones[loc.DARKSHORE] = 20
	self.lowZones[loc.BARRENS] = 10
	self.highZones[loc.BARRENS] = 25
	self.lowZones[loc.STONETALON_MOUNTAINS] = 15
	self.highZones[loc.STONETALON_MOUNTAINS] = 27
	self.lowZones[loc.ASHENVALE] = 18
	self.highZones[loc.ASHENVALE] = 30
	self.lowZones[loc.THOUSAND_NEEDLES] = 25
	self.highZones[loc.THOUSAND_NEEDLES] = 35
	self.lowZones[loc.DESOLACE] = 30
	self.highZones[loc.DESOLACE] = 40
	self.lowZones[loc.DUSTWALLOW_MARSH] = 35
	self.highZones[loc.DUSTWALLOW_MARSH] = 45
	self.lowZones[loc.FERALAS] = 40
	self.highZones[loc.FERALAS] = 50
	self.lowZones[loc.TANARIS] = 40
	self.highZones[loc.TANARIS] = 50
	self.lowZones[loc.AZSHARA] = 45
	self.highZones[loc.AZSHARA] = 55
	self.lowZones[loc.FELWOOD] = 48
	self.highZones[loc.FELWOOD] = 55
	self.lowZones[loc.UN_GORO_CRATER] = 48
	self.highZones[loc.UN_GORO_CRATER] = 55
	self.lowZones[loc.SILITHUS] = 55
	self.highZones[loc.SILITHUS] = 60
	self.lowZones[loc.WINTERSPRING] = 55
	self.highZones[loc.WINTERSPRING] = 60
	self.lowZones[loc.HYJAL] = 60
	self.highZones[loc.HYJAL] = 60
	self.lowZones[loc.MOONGLADE] = -6
	self.highZones[loc.MOONGLADE] = -6
	self.lowZones[loc.DARNASSUS] = -6
	self.highZones[loc.DARNASSUS] = -6
	self.lowZones[loc.TELDRASSIL] = 1
	self.highZones[loc.TELDRASSIL] = 10

	-- Battlegrounds (Tiered)
	self.lowZones[loc.ALTERAC_VALLEY] = -6
	self.highZones[loc.ALTERAC_VALLEY] = -6
	self.lowZones[loc.WARSONG_GULCH] = -6
	self.highZones[loc.WARSONG_GULCH] = -6
	self.lowZones[loc.ARATHI_BASIN] = -6
	self.highZones[loc.ARATHI_BASIN] = -6

	-- Instances
	if GetLocale() == "enUS" then
		self.lowZones[loc.STOCKADE] = 24
		self.highZones[loc.STOCKADE] = 32
		self.lowZones[loc.RAGEFIRE_CHASM] = 13
		self.highZones[loc.RAGEFIRE_CHASM] = 18
		self.lowZones[loc.ZUL_FARRAK] = 44
		self.highZones[loc.ZUL_FARRAK] = 54
		self.lowZones[loc.DEADMINES] = 17
		self.highZones[loc.DEADMINES] = 26
		self.lowZones[loc.WAILING_CAVERNS] = 17
		self.highZones[loc.WAILING_CAVERNS] = 24
		self.lowZones[loc.GNOMEREGAN] = 29
		self.highZones[loc.GNOMEREGAN] = 38
		self.lowZones[loc.RAZORFEN_KRAUL] = 29
		self.highZones[loc.RAZORFEN_KRAUL] = 38
		self.lowZones[loc.BLACKFATHOM_DEEPS] = 24
		self.highZones[loc.BLACKFATHOM_DEEPS] = 32
		self.lowZones[loc.SHADOWFANG_KEEP] = 22
		self.highZones[loc.SHADOWFANG_KEEP] = 30
		self.lowZones[loc.SCARLET_MONASTERY] = 34
		self.highZones[loc.SCARLET_MONASTERY] = 45
		self.lowZones[loc.ULDAMAN] = 41
		self.highZones[loc.ULDAMAN] = 51
		self.lowZones[loc.RAZORFEN_DOWNS] = 37
		self.highZones[loc.RAZORFEN_DOWNS] = 46
		self.lowZones[loc.MARAUDON] = 46
		self.highZones[loc.MARAUDON] = 55
		self.lowZones[loc.ONYXIAS_LAIR] = 60
		self.highZones[loc.ONYXIAS_LAIR] = 62
		self.lowZones[loc.BLACKROCK_MOUNTAIN] = 42
		self.highZones[loc.BLACKROCK_MOUNTAIN] = 54
		self.lowZones[loc.CAVERNS_OF_TIME] = 43
		self.highZones[loc.CAVERNS_OF_TIME] = 61
		self.lowZones[loc.TEMPLE_OF_ATAL_HAKKAR] = 50
		self.highZones[loc.TEMPLE_OF_ATAL_HAKKAR] = 60
		self.lowZones[loc.DIRE_MAUL] = 56
		self.highZones[loc.DIRE_MAUL] = 60
		self.lowZones[loc.BLACKROCK_DEPTHS] = 52
		self.highZones[loc.BLACKROCK_DEPTHS] = 60
		self.lowZones[loc.BLACKROCK_SPIRE] = 55
		self.highZones[loc.BLACKROCK_SPIRE] = 60
		self.lowZones[loc.STRATHOLME] = 58
		self.highZones[loc.STRATHOLME] = 60
		self.lowZones[loc.MOLTEN_CORE] = 60
		self.highZones[loc.MOLTEN_CORE] = 62
		self.lowZones[loc.SCHOLOMANCE] = 58
		self.highZones[loc.SCHOLOMANCE] = 60
		self.lowZones[loc.BLACKWING_LAIR] = 60
		self.highZones[loc.BLACKWING_LAIR] = 62
		self.lowZones[loc.ZUL_GURUB] = 60
		self.highZones[loc.ZUL_GURUB] = 62
		self.lowZones[loc.RUINS_OF_AHN_QIRAJ] = 60
		self.highZones[loc.RUINS_OF_AHN_QIRAJ] = 65
		self.lowZones[loc.TEMPLE_OF_AHN_QIRAJ] = 60
		self.highZones[loc.TEMPLE_OF_AHN_QIRAJ] = 65
	else
		self.lowZones[loc.STOCKADE] = 23
		self.highZones[loc.STOCKADE] = 26
		self.lowZones[loc.RAGEFIRE_CHASM] = 13
		self.highZones[loc.RAGEFIRE_CHASM] = 15
		self.lowZones[loc.ZUL_FARRAK] = 43
		self.highZones[loc.ZUL_FARRAK] = 47
		self.lowZones[loc.DEADMINES] = 15
		self.highZones[loc.DEADMINES] = 20
		self.lowZones[loc.WAILING_CAVERNS] = 15
		self.highZones[loc.WAILING_CAVERNS] = 21
		self.lowZones[loc.GNOMEREGAN] = 24
		self.highZones[loc.GNOMEREGAN] = 33
		self.lowZones[loc.RAZORFEN_KRAUL] = 25
		self.highZones[loc.RAZORFEN_KRAUL] = 35
		self.lowZones[loc.BLACKFATHOM_DEEPS] = 20
		self.highZones[loc.BLACKFATHOM_DEEPS] = 27
		self.lowZones[loc.SHADOWFANG_KEEP] = 18
		self.highZones[loc.SHADOWFANG_KEEP] = 25
		self.lowZones[loc.SCARLET_MONASTERY] = 30
		self.highZones[loc.SCARLET_MONASTERY] = 40
		self.lowZones[loc.ULDAMAN] = 35
		self.highZones[loc.ULDAMAN] = 45
		self.lowZones[loc.RAZORFEN_DOWNS] = 35
		self.highZones[loc.RAZORFEN_DOWNS] = 40
		self.lowZones[loc.MARAUDON] = 40
		self.highZones[loc.MARAUDON] = 49
		self.lowZones[loc.ONYXIAS_LAIR] = 60
		self.highZones[loc.ONYXIAS_LAIR] = 62
		self.lowZones[loc.BLACKROCK_MOUNTAIN] = 42
		self.highZones[loc.BLACKROCK_MOUNTAIN] = 54
		self.lowZones[loc.CAVERNS_OF_TIME] = -6
		self.highZones[loc.CAVERNS_OF_TIME] = -6
		self.lowZones[loc.TEMPLE_OF_ATAL_HAKKAR] = 44
		self.highZones[loc.TEMPLE_OF_ATAL_HAKKAR] = 50
		self.lowZones[loc.DIRE_MAUL] = 56
		self.highZones[loc.DIRE_MAUL] = 60
		self.lowZones[loc.BLACKROCK_DEPTHS] = 48
		self.highZones[loc.BLACKROCK_DEPTHS] = 56
		self.lowZones[loc.BLACKROCK_SPIRE] = 53
		self.highZones[loc.BLACKROCK_SPIRE] = 60
		self.lowZones[loc.STRATHOLME] = 55
		self.highZones[loc.STRATHOLME] = 60
		self.lowZones[loc.MOLTEN_CORE] = 60
		self.highZones[loc.MOLTEN_CORE] = 62
		self.lowZones[loc.SCHOLOMANCE] = 56
		self.highZones[loc.SCHOLOMANCE] = 60
		self.lowZones[loc.BLACKWING_LAIR] = 60
		self.highZones[loc.BLACKWING_LAIR] = 62
		self.lowZones[loc.ZUL_GURUB] = 60
		self.highZones[loc.ZUL_GURUB] = 62
		self.lowZones[loc.RUINS_OF_AHN_QIRAJ] = 60
		self.highZones[loc.RUINS_OF_AHN_QIRAJ] = 65
		self.lowZones[loc.TEMPLE_OF_AHN_QIRAJ] = 60
		self.highZones[loc.TEMPLE_OF_AHN_QIRAJ] = 65
	end
	
	self.zoneInstances[loc.STORMWIND_CITY] = loc.STOCKADE
	self.zoneInstances[loc.ELWYNN_FOREST] = self.zoneInstances[loc.ELWYNN_FOREST]
	self.zoneInstances[loc.ORGRIMMAR] = loc.RAGEFIRE_CHASM
	self.zoneInstances[loc.DUROTAR] = self.zoneInstances[loc.ORGRIMMAR]
	self.zoneInstances[loc.DUN_MOROGH] = loc.GNOMEREGAN
	self.zoneInstances[loc.IRONFORGE] = self.zoneInstances[loc.DUN_MOROGH]
	self.zoneInstances[loc.TIRISFAL_GLADES] = loc.SCARLET_MONASTERY
	self.zoneInstances[loc.UNDERCITY] = self.zoneInstances[loc.TIRISFAL_GLADES]
	self.zoneInstances[loc.WESTFALL] = loc.DEADMINES
	self.zoneInstances[loc.SILVERPINE_FOREST] = loc.SHADOWFANG_KEEP
	self.zoneInstances[loc.ALTERAC_MOUNTAINS] = loc.ALTERAC_VALLEY
	self.zoneInstances[loc.ARATHI_HIGHLANDS] = loc.ARATHI_BASIN
	self.zoneInstances[loc.STRANGLETHORN_VALE] = loc.ZUL_GURUB
	self.zoneInstances[loc.SWAMP_OF_SORROWS] = loc.TEMPLE_OF_ATAL_HAKKAR
	self.zoneInstances[loc.SEARING_GORGE] = {
		[loc.MOLTEN_CORE] = true,
		[loc.BLACKWING_LAIR] = true,
		[loc.BLACKROCK_SPIRE] = true,
		[loc.BLACKROCK_DEPTHS] = true,
	}
	self.zoneInstances[loc.BLACKROCK_MOUNTAIN] = self.zoneInstances[loc.SEARING_GORGE]
	self.zoneInstances[loc.BURNING_STEPPES] = self.zoneInstances[loc.SEARING_GORGE]
	self.zoneInstances[loc.EASTERN_PLAGUELANDS] = loc.STRATHOLME
	self.zoneInstances[loc.WESTERN_PLAGUELANDS] = loc.SCHOLOMANCE
	self.zoneInstances[loc.BARRENS] = {
		[loc.WAILING_CAVERNS] = true,
		[loc.RAZORFEN_KRAUL] = true,
		[loc.RAZORFEN_DOWNS] = true
	}
	self.zoneInstances[loc.ASHENVALE] = loc.BLACKFATHOM_DEEPS
	if UnitFactionGroup("player") == "Horde" then
		self.zoneInstances[loc.BARRENS][loc.WARSONG_GULCH] = true
	else
		self.zoneInstances[loc.ASHENVALE] = {
			[loc.BLACKFATHOM_DEEPS] = true,
			[loc.WARSONG_GULCH] = true
		}
	end
	self.zoneInstances[loc.DESOLACE] = loc.MARAUDON
	self.zoneInstances[loc.DUSTWALLOW_MARSH] = loc.ONYXIAS_LAIR
	self.zoneInstances[loc.FERALAS] = loc.DIRE_MAUL
	self.zoneInstances[loc.SILITHUS] = {
		[loc.RUINS_OF_AHN_QIRAJ] = true,
		[loc.TEMPLE_OF_AHN_QIRAJ] = true
	}
	self.zoneInstances[loc.TANARIS] = loc.ZUL_FARRAK
	self.zoneInstances[loc.BADLANDS] = loc.ULDAMAN
	
	for _,instances in pairs(self.zoneInstances) do
		if type(instances) == "table" then
			for instance in pairs(instances) do
				self.instances[instance] = true
			end
		else
			self.instances[instances] = true
		end
	end
	
	events.PLAYER_LEVEL_UP(self)
end

function lib:LibDeactivate(stub)
	events = nil
end

function events:PLAYER_LEVEL_UP()
	playerLevel = UnitLevel("player")
	playerFaction = UnitFactionGroup("player")
	isHorde = playerFaction == "Horde"
	for k in pairs(self.recZones) do
		self.recZones[k] = nil
	end
	for k in pairs(self.recInstances) do
		self.recInstances[k] = nil
	end
	for zone in self.lowZones do
		if not self:IsHostile(zone) then
			local low, high = self:GetLevel(zone)
			if not self:IsInstance(zone) then
				if low <= playerLevel and playerLevel <= high then
					self.recZones[zone] = true
				end
			elseif self:IsBattleground(zone) then
				if playerLevel >= low and (playerLevel == 60 or math.mod(playerLevel, 10) >= 6) then
					self.recInstances[zone] = true
				end
			else
				if low <= playerLevel and playerLevel <= high then
					self.recInstances[zone] = true
				end
			end
		end
	end
end

events.PLAYER_ENTERING_WORLD = events.PLAYER_LEVEL_UP

function lib:GetLevel(zone)
	if self:IsBattleground(zone) then
		if zone == loc.ALTERAC_VALLEY then
			return 51, 60
		elseif playerLevel == 60 then
			return 60, 60
		elseif playerLevel >= 50 then
			return 50, 59
		elseif playerLevel >= 40 then
			return 40, 49
		elseif playerLevel >= 30 then
			return 30, 39
		elseif playerLevel >= 20 or zone == loc.ARATHI_BASIN then
			return 20, 29
		else
			return 10, 19
		end
	end
	return self.lowZones[zone] or -6, self.highZones[zone] or -6
end

function lib:GetLevelColor(zone)
	if self:IsBattleground(zone) then
		if (playerLevel < 51 and zone == loc.ALTERAC_VALLEY) or (playerLevel < 20 and zone == loc.ARATHI_BASIN) or (playerLevel < 10 and zone == loc.WARSONG_GULCH) then
			return 1, 0, 0
		end
		local playerLevel = playerLevel
		if zone == loc.ALTERAC_VALLEY then
			playerLevel = playerLevel - 1
		end
		if playerLevel == 60 then
			return 1, 1, 0
		end
		playerLevel = math.mod(playerLevel, 10)
		if playerLevel <= 5 then
			return 1, playerLevel / 10, 0
		elseif playerLevel <= 7 then
			return 1, (playerLevel - 3) / 4, 0
		else
			return (9 - playerLevel) / 2, 1, 0
		end
	end
	local low, high = self:GetLevel(zone)
	
	if playerLevel <= low - 3 then
		return 1, 0, 0
	elseif playerLevel <= low then
		return 1, (playerLevel - low - 3) / -6, 0
	elseif playerLevel <= (low + high) / 2 then
		return 1, (playerLevel - low) / (high - low) + 0.5, 0
	elseif playerLevel <= high then
		return 2 * (playerLevel - high) / (low - high), 1, 0
	elseif playerLevel <= high + 3 then
		local num = (playerLevel - high) / 6
		return num, 1 - num, num
	else
		return 0.5, 0.5, 0.5
	end
end

function lib:GetFactionColor(zone)
	if self:IsAlliance(zone) then
		if not isHorde then
			return 0, 1, 0
		else
			return 1, 0, 0
		end
	elseif self:IsHorde(zone) then
		if isHorde then
			return 0, 1, 0
		else
			return 1, 0, 0
		end
	else
		return 1, 1, 0
	end
end

local retNil = function() end
local retOne = function(zone, state)
	if state ~= nil then
		return
	else
		return zone
	end
end

local function iterZoneInstances(zone, position)
	local k = next(zone, position)
	return k
end

function lib:IterateZoneInstances(zone)
	if type(self.zoneInstances[zone]) == nil then
		return retNil, nil, nil
	elseif type(self.zoneInstances[zone]) == "table" then
		return iterZoneInstances, self.zoneInstances[zone], nil
	else
		return retOne, self.zoneInstances[zone], nil
	end
end

function lib:DoesZoneHaveInstances(zone)
	return self.zoneInstances[zone] ~= nil
end

local zonesInstances
local function initZonesInstances()
	if not zonesInstances then
		zonesInstances = {}
		for zone in pairs(self.lowZones) do
			zonesInstances[zone] = true
		end
	end
	initZonesInstances = nil
end

local function zoneInstanceIter(_, position)
	local k = next(zonesInstances, position)
	return k
end
function lib:IterateZonesAndInstances()
	if initZonesInstances then
		initZonesInstances()
	end
	return zoneInstanceIter, nil, nil
end

local function zoneIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and self:IsInstance(k) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateZones()
	if initZonesInstances then
		initZonesInstances()
	end
	return zoneIter, nil, nil
end

local function instanceIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and not self:IsInstance(k) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateInstances()
	if initZonesInstances then
		initZonesInstances()
	end
	return instanceIter, nil, nil
end

local function bgIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and not self:IsBattleground(k) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateBattlegrounds()
	if initZonesInstances then
		initZonesInstances()
	end
	return bgIter, nil, nil
end

local function allianceIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and not self:IsAlliance(k) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateAlliance()
	if initZonesInstances then
		initZonesInstances()
	end
	return allianceIter, nil, nil
end

local function hordeIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and not self:IsHorde(k) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateHorde()
	if initZonesInstances then
		initZonesInstances()
	end
	return hordeIter, nil, nil
end

function lib:IterateFriendly()
	if isHorde then
		return self:IterateHordeZonesInstances()
	else
		return self:IterateAllianceZonesInstances()
	end
end

function lib:IterateHostile()
	if isHorde then
		return self:IterateAllianceZonesInstances()
	else
		return self:IterateHordeZonesInstances()
	end
end

local function contestedIter(_, position)
	local k = next(zonesInstances, position)
	while k ~= nil and (self:IsAlliance(k) or self:IsHorde(k)) do
		k = next(zonesInstances, k)
	end
	return k
end
function lib:IterateContested()
	if initZonesInstances then
		initZonesInstances()
	end
	return contestedIter, nil, nil
end

local function recZoneIter(recZones, position)
	local k = next(recZones, position)
	return k
end
function lib:IterateRecommendedZones()
	return recZoneIter, self.recZones, nil
end

function lib:IterateRecommendedInstances()
	return recZoneIter, self.recInstances, nil
end

function lib:HasRecommendedInstances()
	return next(self.recInstances) ~= nil
end

function lib:IsInstance(zone)
	return self.instances[zone] ~= nil
end

function lib:IsZone(zone)
	return self.instances[zone] == nil and not self.lowZones[zone] ~= nil
end

function lib:IsZoneOrInstance(zone)
	return self.lowZones[zone] ~= nil
end

function lib:IsBattleground(zone)
	return zone == loc.WARSONG_GULCH or zone == loc.ARATHI_BASIN or zone == loc.ALTERAC_VALLEY
end

function lib:IsAlliance(zone)
	return zone == loc.IRONFORGE or zone == loc.STORMWIND_CITY or zone == loc.DUN_MOROGH or zone == loc.ELWYNN_FOREST or zone == loc.LOCH_MODAN or zone == loc.WESTFALL or zone == loc.DARNASSUS or zone == loc.TELDRASSIL or zone == loc.DARKSHORE or zone == loc.STOCKADE or zone == loc.GNOMEREGAN or zone == loc.DEADMINES
end

function lib:IsHorde(zone)
	return zone == loc.UNDERCITY or zone == loc.ORGRIMMAR or zone == loc.THUNDER_BLUFF or zone == loc.TIRISFAL_GLADES or zone == loc.SILVERPINE_FOREST or zone == loc.DUROTAR or zone == loc.MULGORE or zone == loc.BARRENS or zone == loc.RAGEFIRE_CHASM or zone == loc.SHADOWFANG_KEEP or zone == loc.WAILING_CAVERNS
end

function lib:IsFriendly(zone)
	if isHorde then
		return self:IsHorde(zone)
	else
		return self:IsAlliance(zone)
	end
end

function lib:IsHostile(zone)
	if isHorde then
		return self:IsAlliance(zone)
	else
		return self:IsHorde(zone)
	end
end

function lib:IsContested(zone)
	return not self:IsAlliance(zone) and not self:IsHorde(zone)
end

TouristLib:Register(lib)
lib = nil
