--[[
Name: Tourist-2.0
Revision: $Rev: 9447 $
Author(s): ckknight (ckknight@gmail.com)
Website: http://ckknight.wowinterface.com/
Documentation: http://wiki.wowace.com/index.php/Tourist-2.0
SVN: http://svn.wowace.com/root/trunk/TouristLib/Tourist-2.0
Description: A library to provide information about zones and instances.
Dependencies: AceLibrary, Babble-Zone-2.0, AceConsole-2.0 (optional)
]]

local MAJOR_VERSION = "Tourist-2.0"
local MINOR_VERSION = "$Revision: 9447 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

if not AceLibrary:HasInstance("Babble-Zone-2.0") then error(MAJOR_VERSION .. " requires Babble-Zone-2.0") end

local Tourist = {}
local events = {}

local loc
do
	local Z = AceLibrary("Babble-Zone-2.0")
	loc = {
		BOOTY_BAY = Z"Booty Bay",
		DEEPRUN_TRAM = Z"Deeprun Tram",
		IRONFORGE = Z"Ironforge",
		STORMWIND_CITY = Z"Stormwind City",
		ELWYNN_FOREST = Z"Elwynn Forest",
		DUN_MOROGH = Z"Dun Morogh",
		TIRISFAL_GLADES = Z"Tirisfal Glades",
		LOCH_MODAN = Z"Loch Modan",
		SILVERPINE_FOREST = Z"Silverpine Forest",
		WESTFALL = Z"Westfall",
		REDRIDGE_MOUNTAINS = Z"Redridge Mountains",
		DUSKWOOD = Z"Duskwood",
		HILLSBRAD_FOOTHILLS = Z"Hillsbrad Foothills",
		WETLANDS = Z"Wetlands",
		ALTERAC_MOUNTAINS = Z"Alterac Mountains",
		ARATHI_HIGHLANDS = Z"Arathi Highlands",
		STRANGLETHORN_VALE = Z"Stranglethorn Vale",
		BADLANDS = Z"Badlands",
		SWAMP_OF_SORROWS = Z"Swamp of Sorrows",
		DEADWIND_PASS = Z"Deadwind Pass",
		HINTERLANDS = Z"The Hinterlands",
		SEARING_GORGE = Z"Searing Gorge",
		BLASTED_LANDS = Z"Blasted Lands",
		BURNING_STEPPES = Z"Burning Steppes",
		WESTERN_PLAGUELANDS = Z"Western Plaguelands",
		EASTERN_PLAGUELANDS = Z"Eastern Plaguelands",
		RATCHET = Z"Ratchet",
		GADGETZAN = Z"Gadgetzan",
		ORGRIMMAR = Z"Orgrimmar",
		THUNDER_BLUFF = Z"Thunder Bluff",
		UNDERCITY = Z"Undercity",
		DUROTAR = Z"Durotar",
		MULGORE = Z"Mulgore",
		DARKSHORE = Z"Darkshore",
		BARRENS = Z"The Barrens",
		STONETALON_MOUNTAINS = Z"Stonetalon Mountains",
		ASHENVALE = Z"Ashenvale",
		THOUSAND_NEEDLES = Z"Thousand Needles",
		DESOLACE = Z"Desolace",
		DUSTWALLOW_MARSH = Z"Dustwallow Marsh",
		FERALAS = Z"Feralas",
		TANARIS = Z"Tanaris",
		AZSHARA = Z"Azshara",
		FELWOOD = Z"Felwood",
		UN_GORO_CRATER = Z"Un'Goro Crater",
		SILITHUS = Z"Silithus",
		WINTERSPRING = Z"Winterspring",
		HYJAL = Z"Hyjal",
		MOONGLADE = Z"Moonglade",
		DARNASSUS = Z"Darnassus",
		TELDRASSIL = Z"Teldrassil",
		ALTERAC_VALLEY = Z"Alterac Valley",
		WARSONG_GULCH = Z"Warsong Gulch",
		ARATHI_BASIN = Z"Arathi Basin",
		STOCKADE = Z"The Stockade",
		RAGEFIRE_CHASM = Z"Ragefire Chasm",
		ZUL_FARRAK = Z"Zul'Farrak",
		DEADMINES = Z"The Deadmines",
		WAILING_CAVERNS = Z"Wailing Caverns",
		GNOMEREGAN = Z"Gnomeregan",
		RAZORFEN_KRAUL = Z"Razorfen Kraul",
		BLACKFATHOM_DEEPS = Z"Blackfathom Deeps",
		SHADOWFANG_KEEP = Z"Shadowfang Keep",
		SCARLET_MONASTERY = Z"Scarlet Monastery",
		ULDAMAN = Z"Uldaman",
		RAZORFEN_DOWNS = Z"Razorfen Downs",
		MARAUDON = Z"Maraudon",
		ONYXIAS_LAIR = Z"Onyxia's Lair",
		BLACKROCK_MOUNTAIN = Z"Blackrock Mountain",
		CAVERNS_OF_TIME = Z"Caverns of Time",
		TEMPLE_OF_ATAL_HAKKAR = Z"The Temple of Atal'Hakkar",
		DIRE_MAUL = Z"Dire Maul",
		BLACKROCK_DEPTHS = Z"Blackrock Depths",
		BLACKROCK_SPIRE = Z"Blackrock Spire",
		STRATHOLME = Z"Stratholme",
		MOLTEN_CORE = Z"Molten Core",
		SCHOLOMANCE = Z"Scholomance",
		BLACKWING_LAIR = Z"Blackwing Lair",
		ZUL_GURUB = Z"Zul'Gurub",
		RUINS_OF_AHN_QIRAJ = Z"Ruins of Ahn'Qiraj",
		TEMPLE_OF_AHN_QIRAJ = Z"Temple of Ahn'Qiraj",
		NAXXRAMAS = Z"Naxxramas",
	}
end

local playerLevel = 1
local _,race = UnitRace("player")
local isHorde = (race == "Orc" or race == "Troll" or race == "Tauren" or race == "Scourge")

function events:PLAYER_LEVEL_UP()
	playerLevel = UnitLevel("player")
	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 Tourist:GetLevel(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:GetLevelColor(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:GetFactionColor(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:IterateZoneInstances(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:DoesZoneHaveInstances(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist: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 Tourist: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 Tourist: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 Tourist: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 Tourist: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 Tourist:IterateHorde()
	if initZonesInstances then
		initZonesInstances()
	end
	return hordeIter, nil, nil
end

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

function Tourist: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 Tourist: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 Tourist:IterateRecommendedZones()
	return recZoneIter, self.recZones, nil
end

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

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

function Tourist:IsInstance(zone)
	self:argCheck(zone, 2, "string")
	return self.instances[zone] ~= nil
end

function Tourist:IsZone(zone)
	self:argCheck(zone, 2, "string")
	return self.instances[zone] == nil and not self.lowZones[zone] ~= nil
end

function Tourist:IsZoneOrInstance(zone)
	self:argCheck(zone, 2, "string")
	return self.lowZones[zone] ~= nil
end

function Tourist:IsBattleground(zone)
	self:argCheck(zone, 2, "string")
	return zone == loc.WARSONG_GULCH or zone == loc.ARATHI_BASIN or zone == loc.ALTERAC_VALLEY
end

function Tourist:IsAlliance(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:IsHorde(zone)
	self:argCheck(zone, 2, "string")
	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 Tourist:IsFriendly(zone)
	self:argCheck(zone, 2, "string")
	if isHorde then
		return self:IsHorde(zone)
	else
		return self:IsAlliance(zone)
	end
end

function Tourist:IsHostile(zone)
	self:argCheck(zone, 2, "string")
	if isHorde then
		return self:IsAlliance(zone)
	else
		return self:IsHorde(zone)
	end
end

function Tourist:IsContested(zone)
	self:argCheck(zone, 2, "string")
	return not self:IsAlliance(zone) and not self:IsHorde(zone)
end

local function activate(self, oldLib, oldDeactivate)
	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
		self.lowZones[loc.NAXXRAMAS] = 60
		self.highZones[loc.NAXXRAMAS] = 70
	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
		self.lowZones[loc.NAXXRAMAS] = 60
		self.highZones[loc.NAXXRAMAS] = 70
	end
	
	self.zoneInstances[loc.STORMWIND_CITY] = loc.STOCKADE
	self.zoneInstances[loc.ELWYNN_FOREST] = self.zoneInstances[loc.STORMWIND_CITY]
	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] = true,
		[loc.NAXXRAMAS] = true
	}
	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)
	
	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end

local function external(self, major, instance)
	if major == "AceConsole-2.0" then
		local print = print
		if DEFAULT_CHAT_FRAME then
			function print(text)
				DEFAULT_CHAT_FRAME:AddMessage(text)
			end
		end
		instance.RegisterChatCommand(self, { "/tourist", "/touristLib" }, {
			name = MAJOR_VERSION .. "." .. string.gsub(MINOR_VERSION, ".-(%d+).*", "%1"),
			desc = "A library to provide information about zones and instances.",
			type = "group",
			args = {
				zone = {
					name = "Zone",
					desc = "Get information about a zone",
					type = "text",
					usage = "<zone name>",
					get = false,
					set = function(text)
						local type
						if self:IsBattleground(text) then
							type = "Battleground"
						elseif self:IsInstance(text) then
							type = "Instance"
						else
							type = "Zone"
						end
						local faction
						if self:IsAlliance(text) then
							faction = "Alliance"
						elseif self:IsHorde(text) then
							faction = "Horde"
						else
							faction = "Contested"
						end
						if self:IsHostile(text) then
							faction = faction .. " (hostile)"
						elseif self:IsFriendly(text) then
							faction = faction .. " (friendly)"
						end
						local low, high = self:GetLevel(text)
						print("|cffffff7f" .. text .. "|r")
						print("  |cffffff7fType: [|r" .. type .. "|cffffff7f]|r")
						print("  |cffffff7fFaction: [|r" .. faction .. "|cffffff7f]|r")
						if low ~= -6 and high ~= -6 then
							print("  |cffffff7fLevels: [|r" .. low .. "-" .. high .. "|cffffff7f]|r")
						end
						if self:DoesZoneHaveInstances(text) then
							print("  |cffffff7fInstances:|r")
							for instance in self:IterateZoneInstances(text) 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,
					validate = function(text)
						return self:IsZoneOrInstance(text)
					end
				},
				recommend = {
					name = "Recommended Zones",
					desc = "List recommended zones",
					type = "execute",
					func = function()
						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("  |cffffff7f" .. zone .. "|r - " .. 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("  |cffffff7f" .. instance .. "|r" .. isBG .. " - " .. low .. "-" .. high .. faction)
							end
						end
					end
				}
			}
		}, "TOURIST")
	end
end

AceLibrary:Register(Tourist, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
Tourist = nil
