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

local compost
if CompostLib then
	compost = CompostLib:GetInstance('compost-1')
end

local PATTERN_HORDE_FLAG_PICKED_UP, PATTERN_HORDE_FLAG_DROPPED, PATTERN_HORDE_FLAG_CAPTURED, PATTERN_ALLIANCE_FLAG_PICKED_UP, PATTERN_ALLIANCE_FLAG_DROPPED, PATTERN_ALLIANCE_FLAG_CAPTURED, FACTION_DEFILERS, FACTION_FROSTWOLF_CLAN, FACTION_WARSONG_OUTRIDERS, FACTION_LEAGUE_OF_ARATHOR, FACTION_STORMPIKE_GUARD, FACTION_SILVERWING_SENTINELS

local PATTERN_GWSUII_SCORE, PATTERN_GWSUII_BASES
local BGObjectiveDescriptions, BGChatAnnouncements, BGPatternReplacements, BGAcronyms, BattlefieldZoneObjectiveTimes, BattlefieldZoneResourceData   --hC

local deformat = BabbleLib:GetInstance('Deformat 1.2')
local Z = BabbleLib:GetInstance('Zone 1.2')

local WARSONG_GULCH = Z"Warsong Gulch"
local ALTERAC_VALLEY = Z"Alterac Valley"
local ARATHI_BASIN = Z"Arathi Basin"

local locale = GetLocale()
if locale ~= "deDE" then
	locale = "enUS"
end

if locale == "enUS" then
	PATTERN_HORDE_FLAG_PICKED_UP = "The Horde [Ff]lag was picked up by ([^!]+)!"
	PATTERN_HORDE_FLAG_DROPPED = "The Horde [Ff]lag was dropped by (%a+)!"
	PATTERN_HORDE_FLAG_CAPTURED = "(%a+) captured the Horde [Ff]lag!"
	PATTERN_ALLIANCE_FLAG_PICKED_UP = "The Alliance [Ff]lag was picked up by (%a+)!"
	PATTERN_ALLIANCE_FLAG_DROPPED = "The Alliance [Ff]lag was dropped by (%a+)!"
	PATTERN_ALLIANCE_FLAG_CAPTURED = "(%a+) captured the Alliance [Ff]lag!"
	
	FACTION_DEFILERS = "Defilers"
	FACTION_FROSTWOLF_CLAN = "Frostwolf Clan"
	FACTION_WARSONG_OUTRIDERS = "Warsong Outriders"
	
	FACTION_LEAGUE_OF_ARATHOR = "League of Arathor"
	FACTION_STORMPIKE_GUARD = "Stormpike Guard"
	FACTION_SILVERWING_SENTINELS = "Silverwing Sentinels"
	
	BGObjectiveDescriptions = {
		ALLIANCE_CONTROLLED = "Alliance Controlled",
		HORDE_CONTROLLED = "Horde Controlled",
		IN_CONFLICT = "In Conflict",
		UNCONTROLLED = "Uncontrolled",
		DESTROYED = "Destroyed",
	}

	BGChatAnnouncements = {
		BGObjectiveClaimedAnnouncements = {
			PATTERN_OBJECTIVE_CLAIMED_AB = "claims the ([%w ]+).* (%a+) will control",
		},

		BGObjectiveAttackedAnnouncements = {
			PATTERN_OBJECTIVE_ATTACKED_AB = "assaulted the ([%w ]+)",
			PATTERN_OBJECTIVE_ATTACKED_AV0 = "The ([%w ]+) is under attack",
			PATTERN_OBJECTIVE_ATTACKED_AV1 = "^([%w ]+) is under attack",
		},

		BGObjectiveDefendedAnnouncements = {
			PATTERN_OBJECTIVE_DEFENDED_AB = "defended the ([%w ]+)",
		},

		BGObjectiveCapturedAnnouncements = {
			PATTERN_OBJECTIVE_CAPTURED_AB = "The (%a+) has taken the ([%w ]+)",
			PATTERN_OBJECTIVE_CAPTURED_AV0 = "The ([%w ]+) was taken by the (%a+)",
			PATTERN_OBJECTIVE_CAPTURED_AV1 = "^([%w ]+) was taken by the (%a+)",
			PATTERN_OBJECTIVE_CAPTURED_AV2 = "the ([%w ]+) is.*MINE", --and the Irondeep Mine is...MINE!
			PATTERN_OBJECTIVE_CAPTURED_AV3 = "claims the ([%w ]+)",   --Snivvle claims the Coldtooth Mine!
		},

		BGObjectiveDestroyedAnnouncements = {
			PATTERN_OBJECTIVE_DESTROYED_AV0 = "The ([%w ]+) was destroyed",
			PATTERN_OBJECTIVE_DESTROYED_AV1 = "^([%w ]+) was destroyed",
		},
	}

	BGPatternReplacements = {
		["mine"] = "gold mine",
		["southern farm"] = "farm"
	}

	BGAcronyms = {
		[ALTERAC_VALLEY] = "AV",
		[ARATHI_BASIN] = "AB",
		[WARSONG_GULCH] = "WSG",
	}

	PATTERN_GWSUII_SCORE = "(%d+/%d+)"			  --for lifting the score out of the first return value of GetWorldStateUIInfo(index)
	PATTERN_GWSUII_BASES = "Bases: (%d+)"		   --for lifting the number of bases held in Arathi Basin
	PATTERN_GWSUII_RESOURCES = "Resources: (%d+)"   --for lifting the number of bases held in Arathi Basin
	PATTERN_OBJECTIVE_HOLDER = "([%w ]+) Controlled"

elseif locale == "deDE" then
	PATTERN_HORDE_FLAG_PICKED_UP = "([^!]+) hat die [Ff]lagge der Horde aufgenommen!"
	PATTERN_HORDE_FLAG_DROPPED = "(%a+) hat die [Ff]lagge der Horde fallen lassen!"
	PATTERN_HORDE_FLAG_CAPTURED = "(%a+) hat die [Ff]lagge der Horde errungen!"
	PATTERN_ALLIANCE_FLAG_PICKED_UP = "(%a+) hat die [Ff]lagge der Allianz aufgenommen!"
	PATTERN_ALLIANCE_FLAG_DROPPED = "(%a+) hat die [Ff]lagge der Allianz fallen lassen!"
	PATTERN_ALLIANCE_FLAG_CAPTURED = "(%a+) hat die [Ff]lagge der Allianz errungen!"
	
	FACTION_DEFILERS = "Die Entweihten"
	FACTION_FROSTWOLF_CLAN = "Frostwolfklan"
	FACTION_WARSONG_OUTRIDERS = "Warsongvorhut"
	
	FACTION_LEAGUE_OF_ARATHOR = "Liga von Arathor"
	FACTION_STORMPIKE_GUARD = "Stormpike Garde"
	FACTION_SILVERWING_SENTINELS = "Silverwing Schildwache"
	
	BGObjectiveDescriptions = {
		ALLIANCE_CONTROLLED = "Kontrolliert von der Allianz",
		HORDE_CONTROLLED = "Kontrolliert von der Horde",
		IN_CONFLICT = "Umk\195\164mpft",
		UNCONTROLLED = "Unkontrolliert",
		DESTROYED = "Zerst\195\182rt",
	}

	BGChatAnnouncements = {
		BGObjectiveClaimedAnnouncements = {
			PATTERN_OBJECTIVE_CLAIMED_AB0 = "hat das (.+) besetzt.* die (%a+) in",
			PATTERN_OBJECTIVE_CLAIMED_AB1 = "hat den (.+) besetzt.* die (%a+) in",
			PATTERN_OBJECTIVE_CLAIMED_AB2 = "hat die (.+) besetzt.* die (%a+) in",
			PATTERN_OBJECTIVE_CLAIMED_AB3 = "hat (S\195\164gewerk) besetzt.* die (%a+) in",
			PATTERN_OBJECTIVE_CLAIMED_AV0 = "hat den (.+) besetzt.* erlangt die (%a+) die Kontrolle"
		},

		BGObjectiveAttackedAnnouncements = {
			PATTERN_OBJECTIVE_ATTACKED_AB0 = "das (.+) angegriffen",
			PATTERN_OBJECTIVE_ATTACKED_AB1 = "den (.+) angegriffen",
			PATTERN_OBJECTIVE_ATTACKED_AB2 = "die (.+) angegriffen",
			PATTERN_OBJECTIVE_ATTACKED_AV0 = "Das (.+) wird angegriffen.*wird die (%a+) es",
			PATTERN_OBJECTIVE_ATTACKED_AV1 = "Der (.+) wird angegriffen.*wird die (%a+) ihn",
			PATTERN_OBJECTIVE_ATTACKED_AV2 = "Die (.+) wird angegriffen.*wird die (%a+) sie",
		},

		BGObjectiveDefendedAnnouncements = {
			PATTERN_OBJECTIVE_DEFENDED_AB0 = "das (.+) verteidigt",
			PATTERN_OBJECTIVE_DEFENDED_AB1 = "den (.+) verteidigt",
			PATTERN_OBJECTIVE_DEFENDED_AB2 = "die (.+) verteidigt",
		},

		BGObjectiveCapturedAnnouncements = {
			PATTERN_OBJECTIVE_CAPTURED_AB0 = "Die (%a+) hat das (.+) eingenommen",
			PATTERN_OBJECTIVE_CAPTURED_AB1 = "Die (%a+) hat den (.+) eingenommen",
			PATTERN_OBJECTIVE_CAPTURED_AB2 = "Die (%a+) hat die (.+) eingenommen",
			PATTERN_OBJECTIVE_CAPTURED_AV0 = "Das (.+) wurde von der (%a+) erobert",
			PATTERN_OBJECTIVE_CAPTURED_AV1 = "Der (.+) wurde von der (%a+) erobert",
			PATTERN_OBJECTIVE_CAPTURED_AV2 = "geh\195\182rt jetzt die (.+)!",
		},

		BGObjectiveDestroyedAnnouncements = {
			PATTERN_OBJECTIVE_DESTROYED_AV0 = "Der (.+) wurde von der (%a+) zerst\195\182rt",
		},
	}

	BGPatternReplacements = {
		["Schmiede"] = "Schmied",
		["Mine"] = "Goldmine",
		["s\195\188dlichen Hof"] = "Hof",
	}

	BGAcronyms = {
		[ALTERAC_VALLEY] = "AV", -- CHECK
		[ARATHI_BASIN] = "AB", -- CHECK
		[WARSONG_GULCH] = "WSG", -- CHECK
	}

	PATTERN_GWSUII_SCORE = "(%d+/%d+)"	  --for lifting the score out of the first return value of GetWorldStateUIInfo(index)
	PATTERN_GWSUII_BASES = "Basen: (%d+)"   --for lifting the number of bases held in Arathi Basin
	PATTERN_GWSUII_RESOURCES = "Ressourcen: (%d+)"   --for lifting the number of bases held in Arathi Basin -- CHECK
	PATTERN_OBJECTIVE_HOLDER = "Kontrolliert von der ([%w ]+)"
end

BattlefieldZoneObjectiveTimes = {
	[ARATHI_BASIN] = 62.5,
	[ALTERAC_VALLEY] = 302.5,
}

BattlefieldZoneResourceData = {
	[ARATHI_BASIN] = { [0]=0, 5/6, 10/9, 5/3, 10/3, 30, 2000 }
}
-------------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 GloryLib) then
   GloryLib = stub:NewStub();
end

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

local function assert(condition, message)
	if not condition then
		local stack = debugstack()
		local first = string.gsub(stack, "\n.*", "")
		local file = string.gsub(first, "^(.*\\.*).lua:%d+: .*", "%1")
		file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
		if not message then
			local _,_,second = string.find(stack, "\n(.-)\n")
			message = "assertion failed! " .. second
		end
		message = "GloryLib: " .. message
		local i = 1
		for s in string.gfind(stack, "\n([^\n]*)") do
			i = i + 1
			if not string.find(s, file .. "%.lua:%d+:") then
				error(message, i)
				return
			end
		end
		error(message, 2)
		return
	end
	return condition
end

local function argCheck(arg, num, kind, kind2, kind3, kind4)
	if tostring(type(arg)) ~= kind then
		if kind2 then
			if tostring(type(arg)) ~= kind2 then
				if kind3 then
					if tostring(type(arg)) ~= kind3 then
						if kind4 then
							if tostring(type(arg)) ~= kind4 then
								local _,_,func = string.find(debugstack(), "\n.-`(.-)'\n")
								assert(false, format("Bad argument #%d to `%s' (%s, %s, %s, or %s expected, got %s)", num, func, kind, kind2, kind3, kind4, type(arg)))
							end
						else
							local _,_,func = string.find(debugstack(), "\n.-`(.-)'\n")
							assert(false, format("Bad argument #%d to `%s' (%s, %s, or %s expected, got %s)", num, func, kind, kind2, kind3, type(arg)))
						end
					end
				else
					local _,_,func = string.find(debugstack(), "\n.-`(.-)'\n")
					assert(false, format("Bad argument #%d to `%s' (%s or %s expected, got %s)", num, func, kind, kind2, type(arg)))
				end
			end
		else
			local _,_,func = string.find(debugstack(), "\n.-`(.-)'\n")
			assert(false, format("Bad argument #%d to `%s' (%s expected, got %s)", num, func, kind, type(arg)))
		end
	end
end

local lib = {}
local events = {}

local playerFaction = UnitFactionGroup("player")
local isHorde = playerFaction == "Horde"
local playerName = UnitName("player")

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_GLORY" .. i, nil)
		end
	end
	SLASH_GLORY1 = "/glory"
	SLASH_GLORY2 = "/glorylib"
	local function print(msg)
		DEFAULT_CHAT_FRAME:AddMessage(msg)
	end
	SlashCmdList["GLORY"] = 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("|cffffff7fGloryLib v" .. MAJOR_VERSION .. "." .. MINOR_VERSION .. ":|r")
			print("  |cffffff7fhonor|r - general honor information.")
			print("  |cffffff7fbg|r - battleground information.")
		elseif first == "honor" then
			print("|cffffff7fToday's HKs:|r - " .. self:GetTodayHKs())
			print("|cffffff7fToday's Deaths:|r - " .. self:GetTodayDeaths())
			print("|cffffff7fToday's HK Honor:|r - " .. self:GetTodayHKHonor())
			print("|cffffff7fToday's Bonus Honor:|r - " .. self:GetTodayBonusHonor())
			print("|cffffff7fToday's Honor:|r - " .. self:GetTodayHonor())
			local s
			if self:IsPermanentPvP() then
				s = "Flagged"
			elseif self:IsInBattlegrounds() then
				s = "Battlegrounds"
			else
				local t = self:GetPvPCooldown()
				if t == 0 then
					s = "None"
				else
					local min = floor(t / 60)
					local sec = floor(mod(t, 60))
					s = format("%d:%02d", min, sec)
				end
			end
			print("|cffffff7fPvP Cooldown:|r - " .. s)
			print("|cffffff7fRank Limit:|r - " .. format("%s (%d)", self:GetRankLimitInfo()))
			print("|cffffff7fRating Limit:|r - " .. self:GetRatingLimit())
		elseif first == "bg" then
			print("|cffffff7fBG Score:|r - " .. self:GetBattlegroundsWins() .. "-" .. self:GetBattlegroundsLosses())
			print("|cffffff7fWSG Score:|r - " .. self:GetWarsongGulchWins() .. "-" .. self:GetWarsongGulchLosses())
			print("|cffffff7fAB Score:|r - " .. self:GetArathiBasinWins() .. "-" .. self:GetArathiBasinLosses())
			print("|cffffff7fAV Score:|r - " .. self:GetAlteracValleyWins() .. "-" .. self:GetAlteracValleyLosses())
			if self:IsInBattlegrounds() then
				print("|cffffff7fCurrent:|r - " .. self:GetActiveBattlegroundUniqueID())
				print("|cffffff7fStanding:|r - " .. self:GetStanding())
				print("|cffffff7fKilling Blows:|r - " .. self:GetKillingBlows())
				print("|cffffff7fHonorable Kills:|r - " .. self:GetHonorableKills())
				print("|cffffff7fDeaths:|r - " .. self:GetDeaths())
				print("|cffffff7fBonus Honor:|r - " .. self:GetBonusHonor())
				if self:IsInWarsongGulch() then
					print("|cffffff7fFriendly FC:|r - " .. (self:GetFriendlyFlagCarrier() or NONE))
					print("|cffffff7fHostile FC:|r - " .. (self:GetHostileFlagCarrier() or NONE))
				else
					print("|cffffff7fFriendly Bases:|r - " .. self:GetNumFriendlyBases())
					print("|cffffff7fHostile Bases:|r - " .. self:GetNumHostileBases())
					print("|cffffff7fFriendly Resources:|r - " .. self:GetNumFriendlyResources())
					print("|cffffff7fHostile Resources:|r - " .. self:GetNumHostileResources())
				end
				print("|cffffff7fFriendly Players:|r - " .. self:GetNumFriendlyPlayers())
				print("|cffffff7fHostile Players:|r - " .. self:GetNumHostilePlayers())
			end
		end
	end
	
	if oldLib then
		self.frame = oldLib.frame
		self.frame:UnregisterAllEvents()
		self.registry = oldLib.registry
	else
		self.frame = CreateFrame("Frame", "GloryLibFrame", UIParent)
		self.registry = {}
	end
	self.frame:RegisterEvent("ADDON_LOADED")
	self.frame:RegisterEvent("VARIABLES_LOADED")
	self.frame:RegisterEvent("PLAYER_LOGOUT")
	self.frame:RegisterEvent("CHAT_MSG_COMBAT_HONOR_GAIN")
	self.frame:RegisterEvent("CHAT_MSG_BG_SYSTEM_NEUTRAL")
	self.frame:RegisterEvent("CHAT_MSG_BG_SYSTEM_HORDE")
	self.frame:RegisterEvent("CHAT_MSG_MONSTER_YELL")
	self.frame:RegisterEvent("CHAT_MSG_BG_SYSTEM_ALLIANCE")
	self.frame:RegisterEvent("CHAT_MSG_COMBAT_FACTION_CHANGE")
	self.frame:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE")
	self.frame:RegisterEvent("CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS")
	self.frame:RegisterEvent("UPDATE_WORLD_STATES")
	self.frame:RegisterEvent("PLAYER_ENTERING_WORLD")
	self.frame:RegisterEvent("UNIT_PVP_UPDATE")
	self.frame:RegisterEvent("PLAYER_DEAD")
	self.frame:SetScript("OnEvent", function()
		events[event](self)
	end)
	events.VARIABLES_LOADED(self)
	
	if not oldLib then
		local old_TogglePVP = TogglePVP
		function TogglePVP()
			GloryLib:GetInstance(MAJOR_VERSION):_TogglePVP()
			
			old_TogglePVP()
		end
	end
	
	self.battlefieldObjectiveStatus = compost and compost:Acquire() or {}
	self.pvpTime = 0
	self.currentBonusHonor = 0
	self.lastHostileTime = 0
	self.aLastResources = 0
	self.hLastResources = 0
	self.aLastBases = 0
	self.hLastBases	= 0
	self.aLastUpdate = 0
	self.hLastUpdate = 0
	self.aResourceTTV = 0
	self.hResourceTTV = 0
end

function lib:LibDeactivate(stub)
	events = nil
	if compost then
		compost:Reclaim(self.battlefieldObjectiveStatus)
	end
	self.battlefieldObjectiveStatus = nil
end

local function Trigger(self, event, a1, a2, a3, a4, a5, a6, a7, a8)
	if self.registry[event] then
		for func, arg in pairs(self.registry[event]) do
			if type(func) == "function" then
				func(arg, a1, a2, a3, a4, a5, a6, a7, a8)
			else
				func[arg](func, a1, a2, a3, a4, a5, a6, a7, a8)
			end
		end
	end
end

local function CheckNewWeek(self)
	local _,_,lastWeekHonor,_ = GetPVPLastWeekStats()
	if lastWeekHonor ~= self.data.lastWeek then
		self.data.lastWeek = lastWeekHonor
		Trigger(self, "NEW_WEEK")
	end
end

local function CheckNewDay(self)
	local _,_,yesterdayHonor = GetPVPYesterdayStats()
	local lifetimeHK,_,_ = GetPVPLifetimeStats()	
	if yesterdayHonor ~= self.data.yesterday and not (yesterdayHonor == 0 and lifetimeHK == 0) then
		self.data.yesterday = yesterdayHonor
		self.data.hks = {}
		self.data.todayHK = 0
		self.data.todayHKHonor = 0
		self.data.todayBonusHonor = 0
		self.data.todayDeaths = 0
		Trigger(self, "NEW_DAY")
	end
end

local function IncreaseHKs(self, person)
	self.data.todayHK = self.data.todayHK + 1
	self.data.hks[person] = (self.data.hks[person] or 0) + 1
	return self.data.hks[person]
end

local function IncreaseHKHonor(self, amount)
	self.data.todayHKHonor = self.data.todayHKHonor + amount
end

local function IncreaseBonusHonor(self, amount)
	self.data.todayBonusHonor = self.data.todayBonusHonor + amount
end

local function IncreaseBattlegroundsWins(self)
	if self:IsInAlteracValley() then
		self.data.avWin = self.data.avWin + 1
		Trigger(self, "BG_WIN_AV")
	elseif self:IsInArathiBasin() then
		self.data.abWin = self.data.abWin + 1
		Trigger(self, "BG_WIN_AB")
	else
		self.data.wsgWin = self.data.wsgWin + 1
		Trigger(self, "BG_WIN_WSG")
	end
	Trigger(self, "BG_WIN")
end

local function IncreaseBattlegroundsLosses(self)
	if self:IsInAlteracValley() then
		self.data.avLoss = self.data.avLoss + 1
		Trigger(self, "BG_LOSS_AV")
	elseif self:IsInArathiBasin() then
		self.data.abLoss = self.data.abLoss + 1
		Trigger(self, "BG_LOSS_AB")
	else
		self.data.wsgLoss = self.data.wsgLoss + 1
		Trigger(self, "BG_LOSS_WSG")
	end
	Trigger(self, "BG_LOSS")
end

local function IncreaseDeaths(self)
	self.data.todayDeaths = self.data.todayDeaths + 1
	Trigger(self, "DEATH")
end

local db

local function VerifyData(self)
	if not self.data then
		if type(GloryLibDB) ~= "table" then
			GloryLibDB = {}
		end
		db = GloryLibDB
		if type(db[MAJOR_VERSION]) ~= "table" then
			db[MAJOR_VERSION] = {}
		end
		self.data = db[MAJOR_VERSION]
	elseif db ~= GloryLibDB then
		local old = db
		local new = GloryLibDB
		if type(new) ~= "table" then
			GloryLibDB = old
		else
			for k in pairs(old) do
				if not new[k] then
					new[k] = old[k]
				elseif new[k].time == nil then
					new[k] = old[k]
				elseif old[k].time == nil then
					-- keep new
				elseif new[k].time < old[k].time then
					new[k] = old[k]
				end
			end
			db = new
			self.data = db[MAJOR_VERSION]
		end
	end
	if not self.data.hks then self.data.hks = {} end
	if not self.data.todayDeaths then self.data.todayDeaths = 0 end
	if not self.data.todayHK then self.data.todayHK = 0 end
	if not self.data.todayHKHonor then self.data.todayHKHonor = 0 end
	if not self.data.todayBonusHonor then self.data.todayBonusHonor = 0 end
	if not self.data.wsgWin then self.data.wsgWin = 0 end
	if not self.data.wsgLoss then self.data.wsgLoss = 0 end
	if not self.data.abWin then self.data.abWin = 0 end
	if not self.data.abLoss then self.data.abLoss = 0 end
	if not self.data.avWin then self.data.avWin = 0 end
	if not self.data.avLoss then self.data.avLoss = 0 end
	if not self.data.yesterday then self.data.yesterday = 0 end
	if not self.data.lastWeek then self.data.lastWeek = 0 end
	
	CheckNewDay(self)
	CheckNewWeek(self)
	events.UNIT_PVP_UPDATE(self)
end

function events:ADDON_LOADED()
	VerifyData(self)
end

function events:VARIABLES_LOADED()
	VerifyData(self)
end

function events:PLAYER_LOGOUT()
	self.data.time = time()
end

function events:CHAT_MSG_COMBAT_HONOR_GAIN()
	CheckNewDay(self)
	local name, rank, honor = deformat(arg1, COMBATLOG_HONORGAIN)
	if name then
		local kills = IncreaseHKs(self, name)
		local factor = (11 - kills) / 10
		if factor < 0 then
			factor = 0
		end
		local realHonor = ceil(honor * factor)
		IncreaseHKHonor(self, realHonor)
		Trigger(self, "GAIN_HK", rank, name, realHonor, kills)
		return
	end
	
	local bonus = deformat(arg1, COMBATLOG_HONORAWARD)
	if bonus then
		bonus = tonumber(bonus)
		IncreaseBonusHonor(self, bonus)
		Trigger(self, "GAIN_BONUS_HONOR", bonus)
	end
end

function events:CHAT_MSG_BG_SYSTEM_NEUTRAL()
	if string.find(string.lower(arg1), string.lower(VICTORY_TEXT0)) then
		if isHorde then
			IncreaseBattlegroundsWins(self)
		else
			IncreaseBattlegroundsLosses(self)
		end
	elseif string.find(string.lower(arg1), string.lower(VICTORY_TEXT1)) then
		if not isHorde then
			IncreaseBattlegroundsWins(self)
		else
			IncreaseBattlegroundsLosses(self)
		end
	end
end

function events:CHAT_MSG_BG_SYSTEM_HORDE()
	if self:IsInWarsongGulch() then
		local _, _, hordeFC = string.find(arg1, PATTERN_ALLIANCE_FLAG_PICKED_UP)
		if hordeFC then
			self.hordeFC = hordeFC
			Trigger(self, "ALLIANCE_FLAG_PICKED_UP", self.hordeFC)
			Trigger(self, "ALLIANCE_FLAGCARRIER_UPDATE", self.hordeFC)
			if not isHorde then
				Trigger(self, "FRIENDLY_FLAG_PICKED_UP", self.hordeFC)
				Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", self.hordeFC)
			else
				Trigger(self, "HOSTILE_FLAG_PICKED_UP", self.hordeFC)
				Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", self.hordeFC)
			end
			return
		end
		
		if string.find(arg1, PATTERN_ALLIANCE_FLAG_CAPTURED) then
			local hordeFC = self.hordeFC
			self.allianceFC = nil
			self.hordeFC = nil
			Trigger(self, "ALLIANCE_FLAG_CAPTURED", hordeFC)
			if not isHorde then
				Trigger(self, "FRIENDLY_FLAG_CAPTURED", hordeFC)
			else
				Trigger(self, "HOSTILE_FLAG_CAPTURED", hordeFC)
			end
			Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", nil)
			Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", nil)
			return
		end
		
		if string.find(arg1, PATTERN_HORDE_FLAG_DROPPED) then
			local allianceFC = self.allianceFC
			self.allianceFC = nil
			Trigger(self, "HORDE_FLAG_DROPPED", allianceFC)
			if isHorde then
				Trigger(self, "FRIENDLY_FLAG_DROPPED", allianceFC)
				Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", nil)
			else
				Trigger(self, "HOSTILE_FLAG_DROPPED", allianceFC)
				Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", nil)
			end
			return
		end
	elseif self:IsInArathiBasin() or self:IsInAlteracValley() then
		events.BattlefieldObjectiveEventProcessing(self)
	end
end
 
function events:CHAT_MSG_BG_SYSTEM_ALLIANCE()
	if self:IsInWarsongGulch() then
		local _, _, allianceFC = string.find(arg1, PATTERN_HORDE_FLAG_PICKED_UP)
		if allianceFC then
			self.allianceFC = allianceFC
			Trigger(self, "HORDE_FLAG_PICKED_UP", self.allianceFC)
			if isHorde then
				Trigger(self, "FRIENDLY_FLAG_PICKED_UP", self.allianceFC)
				Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", self.allianceFC)
			else
				Trigger(self, "HOSTILE_FLAG_PICKED_UP", self.allianceFC)
				Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", self.allianceFC)
			end
			return
		end
		
		if string.find(arg1, PATTERN_HORDE_FLAG_CAPTURED) then
			Trigger(self, "HORDE_FLAG_CAPTURED", self.allianceFC)
			if isHorde then
				Trigger(self, "FRIENDLY_FLAG_CAPTURED", self.allianceFC)
			else
				Trigger(self, "HOSTILE_FLAG_CAPTURED", self.allianceFC)
			end
			self.allianceFC = nil
			self.hordeFC = nil
			Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", nil)
			Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", nil)
			return
		end
		
		if string.find(arg1, PATTERN_ALLIANCE_FLAG_DROPPED) then
			Trigger(self, "ALLIANCE_FLAG_DROPPED", self.hordeFC)
			if not isHorde then
				Trigger(self, "FRIENDLY_FLAG_DROPPED", self.hordeFC)
				Trigger(self, "HOSTILE_FLAGCARRIER_UPDATE", nil)
			else
				Trigger(self, "HOSTILE_FLAG_DROPPED", self.hordeFC)
				Trigger(self, "FRIENDLY_FLAGCARRIER_UPDATE", nil)
			end
			self.hordeFC = nil
			return
		end
	elseif self:IsInArathiBasin() or self:IsInAlteracValley() then
		events.BattlefieldObjectiveEventProcessing(self)
	end
end

function events:CHAT_MSG_MONSTER_YELL()
	if self:IsInAlteracValley() then
		if string.find(string.lower(arg1), string.lower(VICTORY_TEXT0)) then
			if isHorde then
				IncreaseBattlegroundsWins(self)
			else
				IncreaseBattlegroundsLosses(self)
			end
		elseif string.find(string.lower(arg1), string.lower(VICTORY_TEXT1)) then
			if not isHorde then
				IncreaseBattlegroundsWins(self)
			else
				IncreaseBattlegroundsLosses(self)
			end
		else
			events.BattlefieldObjectiveEventProcessing(self)
		end
	end
end
 
function events:BattlefieldObjectiveEventProcessing() 
	local node, faction
	for k, pattern in pairs(BGChatAnnouncements.BGObjectiveClaimedAnnouncements) do
		_, _, node, faction = string.find(arg1, pattern)
		if node then
			if node == FACTION_ALLIANCE or node == FACTION_HORDE then
				node, faction = faction, node
			end
			Trigger(self, "OBJECTIVE_CLAIMED", BGPatternReplacements[node] or node, faction)
			events.OnObjectiveClaimed(self, BGPatternReplacements[node] or node, faction)
			return
		end
	end
	for k, pattern in pairs(BGChatAnnouncements.BGObjectiveCapturedAnnouncements) do
		_, _, node, faction = string.find(arg1, pattern)
		if node then
			if node == FACTION_ALLIANCE or node == FACTION_HORDE then
				node, faction = faction, node
			end
			Trigger(self, "OBJECTIVE_CAPTURED", BGPatternReplacements[node] or node, faction)
			events.OnObjectiveCaptured(self, BGPatternReplacements[node] or node, faction)
			return
		end
	end
	for  k, pattern in pairs(BGChatAnnouncements.BGObjectiveAttackedAnnouncements) do
		_, _, node = string.find(arg1, pattern)
		if node then
			Trigger(self, "OBJECTIVE_ATTACKED", BGPatternReplacements[node] or node)
			events.OnObjectiveAttacked(self, BGPatternReplacements[node] or node)
			return
		end
	end
	for k, pattern in pairs(BGChatAnnouncements.BGObjectiveDefendedAnnouncements) do
		_, _, node = string.find(arg1, pattern)
		if node then
			Trigger(self, "OBJECTIVE_DEFENDED", BGPatternReplacements[node] or node)
			events.OnObjectiveDefended(self, BGPatternReplacements[node] or node)
			return
		end
	end
	for k, pattern in pairs(BGChatAnnouncements.BGObjectiveDestroyedAnnouncements) do
		_, _, node = string.find(arg1, pattern)
		if node then
			Trigger(self, "OBJECTIVE_DESTROYED", BGPatternReplacements[node] or node)
			events.OnObjectiveDestroyed(self, BGPatternReplacements[node] or node)
			return
		end
	end
end 

if not FACTION_STANDING_INCREASED then -- 1.10
	function events:CHAT_MSG_COMBAT_FACTION_CHANGE()
		local faction, rep = deformat(arg1, FACTION_STANDING_INCREASED1)
		if not faction then
			faction, rep = deformat(arg1, FACTION_STANDING_INCREASED2)
			if not faction then
				faction, rep = deformat(arg1, FACTION_STANDING_INCREASED3)
				if not faction then
					faction, rep = deformat(arg1, FACTION_STANDING_INCREASED4)
				end
			end
		end
		if faction and rep then
			if faction == FACTION_DEFILERS or faction == FACTION_FROSTWOLF_CLAN or faction == FACTION_WARSONG_OUTRIDERS or faction == FACTION_LEAGUE_OF_ARATHOR or faction == FACTION_STORMPIKE_GUARD or faction == FACTION_SILVERWING_SENTINELS then
				Trigger(self, "FACTION_GAIN", faction, rep)
			end
		end
	end
else -- 1.11
	function events:CHAT_MSG_COMBAT_FACTION_CHANGE()
		local faction, rep = deformat(arg1, FACTION_STANDING_INCREASED)
		if faction and rep then
			if faction == FACTION_DEFILERS or faction == FACTION_FROSTWOLF_CLAN or faction == FACTION_WARSONG_OUTRIDERS or faction == FACTION_LEAGUE_OF_ARATHOR or faction == FACTION_STORMPIKE_GUARD or faction == FACTION_SILVERWING_SENTINELS then
				Trigger(self, "FACTION_GAIN", faction, rep)
			end
		end
	end
end

function events:CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS()
	self.lastHostileTime = GetTime()
end
events.CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE = events.CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS

function events:UNIT_PVP_UPDATE()
	if not UnitIsPVP("player") and self.permaPvP then
		self.permaPvP = false
		Trigger(self, "UPDATE_PERMANENT_PVP", self.permaPvP)
	end
	if UnitIsPVP("player") or self:IsInBattlegrounds() then
		self.pvpTime = GetTime()
		Trigger(self, "UPDATE_PVP_COOLDOWN", self:GetPvPCooldown())
	else
		Trigger(self, "UPDATE_PVP_COOLDOWN", 0)
	end
end

function events:UPDATE_WORLD_STATES()
	local resData = BattlefieldZoneResourceData[self:GetActiveBattlefieldZone()]
	if resData and self:GetNumAllianceBases() and self:GetNumHordeBases() then
		-- Common
		local goalResources = resData[table.getn(resData)]
		-- Alliance
		_, _, resources = string.find(self:GetAllianceScoreString(), "(%d+)/")
		bases = self:GetNumAllianceBases()
		if resources and bases and (resources ~= self.aLastResources or bases ~= self.aLastBases) then
			self.aResourceTTV = (goalResources - resources) / resData[bases]
			self.aLastResources = resources
			self.aLastBases = bases
			self.aLastUpdate = GetTime()
		end
		-- Horde
		_, _, resources = string.find(self:GetHordeScoreString(), "(%d+)/")
		bases = self:GetNumHordeBases()
		if resources and bases and (resources ~= self.hLastResources or bases ~= self.hLastBases) then
			self.hResourceTTV = (goalResources - resources) / resData[bases]
			self.hLastResources = resources
			self.hLastBases = bases
			self.hLastUpdate = GetTime()
		end
	end
end

function events:PLAYER_ENTERING_WORLD()
	playerFaction = UnitFactionGroup("player")
	isHorde = playerFaction == "Horde"
	events.UNIT_PVP_UPDATE(self)
	SetMapToCurrentZone()
	if self:IsInBattlegrounds() and GetNumMapLandmarks() > 0 then
		events.InitializeBattlefieldObjectives(self)
	else
		events.ClearBattlefieldObjectives(self)
	end
end

function events:PLAYER_DEAD()
	if GetTime() <= self.lastHostileTime + 15 then
		IncreaseDeaths(self)
	end
end

function lib:IsInBattlegrounds()
--	local zone = GetRealZoneText()
--	return zone == WARSONG_GULCH or zone == ARATHI_BASIN or zone == ALTERAC_VALLEY
	return (MiniMapBattlefieldFrame.status == "active")
end

function lib:IsInWarsongGulch()
	return GetRealZoneText() == WARSONG_GULCH
end

function lib:IsInArathiBasin()
	return GetRealZoneText() == ARATHI_BASIN
end

function lib:IsInAlteracValley()
	return GetRealZoneText() == ALTERAC_VALLEY
end

local function copyTable(to, from)
	for k, v in pairs(from) do
		to[k] = v
	end
	return to
end

local tdate, start, done
local firstbg = { [2006] = 2, }
local function GetBattlegroundWeek(bgdate)
	local bgweekday = math.mod(bgdate.wday + 4, 7) + 1
	local bgweek = math.floor((bgdate.yday + 6 - bgweekday) / 7) + 1
	if not firstbg[bgdate.year] then
		if not tdate then
			tdate = {}
		end
		tdate = copyTable(tdate, bgdate)
		tdate.day = 1
		tdate.month = 1
		tdate = date("*t", time(tdate))
		local d = GetBattlegroundWeek(date)
		tdate.day = 31
		tdate.month = 12
		tdate.year = tdate.year - 1
		tdate = date("*t", time(tdate))
		local _, _, bg = GetBattlegroundWeek(date)
		if d == 1 then
			firstbg[bgdate.year] = math.mod(bg, 4) + 1
		else
			firstbg[bgdate.year] = bg
		end
	end
	local bg = math.mod(bgweek -1 + firstbg[bgdate.year], 4) + 1
	return bgweekday, bgweek, bg
end

local function GetCurrentOrNextBattlegroundWeekend(week)
	local now = date("*t")
	local bgweekday, bgweek, bg = GetBattlegroundWeek(now)
	local bginweeks
	if bg <= week then
		bginweeks = week - bg
	else
		bginweeks = week + 4 - bg
	end
	if not start then
		start = {}
	end
	start = copyTable(start, now)
	start.day = start.day + 4 - bgweekday + 7 * bginweeks
	start.hour = 0
	start.min = 0
	start.sec = 0
	start = date("*t", time(start))
	if not done then
		done = {}
	end
	done = copyTable(done, now)
	done.day = done.day + 7 - bgweekday + 7 * bginweeks
	done.hour = 23
	done.min = 59
	done.sec = 59
	done = date("*t", time(done))
	local sMonth
	local dMonth
	if start.month == done.month then
		sMonth = date("%B", time(start))
		dMonth = sMonth
	else
		sMonth = date("%b", time(start))
		dMonth = date("%b", time(done))
	end
	return sMonth, start.day, dMonth, done.day, time(start) <= time(now) and time(now) <= time(done)
end

function lib:GetCurrentOrNextArathiWeekend()
	return GetCurrentOrNextBattlegroundWeekend(4)
end

function lib:GetCurrentOrNextWarsongWeekend()
	return GetCurrentOrNextBattlegroundWeekend(3)
end

function lib:GetCurrentOrNextAlteracWeekend()
	return GetCurrentOrNextBattlegroundWeekend(2)
end

function lib:_TogglePVP()
	self.permaPvP = not self.permaPvP
	if not UnitIsPVP("player") then
		self.permaPvP = true
	end
	Trigger(self, "UPDATE_PERMANENT_PVP", self.permaPvP)
	self.pvpTime = GetTime()
end

function lib:GetTodayHKs(person)
	if not self.data then
		return 0
	end
	if person then
		argCheck(person, 2, "string")
		return self.data.hks[person] or 0
	else
		return self.data.todayHK
	end
end

function lib:GetTodayDeaths()
	if not self.data then
		return 0
	end
	return self.data.todayDeaths
end

function lib:GetTodayHKHonor()
	if not self.data then
		return 0
	end
	return self.data.todayHKHonor
end

function lib:GetTodayBonusHonor()
	if not self.data then
		return 0
	end
	return self.data.todayBonusHonor
end

function lib:GetTodayHonor()
	if not self.data then
		return 0
	end
	return self.data.todayHKHonor + self.data.todayBonusHonor
end

function lib:GetBattlegroundsWins()
	if not self.data then
		return 0
	end
	return self.data.wsgWin + self.data.abWin + self.data.avWin
end

function lib:GetWarsongGulchWins()
	if not self.data then
		return 0
	end
	return self.data.wsgWin
end

function lib:GetArathiBasinWins()
	if not self.data then
		return 0
	end
	return self.data.abWin
end

function lib:GetAlteracValleyWins()
	if not self.data then
		return 0
	end
	return self.data.avWin
end

function lib:GetBattlegroundsLosses()
	if not self.data then
		return 0
	end
	return self.data.wsgLoss + self.data.abLoss + self.data.avLoss
end

function lib:GetWarsongGulchLosses()
	if not self.data then
		return 0
	end
	return self.data.wsgLoss
end

function lib:GetArathiBasinLosses()
	if not self.data then
		return 0
	end
	return self.data.abLoss
end

function lib:GetAlteracValleyLosses()
	if not self.data then
		return 0
	end
	return self.data.avLoss
end

function lib:ResetBGScores()
	self.data.wsgWin = 0
	self.data.wsgLoss = 0
	self.data.abWin = 0
	self.data.abLoss = 0
	self.data.avWin = 0
	self.data.avLoss = 0
	Trigger(self, "BG_RESET_SCORES")
end

function lib:IsPermanentPvP()
	return self.permaPvP
end

function lib:GetPvPCooldown()
	if self:IsInBattlegrounds() or self.permaPvP then
		return 300
	end
	local t = self.pvpTime - GetTime() + 300
	if t < 0 or not UnitIsPVP("player") then
		return 0
	else
		return t
	end
end

function lib:GetRankLimitInfo()
	local level = UnitLevel("player")
	if level < 10 then
		return NONE, 0
	elseif level <= 32 then
		return GetPVPRankInfo(7)
	elseif level <= 37 then
		return GetPVPRankInfo(8)
	elseif level <= 40 then
		return GetPVPRankInfo(9)
	elseif level <= 43 then
		return GetPVPRankInfo(10)
	elseif level <= 45 then
		return GetPVPRankInfo(11)
	elseif level <= 47 then
		return GetPVPRankInfo(12)
	elseif level <= 50 then
		return GetPVPRankInfo(13)
	elseif level <= 52 then
		return GetPVPRankInfo(14)
	elseif level <= 54 then
		return GetPVPRankInfo(15)
	elseif level <= 56 then
		return GetPVPRankInfo(16)
	elseif level <= 58 then
		return GetPVPRankInfo(17)
	elseif level <= 60 then
		return GetPVPRankInfo(18)
	end
end

function lib:GetRatingLimit()
	local level = UnitLevel("player")
	if level < 10 then
		return 0
	elseif level <= 29 then
		return 6500
	elseif level <= 35 then
		return 7150 + (level - 30) * 975
	elseif level <= 39 then
		return 13325 + (level - 36) * 1300
	elseif level <= 43 then
		return 18850 + (level - 40) * 1625
	elseif level <= 52 then
		return 26000 + (level - 44) * 2275
	elseif level <= 59 then
		return 46800 + (level - 53) * 2600
	else
		return 65000
	end
end

function lib:GetStanding(name)
	name = name or playerName
	argCheck(name, 2, "string")
	for i=1, GetNumBattlefieldScores() do
		if name == GetBattlefieldScore(i) then
			return i
		end
	end
end

function lib:GetKillingBlows(name)
	local unit, killingBlows
	name = name or playerName
	argCheck(name, 2, "string")
	for i=1, GetNumBattlefieldScores() do
		unit, killingBlows = GetBattlefieldScore(i)
		if unit == name then
			return killingBlows
		end
	end
end

function lib:GetHonorableKills(name)
	name = name or playerName
	argCheck(name, 2, "string")
	local unit, honorableKills
	for i=1, GetNumBattlefieldScores() do
		unit, _, honorableKills = GetBattlefieldScore(i)
		if unit == name then
			return honorableKills
		end
	end
end

function lib:GetDeaths(name)
	name = name or playerName
	argCheck(name, 2, "string")
	local unit, deaths
	for i=1, GetNumBattlefieldScores() do
		unit, _, _, deaths = GetBattlefieldScore(i)
		if unit == name then
			return deaths
		end
	end
end

function lib:GetBonusHonor(name)
	name = name or playerName
	argCheck(name, 2, "string")
	local unit, bonusHonor
	for i=1, GetNumBattlefieldScores() do
		unit, _, _, _, bonusHonor = GetBattlefieldScore(i)
		if unit == name then
			return bonusHonor
		end
	end
end

function lib:GetActiveBattlefieldZone()
	local status, mapName
	for i = 1, MAX_BATTLEFIELD_QUEUES do
		status, mapName = GetBattlefieldStatus(i)
		if status == "active" then
			return mapName
		end
	end
end

function lib:GetActiveBattlefieldUniqueID()
	local status, mapName, instanceID
	for i = 1, MAX_BATTLEFIELD_QUEUES do
		status, mapName, instanceID = GetBattlefieldStatus(i)
		if status == "active" then
			return mapName .. " " .. instanceID
		end
	end
end

local function queuedBattlefieldIndicesIter(_, position)
	position = position + 1
	while position <= MAX_BATTLEFIELD_QUEUES do
		local status, name = GetBattlefieldStatus(position)
		if status == "queued" then
			return position, name
		end
		position = position + 1
	end
	return nil
end
function lib:IterateQueuedBattlefieldZones()
	return queuedBattlefieldIndicesIter, nil, 0
end

local function GetHolder(self, node)
	local poi = self:NodeToPOI(node)
	if self:IsUncontrolled(node) then
		return BGObjectiveDescriptions.UNCONTROLLED
	end
	if poi and not self:IsDestroyed(poi) then
		_, description = GetMapLandmarkInfo(poi)
		if string.find(description, PATTERN_OBJECTIVE_HOLDER) then
			local _, _, faction = string.find(description, PATTERN_OBJECTIVE_HOLDER)
			return faction
		end
	end
end

function lib:IsBattlefieldObjective(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi and (GetHolder(self, node) or self:IsInConflict(node) or self:IsDestroyed(node)) then
		return true
	end
	return false
end

function lib:IsInConflict(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi then
		local _, description = GetMapLandmarkInfo(poi)
		if description == BGObjectiveDescriptions.IN_CONFLICT then
			return true
		end
	end
	return false
end

function lib:IsAllianceControlled(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi then
		local _, description = GetMapLandmarkInfo(poi)
		if description == BGObjectiveDescriptions.ALLIANCE_CONTROLLED then
			return true
		end
	end
	return false
end

function lib:IsHordeControlled(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi then
		local _, description = GetMapLandmarkInfo(poi)
		if description == BGObjectiveDescriptions.HORDE_CONTROLLED then
			return true
		end
	end
	return false
end

if isHorde then
	lib.IsFriendlyControlled = lib.IsHordeControlled
	lib.IsHostileControlled = lib.IsAllianceControlled
else
	lib.IsFriendlyControlled = lib.IsAllianceControlled
	lib.IsHostileControlled = lib.IsHordeControlled
end

function lib:IsUncontrolled(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi then
		local _, description = GetMapLandmarkInfo(poi)
		if description == BGObjectiveDescriptions.UNCONTROLLED then
			return true
		end
	end
	return
end

function lib:IsDestroyed(node)
	argCheck(node, 2, "string", "number")
	local poi = self:NodeToPOI(node)
	if poi then
		local _, description = GetMapLandmarkInfo(poi)
		if description == BGObjectiveDescriptions.DESTROYED then
			return true
		end
	end
	return
end

function lib:GetTimeAttacked(node)
	argCheck(node, 2, "string", "number")
	return self.battlefieldObjectiveStatus[node].timeAttacked
end

function lib:GetTimeToCapture(node)
	argCheck(node, 2, "string", "number")
	local t = BattlefieldZoneObjectiveTimes[self:GetActiveBattlefieldZone()] or 0
	return t - GetTime() + self.battlefieldObjectiveStatus[node].timeAttacked
end

function lib:GetName(node)
	argCheck(node, 2, "string", "number")
	return self.battlefieldObjectiveStatus[node].name
end

function lib:GetDefender(node)
	argCheck(node, 2, "string", "number")
	return self.battlefieldObjectiveStatus[node].defender 
end

function lib:GetAttacker(node)
	argCheck(node, 2, "string", "number")
	return self.battlefieldObjectiveStatus[node].attacker
end

local function objectiveNodesIter(_, position)
	local k = next(self.battlefieldObjectiveStatus, position)
	while k ~= nil and type(k) ~= "number" do
		k = next(self.battlefieldObjectiveStatus, position)
	end
	return k
end

function lib:IterateObjectiveNodes()
	return objectiveNodesIter, nil, nil
end

local function sortedObjectiveNodesIter(t, position)
	position = position + 1
	if position <= table.getn(t) then
		return position, t[position]
	else
		if compost then
			compost:Reclaim(t)
		end
		return nil
	end
end
function lib:IterateSortedObjectiveNodes()
	local t = compost and compost:Acquire() or {}
	for poi in pairs(self.battlefieldObjectiveStatus) do
		if type(poi) == "number" then
			table.insert(t, poi)
		end
	end
	table.sort(t, function(a, b)
		return self.battlefieldObjectiveStatus[a].ypos and self.battlefieldObjectiveStatus[b].ypos and self.battlefieldObjectiveStatus[a].ypos < self.battlefieldObjectiveStatus[b].ypos
	end)
	if not next(t) then
		if compost then
			compost:Reclaim(t)
		end
	else
		return sortedObjectiveNodesIter, t, 0
	end
end

function events:ClearBattlefieldObjectives()
	for k in pairs(self.battlefieldObjectiveStatus) do
		if compost then
			compost:Reclaim(self.battlefieldObjectiveStatus[k])
		end
		self.battlefieldObjectiveStatus[k] = nil
	end
end

function events:InitializeBattlefieldObjectives()
	local n, node, y
	events.ClearBattlefieldObjectives(self)
	SetMapToCurrentZone()
	local numPOIS = GetNumMapLandmarks()
	for i=1, numPOIS do
		if self:IsBattlefieldObjective(i) then
			node, _, _, _, y = GetMapLandmarkInfo(i)
			self.battlefieldObjectiveStatus[i] = false and compost and compost:AcquireHash(
				'name', node,
				'ypos', y,
				'defender', GetHolder(self, i),
				'inConflict', self:IsInConflict(i),
				'isDestroyed', self:IsDestroyed(i)
			) or {
				name = node,
				ypos = y,
				defender = GetHolder(self, i),
				inConflict = self:IsInConflict(i),
				isDestroyed = self:IsDestroyed(i),
			}
			self.battlefieldObjectiveStatus[node] = self.battlefieldObjectiveStatus[i]
		end
	end
end

function events:OnObjectiveClaimed(node, faction)
	local poi = self:NodeToPOI(node)
	if poi then
		if not next(self.battlefieldObjectiveStatus) then
			events.InitializeBattlefieldObjectives(self)
		end
		local n = self.battlefieldObjectiveStatus[poi]
		if n then
			n.attacker = faction
			n.inConflict = true
			n.timeAttacked = GetTime()
		end
	end	
end

function events:OnObjectiveCaptured(node, faction)
	local poi = self:NodeToPOI(node)
	if poi then
		if not next(self.battlefieldObjectiveStatus) then
			events.InitializeBattlefieldObjectives(self)
		end
		local n = self.battlefieldObjectiveStatus[poi]
		if n then
			n.defender = GetHolder(self, node) or faction
			n.attacker = nil
			n.inConflict = nil
			n.timeAttacked = nil
		end
	end
end

function events:OnObjectiveAttacked(node)
	local poi = self:NodeToPOI(node)
	if poi then
		if not next(self.battlefieldObjectiveStatus) then
			events.InitializeBattlefieldObjectives(self)
		end
		local n = self.battlefieldObjectiveStatus[poi]
		if n then
			if n.defender == FACTION_ALLIANCE then
				n.attacker = FACTION_HORDE
			else
				n.attacker = FACTION_ALLIANCE
			end
			n.inConflict = true
			n.timeAttacked = GetTime()
		end
	end
end

function events:OnObjectiveDefended(node)
	local poi = self:NodeToPOI(node)
	if poi then
		if not next(self.battlefieldObjectiveStatus) then
			events.InitializeBattlefieldObjectives(self)
		end
		local n = self.battlefieldObjectiveStatus[poi]
		if n then
			n.attacker = nil
			n.inConflict = nil
			n.timeAttacked = nil
		end
	end
end

function events:OnObjectiveDestroyed(node)
	local poi = self:NodeToPOI(node)
	if poi then
		if not next(self.battlefieldObjectiveStatus) then
			events.InitializeBattlefieldObjectives(self)
		end
		local n = self.battlefieldObjectiveStatus[poi]
		if n then
			n.isDestroyed = true
			n.defender = nil
			n.attacker = nil
			n.inConflict = nil
			n.timeAttacked = nil
		end
	end
end

function lib:GetAllianceFlagCarrier()
	return self.allianceFC
end

function lib:GetHordeFlagCarrier()
	return self.hordeFC
end

function lib:TargetAllianceFlagCarrier()
	if self.allianceFC then
		TargetByName(self.allianceFC)
	end
end

function lib:TargetHordeFlagCarrier()
	if self.hordeFC then
		TargetByName(self.hordeFC)
	end
end

if isHorde then
	lib.GetFriendlyFlagCarrier = lib.GetHordeFlagCarrier
	lib.GetHostileFlagCarrier = lib.GetAllianceFlagCarrier
	lib.TargetFriendlyFlagCarrier = lib.TargetHordeFlagCarrier
	lib.TargetHostileFlagCarrier = lib.TargetAllianceFlagCarrier
else
	lib.GetFriendlyFlagCarrier = lib.GetAllianceFlagCarrier
	lib.GetHostileFlagCarrier = lib.GetHordeFlagCarrier
	lib.TargetFriendlyFlagCarrier = lib.TargetAllianceFlagCarrier
	lib.TargetHostileFlagCarrier = lib.TargetHordeFlagCarrier
end

function lib:GetFlagCarrier(faction)
	argCheck(faction, 2, "string", "number")
	if faction == FACTION_ALLIANCE or faction == "Alliance" or faction == 1 then
		return self.allianceFC
	else
		return self.hordeFC
	end
end

function lib:TargetFlagCarrier(faction)
	argCheck(faction, 2, "string", "number")
	if faction == FACTION_ALLIANCE or faction == "Alliance" or faction == 1 then
		if self.allianceFC then
			TargetByName(self.allianceFC)
		end
	elseif self.hordeFC then
		TargetByName(self.hordeFC)
	end
end

function lib:GetNumAllianceBases()
	if GetWorldStateUIInfo(1) then
		local _, _, bases = string.find(GetWorldStateUIInfo(1), PATTERN_GWSUII_BASES)
		return tonumber(bases)
	end
end

function lib:GetNumHordeBases()
	if GetWorldStateUIInfo(2) then
		local _, _, bases = string.find(GetWorldStateUIInfo(2), PATTERN_GWSUII_BASES)
		return tonumber(bases)
	end
end

if isHorde then
	lib.GetNumFriendlyBases = lib.GetNumHordeBases
	lib.GetNumHostileBases = lib.GetNumAllianceBases
else
	lib.GetNumFriendlyBases = lib.GetNumAllianceBases
	lib.GetNumHostileBases = lib.GetNumHordeBases
end

function lib:GetNumBases(team)
	argCheck(team, 2, "string", "number")
	if team == FACTION_ALLIANCE or team == "Alliance" or team == 1 then
		team = 1
	else
		team = 2
	end
	if GetWorldStateUIInfo(team) then
		local _, _, bases = string.find(GetWorldStateUIInfo(team), PATTERN_GWSUII_BASES)
		return tonumber(bases)
	end
end

function lib:GetNumAllianceResources()
	if GetWorldStateUIInfo(1) then
		local _, _, resources = string.find(GetWorldStateUIInfo(1), PATTERN_GWSUII_RESOURCES)
		return tonumber(resources)
	end
end

function lib:GetNumHordeResources()
	if GetWorldStateUIInfo(2) then
		local _, _, resources = string.find(GetWorldStateUIInfo(2), PATTERN_GWSUII_RESOURCES)
		return tonumber(resources)
	end
end

if isHorde then
	lib.GetNumFriendlyResources = lib.GetNumHordeResources
	lib.GetNumHostileResources = lib.GetNumAllianceResources
else
	lib.GetNumFriendlyResources = lib.GetNumAllianceResources
	lib.GetNumHostileResources = lib.GetNumHordeResources
end

function lib:GetNumTeamResources(team)
	argCheck(team, 2, "string", "number")
	if team == FACTION_ALLIANCE or team == "Alliance" or team == 1 then
		return self:GetNumAllianceResources()
	else
		return self:GetNumHordeResources()
	end
end

function lib:GetAllianceTTV()
	return self.aResourceTTV - GetTime() + self.aLastUpdate
end

function lib:GetHordeTTV()
	return self.hResourceTTV - GetTime() + self.hLastUpdate
end

function lib:GetTeamTTV(team)
	argCheck(team, 2, "string", "number")
	if team == FACTION_ALLIANCE or team == "Alliance" or team == 1 then
		return self:GetAllianceTTV()
	else
		return self:GetHordeTTV()
	end
end

if isHorde then
	lib.GetFriendlyTTV = lib.GetHordeTTV
	lib.GetHostileTTV = lib.GetAllianceTTV
else
	lib.GetFriendlyTTV = lib.GetAllianceTTV
	lib.GetHostileTTV = lib.GetHordeTTV
end
	
function lib:GetAllianceScoreString()
	if GetWorldStateUIInfo(1) then
		local _, _, scoreString = string.find(GetWorldStateUIInfo(1), PATTERN_GWSUII_SCORE)
		return scoreString
	end
end

function lib:GetHordeScoreString()
	if GetWorldStateUIInfo(2) then
		local _, _, scoreString = string.find(GetWorldStateUIInfo(2), PATTERN_GWSUII_SCORE)
		return scoreString
	end
end

if isHorde then
	lib.GetFriendlyScoreString = lib.GetHordeScoreString
	lib.GetHostileScoreString = lib.GetAllianceScoreString
else
	lib.GetFriendlyScoreString = lib.GetAllianceScoreString
	lib.GetHostileScoreString = lib.GetHordeScoreString
end

function lib:GetTeamScoreString(team)
	if team == FACTION_ALLIANCE or team == "Alliance" or team == 1 then
		return self:GetAllianceScoreString()
	else
		return self:GetHordeScoreString()
	end
end

function lib:GetNumAlliancePlayers()
	local numPlayers = 0
	for i = 1, GetNumBattlefieldScores() do
		local _, _, _, _, _, faction = GetBattlefieldScore(i)
		if faction == 1 then
			numPlayers = numPlayers + 1
		end
	end
	return numPlayers
end

function lib:GetNumHordePlayers()
	local numPlayers = 0
	for i = 1, GetNumBattlefieldScores() do
		local _, _, _, _, _, faction = GetBattlefieldScore(i)
		if faction == 0 then
			numPlayers = numPlayers + 1
		end
	end
	return numPlayers
end

if isHorde then
	lib.GetNumFriendlyPlayers = lib.GetNumHordePlayers
	lib.GetNumHostilePlayers = lib.GetNumAlliancePlayers
else
	lib.GetNumFriendlyPlayers = lib.GetNumAlliancePlayers
	lib.GetNumHostilePlayers = lib.GetNumHordePlayers
end

function lib:GetNumPlayers(team)
	argCheck(team, 2, "string", "number")
	if team == FACTION_ALLIANCE or team == "Alliance" or team == 1 then
		return self:GetNumAlliancePlayers()
	else
		return self:GetNumHordePlayers()
	end
end

function lib:NodeToPOI(node)
	local landmark, description
	if type(node) == "number" and node > 0 then
		return node
	elseif type(node) == "string" then
		for i = 1, GetNumMapLandmarks() do
			if string.lower(node) == string.lower(GetMapLandmarkInfo(i)) then
				return i
			end
		end
	elseif type(node) ~= "number" then
		assert(false, format("Bad argument #2 to `NodeToPOI' (string or number expected, got %s)", type(node)))
	else
		assert(false, format("Bad argument #2 to `NodeToPOI' (out of bounds: [1, %d] expected, got %d)", GetNumMapLandmarks(), node))
	end
end

function lib:GetBGAcronym(bgName)
	argCheck(bgName, 2, "string")
	return BGAcronyms[bgName] or bgName
end

function lib:GetFactionColor(faction)
	argCheck(faction, 2, "string", "number", "nil")
	if faction then
		if faction == "Alliance" or faction == FACTION_ALLIANCE or faction == 1 then
			faction = "ALLIANCE"
		elseif faction == "Horde" or faction == FACTION_HORDE or faction == 0 or faction == 2 then
			faction = "HORDE"
		end
		local cti = ChatTypeInfo["BG_SYSTEM_" .. faction]
		if cti then
			return cti.r, cti.g, cti.b
		end
	end
	return 0.7, 0.7, 0.7
end

function lib:GetFactionHexColor(faction)
	local r, g, b = self:GetFactionColor(faction)
	return string.format("%02X%02X%02X", 255*r, 255*g, 255*b)
end

function lib:Register(event, func, arg)
	argCheck(event, 2, "string")
	argCheck(func, 3, "function", "table")
	if type(func) == "table" and arg then
		argCheck(arg, 4, "string")
	end
	if not self.registry[event] then
		self.registry[event] = compost and compost:Acquire() or {}
	end
	if arg == nil then
		arg = event
	end
	self.registry[event][func] = arg
end

function lib:Unregister(event, func)
	argCheck(event, 2, "string")
	argCheck(func, 3, "function", "table")
	if self.registry[event][func] then
		self.registry[event][func] = nil
	end
	if not next(self.registry[event]) then
		if compost then
			compost:Reclaim(self.registry[event])
		end
		self.registry[event] = nil
	end
end

function lib:UnregisterAll(object)
	argCheck(object, 2, "table")
	for event in pairs(self.registry) do
		self.registry[event][object] = nil
		for func, u in pairs(self.registry[event]) do
			if u == object then
				self.registry[event][func] = nil
			end
		end
	end
end

GloryLib:Register(lib)
lib = nil
