--[[
Name: Tourist-2.0
Revision: $Rev: 12655 $
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: 12655 $"

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 Z = AceLibrary("Babble-Zone-2.0")

local playerLevel = 1
local _,race = UnitRace("player")
local isHorde = (race == "Orc" or race == "Troll" or race == "Tauren" or race == "Scourge")
local math_mod = math.mod or math.fmod

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 pairs(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 == Z["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 == Z["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 == Z["Alterac Valley"]) or (playerLevel < 20 and zone == Z["Arathi Basin"]) or (playerLevel < 10 and zone == Z["Warsong Gulch"]) then
			return 1, 0, 0
		end
		local playerLevel = playerLevel
		if zone == Z["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 == Z["Warsong Gulch"] or zone == Z["Arathi Basin"] or zone == Z["Alterac Valley"]
end

function Tourist:IsAlliance(zone)
	self:argCheck(zone, 2, "string")
	return zone == Z["Ironforge"] or zone == Z["Stormwind City"] or zone == Z["Dun Morogh"] or zone == Z["Elwynn Forest"] or zone == Z["Loch Modan"] or zone == Z["Westfall"] or zone == Z["Darnassus"] or zone == Z["Teldrassil"] or zone == Z["Darkshore"] or zone == Z["The Stockade"] or zone == Z["Gnomeregan"] or zone == Z["The Deadmines"]
end

function Tourist:IsHorde(zone)
	self:argCheck(zone, 2, "string")
	return zone == Z["Undercity"] or zone == Z["Orgrimmar"] or zone == Z["Thunder Bluff"] or zone == Z["Tirisfal Glades"] or zone == Z["Silverpine Forest"] or zone == Z["Durotar"] or zone == Z["Mulgore"] or zone == Z["The Barrens"] or zone == Z["Ragefire Chasm"] or zone == Z["Shadowfang Keep"] or zone == Z["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[Z["Booty Bay"]] = -6
	self.highZones[Z["Booty Bay"]] = -6
	self.lowZones[Z["Deeprun Tram"]] = -6
	self.highZones[Z["Deeprun Tram"]] = -6
	self.lowZones[Z["Ironforge"]] = -6
	self.highZones[Z["Ironforge"]] = -6
	self.lowZones[Z["Stormwind City"]] = -6
	self.highZones[Z["Stormwind City"]] = -6
	self.lowZones[Z["Elwynn Forest"]] = 1
	self.highZones[Z["Elwynn Forest"]] = 10
	self.lowZones[Z["Dun Morogh"]] = 1
	self.highZones[Z["Dun Morogh"]] = 10
	self.lowZones[Z["Tirisfal Glades"]] = 1
	self.highZones[Z["Tirisfal Glades"]] = 10
	self.lowZones[Z["Loch Modan"]] = 10
	self.highZones[Z["Loch Modan"]] = 20
	self.lowZones[Z["Silverpine Forest"]] = 10
	self.highZones[Z["Silverpine Forest"]] = 20
	self.lowZones[Z["Westfall"]] = 10
	self.highZones[Z["Westfall"]] = 20
	self.lowZones[Z["Redridge Mountains"]] = 15
	self.highZones[Z["Redridge Mountains"]] = 25
	self.lowZones[Z["Duskwood"]] = 18
	self.highZones[Z["Duskwood"]] = 30
	self.lowZones[Z["Hillsbrad Foothills"]] = 20
	self.highZones[Z["Hillsbrad Foothills"]] = 30
	self.lowZones[Z["Wetlands"]] = 20
	self.highZones[Z["Wetlands"]] = 30
	self.lowZones[Z["Alterac Mountains"]] = 30
	self.highZones[Z["Alterac Mountains"]] = 40
	self.lowZones[Z["Arathi Highlands"]] = 30
	self.highZones[Z["Arathi Highlands"]] = 40
	self.lowZones[Z["Stranglethorn Vale"]] = 30
	self.highZones[Z["Stranglethorn Vale"]] = 45
	self.lowZones[Z["Badlands"]] = 35
	self.highZones[Z["Badlands"]] = 45
	self.lowZones[Z["Swamp of Sorrows"]] = 35
	self.highZones[Z["Swamp of Sorrows"]] = 45
	self.lowZones[Z["Deadwind Pass"]] = 37
	self.highZones[Z["Deadwind Pass"]] = 60
	self.lowZones[Z["The Hinterlands"]] = 40
	self.highZones[Z["The Hinterlands"]] = 50
	self.lowZones[Z["Searing Gorge"]] = 43
	self.highZones[Z["Searing Gorge"]] = 50
	self.lowZones[Z["Blasted Lands"]] = 45
	self.highZones[Z["Blasted Lands"]] = 55
	self.lowZones[Z["Burning Steppes"]] = 50
	self.highZones[Z["Burning Steppes"]] = 58
	self.lowZones[Z["Western Plaguelands"]] = 51
	self.highZones[Z["Western Plaguelands"]] = 58
	self.lowZones[Z["Eastern Plaguelands"]] = 53
	self.highZones[Z["Eastern Plaguelands"]] = 60

	-- Kalimdor
	self.lowZones[Z["Ratchet"]] = -6
	self.highZones[Z["Ratchet"]] = -6
	self.lowZones[Z["Gadgetzan"]] = -6
	self.highZones[Z["Gadgetzan"]] = -6
	self.lowZones[Z["Orgrimmar"]] = -6
	self.highZones[Z["Orgrimmar"]] = -6
	self.lowZones[Z["Thunder Bluff"]] = -6
	self.highZones[Z["Thunder Bluff"]] = -6
	self.lowZones[Z["Undercity"]] = -6
	self.highZones[Z["Undercity"]] = -6
	self.lowZones[Z["Durotar"]] = 1
	self.highZones[Z["Durotar"]] = 10
	self.lowZones[Z["Mulgore"]] = 1
	self.highZones[Z["Mulgore"]] = 10
	self.lowZones[Z["Darkshore"]] = 10
	self.highZones[Z["Darkshore"]] = 20
	self.lowZones[Z["The Barrens"]] = 10
	self.highZones[Z["The Barrens"]] = 25
	self.lowZones[Z["Stonetalon Mountains"]] = 15
	self.highZones[Z["Stonetalon Mountains"]] = 27
	self.lowZones[Z["Ashenvale"]] = 18
	self.highZones[Z["Ashenvale"]] = 30
	self.lowZones[Z["Thousand Needles"]] = 25
	self.highZones[Z["Thousand Needles"]] = 35
	self.lowZones[Z["Desolace"]] = 30
	self.highZones[Z["Desolace"]] = 40
	self.lowZones[Z["Dustwallow Marsh"]] = 35
	self.highZones[Z["Dustwallow Marsh"]] = 45
	self.lowZones[Z["Feralas"]] = 40
	self.highZones[Z["Feralas"]] = 50
	self.lowZones[Z["Tanaris"]] = 40
	self.highZones[Z["Tanaris"]] = 50
	self.lowZones[Z["Azshara"]] = 45
	self.highZones[Z["Azshara"]] = 55
	self.lowZones[Z["Felwood"]] = 48
	self.highZones[Z["Felwood"]] = 55
	self.lowZones[Z["Un'Goro Crater"]] = 48
	self.highZones[Z["Un'Goro Crater"]] = 55
	self.lowZones[Z["Silithus"]] = 55
	self.highZones[Z["Silithus"]] = 60
	self.lowZones[Z["Winterspring"]] = 55
	self.highZones[Z["Winterspring"]] = 60
	self.lowZones[Z["Hyjal"]] = 60
	self.highZones[Z["Hyjal"]] = 60
	self.lowZones[Z["Moonglade"]] = -6
	self.highZones[Z["Moonglade"]] = -6
	self.lowZones[Z["Darnassus"]] = -6
	self.highZones[Z["Darnassus"]] = -6
	self.lowZones[Z["Teldrassil"]] = 1
	self.highZones[Z["Teldrassil"]] = 10

	-- Battlegrounds (Tiered)
	self.lowZones[Z["Alterac Valley"]] = -6
	self.highZones[Z["Alterac Valley"]] = -6
	self.lowZones[Z["Warsong Gulch"]] = -6
	self.highZones[Z["Warsong Gulch"]] = -6
	self.lowZones[Z["Arathi Basin"]] = -6
	self.highZones[Z["Arathi Basin"]] = -6

	-- Instances
	if GetLocale() == "enUS" then
		self.lowZones[Z["The Stockade"]] = 24
		self.highZones[Z["The Stockade"]] = 32
		self.lowZones[Z["Ragefire Chasm"]] = 13
		self.highZones[Z["Ragefire Chasm"]] = 18
		self.lowZones[Z["Zul'Farrak"]] = 44
		self.highZones[Z["Zul'Farrak"]] = 54
		self.lowZones[Z["The Deadmines"]] = 17
		self.highZones[Z["The Deadmines"]] = 26
		self.lowZones[Z["Wailing Caverns"]] = 17
		self.highZones[Z["Wailing Caverns"]] = 24
		self.lowZones[Z["Gnomeregan"]] = 29
		self.highZones[Z["Gnomeregan"]] = 38
		self.lowZones[Z["Razorfen Kraul"]] = 29
		self.highZones[Z["Razorfen Kraul"]] = 38
		self.lowZones[Z["Blackfathom Deeps"]] = 24
		self.highZones[Z["Blackfathom Deeps"]] = 32
		self.lowZones[Z["Shadowfang Keep"]] = 22
		self.highZones[Z["Shadowfang Keep"]] = 30
		self.lowZones[Z["Scarlet Monastery"]] = 34
		self.highZones[Z["Scarlet Monastery"]] = 45
		self.lowZones[Z["Uldaman"]] = 41
		self.highZones[Z["Uldaman"]] = 51
		self.lowZones[Z["Razorfen Downs"]] = 37
		self.highZones[Z["Razorfen Downs"]] = 46
		self.lowZones[Z["Maraudon"]] = 46
		self.highZones[Z["Maraudon"]] = 55
		self.lowZones[Z["Onyxia's Lair"]] = 60
		self.highZones[Z["Onyxia's Lair"]] = 62
		self.lowZones[Z["Blackrock Mountain"]] = 42
		self.highZones[Z["Blackrock Mountain"]] = 54
		self.lowZones[Z["Caverns of Time"]] = 43
		self.highZones[Z["Caverns of Time"]] = 61
		self.lowZones[Z["The Temple of Atal'Hakkar"]] = 50
		self.highZones[Z["The Temple of Atal'Hakkar"]] = 60
		self.lowZones[Z["Dire Maul"]] = 56
		self.highZones[Z["Dire Maul"]] = 60
		self.lowZones[Z["Blackrock Depths"]] = 52
		self.highZones[Z["Blackrock Depths"]] = 60
		self.lowZones[Z["Blackrock Spire"]] = 55
		self.highZones[Z["Blackrock Spire"]] = 60
		self.lowZones[Z["Stratholme"]] = 58
		self.highZones[Z["Stratholme"]] = 60
		self.lowZones[Z["Molten Core"]] = 60
		self.highZones[Z["Molten Core"]] = 62
		self.lowZones[Z["Scholomance"]] = 58
		self.highZones[Z["Scholomance"]] = 60
		self.lowZones[Z["Blackwing Lair"]] = 60
		self.highZones[Z["Blackwing Lair"]] = 62
		self.lowZones[Z["Zul'Gurub"]] = 60
		self.highZones[Z["Zul'Gurub"]] = 62
		self.lowZones[Z["Ruins of Ahn'Qiraj"]] = 60
		self.highZones[Z["Ruins of Ahn'Qiraj"]] = 65
		self.lowZones[Z["Temple of Ahn'Qiraj"]] = 60
		self.highZones[Z["Temple of Ahn'Qiraj"]] = 65
		self.lowZones[Z["Naxxramas"]] = 60
		self.highZones[Z["Naxxramas"]] = 70
	else
		self.lowZones[Z["The Stockade"]] = 23
		self.highZones[Z["The Stockade"]] = 26
		self.lowZones[Z["Ragefire Chasm"]] = 13
		self.highZones[Z["Ragefire Chasm"]] = 15
		self.lowZones[Z["Zul'Farrak"]] = 43
		self.highZones[Z["Zul'Farrak"]] = 47
		self.lowZones[Z["The Deadmines"]] = 15
		self.highZones[Z["The Deadmines"]] = 20
		self.lowZones[Z["Wailing Caverns"]] = 15
		self.highZones[Z["Wailing Caverns"]] = 21
		self.lowZones[Z["Gnomeregan"]] = 24
		self.highZones[Z["Gnomeregan"]] = 33
		self.lowZones[Z["Razorfen Kraul"]] = 25
		self.highZones[Z["Razorfen Kraul"]] = 35
		self.lowZones[Z["Blackfathom Deeps"]] = 20
		self.highZones[Z["Blackfathom Deeps"]] = 27
		self.lowZones[Z["Shadowfang Keep"]] = 18
		self.highZones[Z["Shadowfang Keep"]] = 25
		self.lowZones[Z["Scarlet Monastery"]] = 30
		self.highZones[Z["Scarlet Monastery"]] = 40
		self.lowZones[Z["Uldaman"]] = 35
		self.highZones[Z["Uldaman"]] = 45
		self.lowZones[Z["Razorfen Downs"]] = 35
		self.highZones[Z["Razorfen Downs"]] = 40
		self.lowZones[Z["Maraudon"]] = 40
		self.highZones[Z["Maraudon"]] = 49
		self.lowZones[Z["Onyxia's Lair"]] = 60
		self.highZones[Z["Onyxia's Lair"]] = 62
		self.lowZones[Z["Blackrock Mountain"]] = 42
		self.highZones[Z["Blackrock Mountain"]] = 54
		self.lowZones[Z["Caverns of Time"]] = -6
		self.highZones[Z["Caverns of Time"]] = -6
		self.lowZones[Z["The Temple of Atal'Hakkar"]] = 44
		self.highZones[Z["The Temple of Atal'Hakkar"]] = 50
		self.lowZones[Z["Dire Maul"]] = 56
		self.highZones[Z["Dire Maul"]] = 60
		self.lowZones[Z["Blackrock Depths"]] = 48
		self.highZones[Z["Blackrock Depths"]] = 56
		self.lowZones[Z["Blackrock Spire"]] = 53
		self.highZones[Z["Blackrock Spire"]] = 60
		self.lowZones[Z["Stratholme"]] = 55
		self.highZones[Z["Stratholme"]] = 60
		self.lowZones[Z["Molten Core"]] = 60
		self.highZones[Z["Molten Core"]] = 62
		self.lowZones[Z["Scholomance"]] = 56
		self.highZones[Z["Scholomance"]] = 60
		self.lowZones[Z["Blackwing Lair"]] = 60
		self.highZones[Z["Blackwing Lair"]] = 62
		self.lowZones[Z["Zul'Gurub"]] = 60
		self.highZones[Z["Zul'Gurub"]] = 62
		self.lowZones[Z["Ruins of Ahn'Qiraj"]] = 60
		self.highZones[Z["Ruins of Ahn'Qiraj"]] = 65
		self.lowZones[Z["Temple of Ahn'Qiraj"]] = 60
		self.highZones[Z["Temple of Ahn'Qiraj"]] = 65
		self.lowZones[Z["Naxxramas"]] = 60
		self.highZones[Z["Naxxramas"]] = 70
	end
	
	self.zoneInstances[Z["Stormwind City"]] = Z["The Stockade"]
	self.zoneInstances[Z["Elwynn Forest"]] = self.zoneInstances[Z["Stormwind City"]]
	self.zoneInstances[Z["Orgrimmar"]] = Z["Ragefire Chasm"]
	self.zoneInstances[Z["Durotar"]] = self.zoneInstances[Z["Orgrimmar"]]
	self.zoneInstances[Z["Dun Morogh"]] = Z["Gnomeregan"]
	self.zoneInstances[Z["Ironforge"]] = self.zoneInstances[Z["Dun Morogh"]]
	self.zoneInstances[Z["Tirisfal Glades"]] = Z["Scarlet Monastery"]
	self.zoneInstances[Z["Undercity"]] = self.zoneInstances[Z["Tirisfal Glades"]]
	self.zoneInstances[Z["Westfall"]] = Z["The Deadmines"]
	self.zoneInstances[Z["Silverpine Forest"]] = Z["Shadowfang Keep"]
	self.zoneInstances[Z["Alterac Mountains"]] = Z["Alterac Valley"]
	self.zoneInstances[Z["Arathi Highlands"]] = Z["Arathi Basin"]
	self.zoneInstances[Z["Stranglethorn Vale"]] = Z["Zul'Gurub"]
	self.zoneInstances[Z["Swamp of Sorrows"]] = Z["The Temple of Atal'Hakkar"]
	self.zoneInstances[Z["Searing Gorge"]] = {
		[Z["Molten Core"]] = true,
		[Z["Blackwing Lair"]] = true,
		[Z["Blackrock Spire"]] = true,
		[Z["Blackrock Depths"]] = true,
	}
	self.zoneInstances[Z["Blackrock Mountain"]] = self.zoneInstances[Z["Searing Gorge"]]
	self.zoneInstances[Z["Burning Steppes"]] = self.zoneInstances[Z["Searing Gorge"]]
	self.zoneInstances[Z["Eastern Plaguelands"]] = {
		[Z["Stratholme"]] = true,
		[Z["Naxxramas"]] = true
	}
	self.zoneInstances[Z["Western Plaguelands"]] = Z["Scholomance"]
	self.zoneInstances[Z["The Barrens"]] = {
		[Z["Wailing Caverns"]] = true,
		[Z["Razorfen Kraul"]] = true,
		[Z["Razorfen Downs"]] = true
	}
	self.zoneInstances[Z["Ashenvale"]] = Z["Blackfathom Deeps"]
	if UnitFactionGroup("player") == "Horde" then
		self.zoneInstances[Z["The Barrens"]][Z["Warsong Gulch"]] = true
	else
		self.zoneInstances[Z["Ashenvale"]] = {
			[Z["Blackfathom Deeps"]] = true,
			[Z["Warsong Gulch"]] = true
		}
	end
	self.zoneInstances[Z["Desolace"]] = Z["Maraudon"]
	self.zoneInstances[Z["Dustwallow Marsh"]] = Z["Onyxia's Lair"]
	self.zoneInstances[Z["Feralas"]] = Z["Dire Maul"]
	self.zoneInstances[Z["Silithus"]] = {
		[Z["Ruins of Ahn'Qiraj"]] = true,
		[Z["Temple of Ahn'Qiraj"]] = true
	}
	self.zoneInstances[Z["Tanaris"]] = Z["Zul'Farrak"]
	self.zoneInstances[Z["Badlands"]] = Z["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
