--[[
	Name: ParserLib
	Revision: $Revision: 15186 $
	Author(s): rophy (rophy123@gmail.com)
	Website: http://www.wowace.com/index.php/ParserLib
	Documentation: http://www.wowace.com/index.php/ParserLib
	SVN: http://svn.wowace.com/root/trunk/ParserLib
	Description: An embedded combat log parser, which works on all localizations.
	Dependencies: CompostLib (optional) or Compost-2.0 (optional).
]]

---------------------------------------------------------------------------
--	To Get an instance of ParserLib, call this:
-- 	local parser = ParserLib:GetInstance(version)
-- 	where the version is the variable 'vmajor' you see here.
---------------------------------------------------------------------------
local vmajor, vminor = "1.1", tonumber(string.sub("$Revision: 15186 $", 12, -3))

local stubvarname = "TekLibStub"
local libvarname = "ParserLib"

-- Check to see if an update is needed
-- if not then just return out now before we do anything
local libobj = getglobal(libvarname)
if libobj and not libobj:NeedsUpgraded(vmajor, vminor) then return end

local lua51 = loadstring("return function(...) return ... end") and true or false
local string_gmatch = lua51 and string.gmatch or string.gfind

local function print(msg, r, g, b)
	ChatFrame1:AddMessage(string.format("<%s-%s-%s> %s", libvarname, vmajor, vminor, msg), r, g, b)
end

---------------------------------------------------------------------------
-- Embedded Library Registration Stub
-- Written by Iriel <iriel@vigilance-committee.org>
-- Version 0.1 - 2006-03-05
-- Modified by Tekkub <tekkub@gmail.com>
---------------------------------------------------------------------------

local stubobj = getglobal(stubvarname)
if not stubobj then
	stubobj = {}
	setglobal(stubvarname, stubobj)
	
	-- Instance replacement method, replace contents of old with that of new
	function stubobj: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 stubobj:NewStub(name)
		local newStub = {}
		self:ReplaceInstance(newStub, self)
		newStub.libName = name
		newStub.lastVersion = ''
		newStub.versions = {}
		return newStub
	end
	
	
	-- Get instance version
	function stubobj:NeedsUpgraded(vmajor, vminor)
		local versionData = self.versions[vmajor]
		if not versionData or versionData.minor < vminor then return true end
	end
	
	
	-- Get instance version
	function stubobj:GetInstance(version)
		if not version then version = self.lastVersion end
		local versionData = self.versions[version]
		if not versionData then print(string.format("<%s> Cannot find library version: %s", self.libName, version or "")) return end
		return versionData.instance
	end
	
	
	-- Register new instance
	function stubobj: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
		-- 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
end


if not libobj then
	libobj = stubobj:NewStub(libvarname)
	setglobal(libvarname, libobj)
end


local lib = {}


-- Return the library's current version
function lib:GetLibraryVersion()
	return vmajor, vminor
end


-- Activate a new instance of this library
function lib:LibActivate(stub, oldLib, oldList)
	local maj, min = self:GetLibraryVersion()
	
	if oldLib then
		local omaj, omin = oldLib:GetLibraryVersion()
		----------------------------------------------------
		-- ********************************************** --
		-- **** Copy over any old data you need here **** --
		-- ********************************************** --
		----------------------------------------------------
		self.frame = oldLib.frame
		self:OnLoad()
		
		if omin < 11 and oldLib.clients then
			for event in pairs(oldLib.clients) do
				for i in pairs(oldLib.clients[event]) do
					if type(oldLib.clients[event][i]["func"]) == "string" then
						oldLib.clients[event][i]["func"] = getglobal(oldLib.clients[event][i]["func"])
					end
				end
			end
		end
		self.clients = oldLib.clients
		
		
	else
		---------------------------------------------------
		-- ********************************************* --
		-- **** Do any initialization you need here **** --
		-- ********************************************* --
		---------------------------------------------------
		self:OnLoad()
	end
	-- nil return makes stub do object copy
end

----------------------------------------------
--	*ParserLib Public Methods*      --
----------------------------------------------

-- Register an event to ParserLib.
function lib:RegisterEvent(addonID, event, handler)
	
	local eventExist
	for i, v in pairs(self.supportedEvents) do
		if v == event then
			eventExist = true
			break
		end
	end
	
	if not eventExist then
		self:Print( string.format("Event %s is not supported. (AddOnID %s)", event, addonID), 1, 0, 0 )
		return
	end
	
	
	-- self:Print(string.format("Registering %s for addon %s.", event, addonID) );	 -- debug
	
	if type(handler) == "string" then handler = getglobal(handler) end
	
	-- if not handler then self:Print("nil handler from " .. addonID, 1, 0, 0) end -- debug
	
	if self.clients[event] == nil then
		self.clients[event] = {};	
	end
	
	-- load pattern list & info when event is registering
	self:PreLoadPatternInfo(event);
	
	table.insert(self.clients[event], { ["id"]=addonID, ["func"]=handler } );
	self.frame:RegisterEvent(event);
	
end

-- Check if you have registered an event.
function lib:IsEventRegistered(addonID, event)
	if self.clients[event] then
		for i, v in pairs(self.clients[event]) do
			if v.id == addonID then return true end
		end
	end
end

-- Unregister an event.
function lib:UnregisterEvent(addonID, event)
	local empty = true
	
	
	if not self.clients[event] then return end
	
	for i, v in pairs(self.clients[event]) do
		if v.id == addonID then
			-- self:Print( format("Removing %s from %s", v.id, event) )		 -- debug
			table.remove(self.clients[event], i)
		else
			empty = false
		end
	end
	
	if empty then
		-- self:Print("Unregistering event " .. event) -- debug
		self.frame:UnregisterEvent(event)
		self.clients[event] = nil
	end
end

-- Unregister all events.
function lib:UnregisterAllEvents(addonID)
	local event, index, empty;
	
	for event in pairs(self.clients) do
		empty = true;
		
		for i, v in pairs(self.clients[event]) do
			if v.id == addonID then
				-- self:Print( format("Removing %s for %s", v.id, event) ) -- debug
				table.remove(self.clients[event], i)
				--				self.clients[event][index] = nil;
			else
				empty = false;
			end
		end
		
		if empty then
			-- self:Print("Unregistering event " .. event) -- debug
			self.frame:UnregisterEvent(event);
			self.clients[event] = nil;
		end
		
	end	
	
end

-- Parse custom messages, check documentation.html for more info.
function lib:Deformat(text, pattern)
	if not self.customPatterns then self.customPatterns = {} end		
	if not self.customPatterns[pattern] then
		self.customPatterns[pattern] = self:Curry(pattern)
	end
	return self.customPatterns[pattern](text)
end

--------------------------------------------------------
-- Methods to control ParserLib behaviour --
--------------------------------------------------------

-- Use CompostLib or not?
-- Not that at 1.1-23 CompostLib no longer slows down the speed of ParserLib, 
--   because I found out that I was calling CompostLib:Recycle too frequently, which can be avoided.
function lib:UseCompost(flag)
	self.vars.noCompost = not flag
end


---------------------------------------------------
--	*End of ParserLib Public Methods* --
---------------------------------------------------


----------------------------------------------
--	ParserLib Private Methods
----------------------------------------------

-- Constants
ParserLib_SELF = 103
ParserLib_MELEE = 112
ParserLib_DAMAGESHIELD = 113

-- lib.timing = true -- timer

-- Stub function called by frame.OnEvent
local function ParserOnEvent() lib:OnEvent() end

-- Sort the pattern so that they can be parsed in a correct sequence, will only do once for each registered event.
local function PatternCompare(a, b)
	
	local pa = getglobal(a)
	local pb = getglobal(b)
	
	if not pa then ChatFrame1:AddMessage("|cffff0000Nil pattern: ".. a.."|r") end
	if not pb then ChatFrame1:AddMessage("|cffff0000Nil pattern: ".. b.."|r") end
	
	local ca=0
	for _ in string_gmatch(pa, "%%%d?%$?[sd]") do ca=ca+1 end
	local cb=0
	for _ in string_gmatch(pb, "%%%d?%$?[sd]") do cb=cb+1 end
	
	pa = string.gsub(pa, "%%%d?%$?[sd]", "")	
	pb = string.gsub(pb, "%%%d?%$?[sd]", "")
	
	if string.len(pa) == string.len(pb) then
		return  ca < cb;
	else
		return string.len(pa) > string.len(pb)
	end
	
end

local FindString = {
	[0] = function(m,p,t) return string.find(m,p), t end,
	[1] = function(m,p,t) _,_,t[1] = string.find(m,p) if t[1] then return true, t else return false, t end end,
	[2] = function(m,p,t) _,_,t[1],t[2] = string.find(m,p) if t[2] then return true, t else return false, t end end,
	[3] = function(m,p,t) _,_,t[1],t[2],t[3] = string.find(m,p) if t[3] then return true, t else return false, t end end,
	[4] = function(m,p,t) _,_,t[1],t[2],t[3],t[4] = string.find(m,p) if t[4] then return true, t else return false, t end end,
	[5] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5] = string.find(m,p) if t[5] then return true, t else return false, t end end,
	[6] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6] = string.find(m,p) if t[6] then return true, t else return false, t end end,
	[7] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7] = string.find(m,p) if t[7] then return true, t else return false, t end end,
	[8] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8] = string.find(m,p) if t[8] then return true, t else return false, t end end,
	[9] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9] = string.find(m,p) if t[9] then return true, t else return false, t end end,
}

-- Currently supported event list.
lib.supportedEvents = {
	
	"CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS",
	"CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES",
	"CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS",
	"CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES",
	"CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS",
	"CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES",
	"CHAT_MSG_COMBAT_FACTION_CHANGE",
	"CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS",
	"CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES",
	"CHAT_MSG_COMBAT_FRIENDLY_DEATH",
	"CHAT_MSG_COMBAT_HONOR_GAIN",
	"CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS",
	"CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES",
	"CHAT_MSG_COMBAT_HOSTILE_DEATH",
	"CHAT_MSG_COMBAT_PARTY_HITS",
	"CHAT_MSG_COMBAT_PARTY_MISSES",
	"CHAT_MSG_COMBAT_PET_HITS",
	"CHAT_MSG_COMBAT_PET_MISSES",
	"CHAT_MSG_COMBAT_SELF_HITS",
	"CHAT_MSG_COMBAT_SELF_MISSES",
	"CHAT_MSG_COMBAT_XP_GAIN",
	"CHAT_MSG_SPELL_AURA_GONE_OTHER",
	"CHAT_MSG_SPELL_AURA_GONE_SELF",
	"CHAT_MSG_SPELL_AURA_GONE_PARTY",
	"CHAT_MSG_SPELL_BREAK_AURA",
	"CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF",
	"CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE",
	"CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF",
	"CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE",
	"CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF",
	"CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE",
	"CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS",
	"CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF",
	"CHAT_MSG_SPELL_FAILED_LOCALPLAYER",
	"CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF",
	"CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE",
	"CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF",
	"CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE",
	"CHAT_MSG_SPELL_ITEM_ENCHANTMENTS",
	"CHAT_MSG_SPELL_PARTY_BUFF",
	"CHAT_MSG_SPELL_PARTY_DAMAGE",
	"CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS",
	"CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE",
	"CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS",
	"CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE",
	"CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS",
	"CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE",
	"CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS",
	"CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE",
	"CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS",
	"CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE",
	"CHAT_MSG_SPELL_PET_BUFF",
	"CHAT_MSG_SPELL_PET_DAMAGE",
	"CHAT_MSG_SPELL_SELF_BUFF",
	"CHAT_MSG_SPELL_SELF_DAMAGE",
	"CHAT_MSG_SPELL_TRADESKILLS",
	
}

function lib:GetCompost()
	
	if self.vars.noCompost then
		
		if not self.myCompost then
			self.myCompost = {
				Recycle = function() return {} end,
				Acquire = function() return {} end,
				Reclaim = function() end,
			}					
		end
		return self.myCompost
		
	else
		
		if not self.compost then
			if AceLibrary and AceLibrary:HasInstance("Compost-2.0") then
				self.compost = AceLibrary:GetInstance("Compost-2.0")
			elseif CompostLib then
				self.compost = CompostLib:GetInstance("compost-1")
			else
				-- Cannot find the existance of CompostLib
				self.vars.noCompost = true
				return self:GetCompost()
			end			
		end
		return self.compost
		
	end
	
end

-- Convert "%s hits %s for %d." to "(.+) hits (.+) for (%d+)."
-- Will additionaly return the sequence of tokens, for example:	                    
--  "%2$s reflects %3$d %4$s damage to %1$s."   will return: 
--    "(.-) reflects (%+) (.-) damage to (.-)%.",  4 1 2 3. 
--  (    [1]=2,[2]=3,[3]=4,[4]=1  Reverting indexes and become  [1]=4, [2]=[1],[3]=2,[4]=3. )
function lib:ConvertPattern(pattern, anchor)
	
	local seq
	
	-- Add % to escape all magic characters used in LUA pattern matching, except $ and %
	pattern = string.gsub(pattern,"([%^%(%)%.%[%]%*%+%-%?])","%%%1");
	
	-- Do these AFTER escaping the magic characters.
	pattern = string.gsub(pattern,"%%s","(.-)"); -- %s to (.-)
	pattern = string.gsub(pattern,"%%d","(%-?%%d+)"); -- %d to (%d+)			
	
	if string.find(pattern,"%$") then
		seq = {}; -- fills with ordered list of $s as they appear
		local idx = 1; -- incremental index into field[]
		
		local tmpSeq = {}
		for i in string_gmatch(pattern,"%%(%d)%$.") do
			tmpSeq[idx] = tonumber(i);
			idx = idx + 1
		end
		for i, j in ipairs(tmpSeq) do
			seq[j] = i
		end
		table.setn(seq, table.getn(tmpSeq))
		pattern = string.gsub(pattern,"%%%d%$s","(.-)"); -- %1$s to (.-)
		pattern = string.gsub(pattern,"%%%d%$d","(%-?%%d+)"); -- %1$d to (%d+)
	end
	
	-- Escape $ now.
	pattern = string.gsub(pattern,"%$","%%$");
	
	-- Anchor tag can improve string.find() performance by 100%.
	if anchor then pattern = "^"..pattern end	
	
	-- If the pattern ends with (.-), replace it with (.+), or the capsule will be lost.
	if string.sub(pattern, -4) == "(.-)" then
		pattern = string.sub(pattern, 0, -5) .. "(.+)";
	end
	
	if not seq then return pattern end
	
	return pattern, seq[1], seq[2], seq[3], seq[4], seq[5], seq[6], seq[7], seq[8], seq[9], seq[10]
end

function lib:OnLoad()
	
	-- Both table starts out empty, and load the data only when required.
	self.eventTable = {}
	self.patternTable = {}
	
	
	self.vars = {}
	
	if self.timing then
		self.timer = {
			ParseMessage_LoadPatternList = 0,
			ParseMessage_FindPattern = 0,
			ParseMessage_FindPattern_Regexp = 0,
			ParseMessage_FindPattern_Regexp_FindString = 0,
			ParseMessage_FindPattern_LoadPatternInfo = 0,			
			ParseMessage_ParseInformation = 0,
			ParseMessage_ParseTrailers = 0,
			ParseMessage_ConvertTypes = 0,
			NotifyClients = 0,
			Compost_Acquire = 0,
			Compost_Recycle = 0,
			Compost_Reclaim = 0,
		}
	end
	
	if not self.clients then self.clients = {} end
	
	if not self.frame then
		self.frame = CreateFrame("FRAME", "ParserLibFrame")
		self.frame:SetScript("OnEvent", ParserOnEvent )
		self.frame:Hide()
	end
	
	
end

function lib:OnEvent(e, a1)
	
	if not e then e = event end
	if not a1 then a1 = arg1 end
	
	-- self:Print("Event: |cff3333ff"..e.."|r") -- debug
	
	-- Titan Honor+ was changing the global events... just change it back.
	if e == "CHAT_MSG_HONORPLUS" then e = "CHAT_MSG_COMBAT_HONOR_GAIN" end
	
	
	if self:ParseMessage(a1, e) then
		
		-- 		local timer = GetTime() -- timer
		self:NotifyClients(e)
		-- 		self.timer.NotifyClients = self.timer.NotifyClients + GetTime() - timer -- timer
		
	end
	
	
end

function lib:NotifyClients(event)
	
	if not self.clients or not self.clients[event] then 
		-- self:Print(event .. " has no client to notify.") -- debug
		return 
	end
	
	-- Noneed to recycle the table if there is only one client.
	if table.getn(self.clients[event]) == 1 then
		-- self:Print(event .. ", calling " .. self.clients[event][1].id) -- debug
		self.clients[event][1].func(event, self.info)
		return
	end
	
	-- 	local timer = GetTime() -- timer
	local info = self:GetCompost():Acquire()	
	-- 	self.timer.Compost_Acquire = GetTime() - timer + self.timer.Compost_Acquire -- timer
	
	for i, client in pairs(self.clients[event]) do
		-- self:Print(event .. ", calling " .. client.id) -- debug
		
		-- I can just do a compost:Recycle() here, but I hope this can improve the performance.
		for j in pairs(info) do if not self.info[j] then info[j] = nil end end
		for j, v in pairs(self.info) do info[j] = v end
		
		client.func(event, info)
	end
	
	-- 	timer = GetTime() -- timer
	self:GetCompost():Reclaim(info)
	-- 	self.timer.reclaim = GetTime() - timer + self.timer.reclaim -- timer
	
end

lib.Print = print

-- load pattern list & info
function lib:PreLoadPatternInfo(event)
	if not event then return end
	
	if not self.eventTable[event] then self.eventTable[event] = self:LoadPatternList(event) end
	local list = self.eventTable[event]
	if list then
		for i, v in pairs(list) do
			if not self.patternTable[v] then self.patternTable[v] = self:LoadPatternInfo(v) end
		end
	end
end

-- message : the arg1 in the event
-- event : name says it all.
-- info : the table which will store the passed result, so THIS IS THE OUTPUT.
-- return : true if pattern found and parsed, nil otherwise.
function lib:ParseMessage(message, event)
	
	-- --	local currTime -- timer
	
	
	-- 	currTime = GetTime() -- timer
	if not self.eventTable[event] then self.eventTable[event] = self:LoadPatternList(event) end -- loaded by registering already
	local list = self.eventTable[event]
	-- 	self.timer.ParseMessage_LoadPatternList = self.timer.ParseMessage_LoadPatternList + GetTime() - currTime -- timer
	
	if not list then return end
	
	-- Get the table to store parsed results.
	if not self.info then 
		-- 		timer = GetTime() -- timer
		self.info = self:GetCompost():Acquire() 
		-- 		self.timer.Compost_Acquire = GetTime() - timer + self.timer.Compost_Acquire -- timer
	else 
		-- 		timer = GetTime() -- timer
		self.info = self:GetCompost():Recycle(self.info) 
		-- 		self.timer.Compost_Recycle = GetTime() - timer + self.timer.Compost_Recycle -- timer
	end
	
	
	-- 	currTime = GetTime() -- timer
	local pattern = self:FindPattern(message, list)
	-- 	self.timer.ParseMessage_FindPattern = GetTime() - currTime + self.timer.ParseMessage_FindPattern -- timer
	
	
	if not pattern then 
		-- create "unknown" event type.
		self.info.type = "unknown"
		self.info.message = message		
		return true
	end
	
	-- 	currTime = GetTime() -- timer
	self:ParseInformation(pattern)
	-- 	self.timer.ParseMessage_ParseInformation = GetTime() - currTime + self.timer.ParseMessage_ParseInformation -- timer
	
	
	-- 	currTime = GetTime() -- timer
	if self.info.type == "hit" or self.info.type == "environment" then
		self:ParseTrailers(message)
	end
	-- 	self.timer.ParseMessage_ParseTrailers = GetTime() - currTime + self.timer.ParseMessage_ParseTrailers -- timer
	
	-- 	currTime = GetTime() -- timer
	self:ConvertTypes(self.info)
	-- 	self.timer.ParseMessage_ConvertTypes = GetTime() - currTime + self.timer.ParseMessage_ConvertTypes -- timer
	
	return true
	
	
end


-- Search for pattern in 'patternList' which matches 'message', parsed tokens will be stored in table self.info
function lib:FindPattern(message, patternList)
	
	local pt, timer, found
	
	for i, v in pairs(patternList) do
		
		-- 		timer = GetTime() -- timer
		if not self.patternTable[v] then self.patternTable[v] = self:LoadPatternInfo(v) end -- loaded by registering already
		-- 		self.timer.ParseMessage_FindPattern_LoadPatternInfo = GetTime() - timer + self.timer.ParseMessage_FindPattern_LoadPatternInfo -- timer
		
		pt = self.patternTable[v]
		
		found = false
		
		if not pt then return end -- check patternTable
		
		-- 		timer = GetTime() -- timer
		if self:OptimizerCheck(message, v) then
			-- 			timer = GetTime() -- timer
			found, self.info = FindString[pt.tc](message, pt.pattern, self.info)
			-- 			self.timer.ParseMessage_FindPattern_Regexp_FindString = GetTime() - timer + self.timer.ParseMessage_FindPattern_Regexp_FindString -- timer
		end
		-- 		self.timer.ParseMessage_FindPattern_Regexp = GetTime() - timer + self.timer.ParseMessage_FindPattern_Regexp -- timer
		
		if found then 
			-- self:Print(message.." = " .. v .. ":" .. pt.pattern)  -- debug
			return v	
		end
		
		
	end
	
	
end


-- Parses for trailors.
function lib:ParseTrailers(message)
	local found, amount, info
	
	info = self.info
	
	
	if not self.trailers then 
		self.trailers = {
			CRUSHING_TRAILER = self:ConvertPattern(CRUSHING_TRAILER),
			GLANCING_TRAILER = self:ConvertPattern(GLANCING_TRAILER),
			ABSORB_TRAILER = self:ConvertPattern(ABSORB_TRAILER),
			BLOCK_TRAILER = self:ConvertPattern(BLOCK_TRAILER),
			RESIST_TRAILER = self:ConvertPattern(RESIST_TRAILER),
			VULNERABLE_TRAILER = self:ConvertPattern(VULNERABLE_TRAILER),
		}
	end
	
	found = string.find(message, self.trailers.CRUSHING_TRAILER)
	if found then 
		info.isCrushing = true
	end
	found = string.find(message, self.trailers.GLANCING_TRAILER)
	if found then 
		info.isGlancing = true
	end
	found, _, amount = string.find(message, self.trailers.ABSORB_TRAILER)
	if found then 
		info.amountAbsorb = amount
	end
	found, _, amount = string.find(message, self.trailers.BLOCK_TRAILER)
	if found then 
		info.amountBlock = amount
	end
	found, _, amount = string.find(message, self.trailers.RESIST_TRAILER)
	if found then 
		info.amountResist = amount
	end
	found, _, amount = string.find(message, self.trailers.VULNERABLE_TRAILER)
	if found then 
		info.amountVulnerable = amount
	end
	
end

function lib:ParseInformation(patternName)
	
	local patternInfo = self.patternTable[patternName]
	local info = self.info
	
	-- Create an info table from pattern table, copies everything except the pattern string.	
	for i, v in pairs(patternInfo) do
		if i == 1 then
			info.type = v
		elseif type(i) == "number" then
			if type(v) == "number" and v < 100 then
				info[ self:GetInfoFieldName( self.patternTable[patternName][1], i) ] = info[v]
			else
				info[ self:GetInfoFieldName( self.patternTable[patternName][1], i) ] = v
			end
		end
	end
	
	if info.type == "honor" and not info.amount then
		info.isDishonor = true
		
	elseif info.type == "durability" and not info.item  then
		info.isAllItems = true
	end
	
	for i in ipairs(info) do
		info[i] = nil
	end
	
end

function lib:ConvertTypes(info)
	for i in pairs(info) do
		if string.find(i, "^amount") then info[i] = tonumber(info[i]) end
	end
end

function lib:OptimizerCheck(message, patternName)
	
	--if 1 then return true end
	if not ParserLibOptimizer then return true end
	
	if not ParserLibOptimizer[patternName] then return true end
	
	if string.find(message, ParserLibOptimizer[patternName], 1, true) then return true end
	
	return false
end

-- Most of the parts were learnt from BabbleLib by chknight, so credits goes to him.
function lib:Curry(pattern)
	local cp, tt, n, f, o
	local DoNothing = function(tok) return tok end	
	
	tt = {}
	for tk in string_gmatch(pattern, "%%%d?%$?([sd])") do
		table.insert(tt, tk)
	end	
	
	cp = { self:ConvertPattern(pattern, true) }
	cp.p = cp[1]
	
	n = table.getn(cp)
	for i=1,n-1 do
		cp[i] = cp[i+1]
	end
	table.remove(cp, n)
	
	f = {}
	o = {}
	n = table.getn(tt)
	for i=1, n do
		if tt[i] == "s" then
			f[i] = DoNothing
		else
			f[i] = tonumber
		end
	end
	
	if not cp[1] then
		if n == 0 then
			return function() end
		elseif n == 1 then
			return function(text)
				_, _, o[1] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1])
				end
			end
		elseif n == 2 then
			return function(text)
				_, _, o[1], o[2]= string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2])
				end
			end
		elseif n == 3 then
			return function(text)
				_, _, o[1], o[2], o[3] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3])
				end
			end
		elseif n == 4 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4])
				end
			end
		elseif n == 5 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5])
				end
			end
		elseif n == 6 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6])
				end
			end
		elseif n == 7 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7])
				end
			end
		elseif n == 8 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7]), f[8](o[8])
				end
			end
		elseif n == 9 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9] = string.find(text, cp.p)
				if o[1] then
					return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7]), f[8](o[8]), f[9](o[9])
				end
			end
		end
	else	
		if n == 0 then
			return function() end
		elseif n == 1 then
			return function(text)
				_, _, o[1] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]])
				end
			end			elseif n == 2 then
			return function(text)
				_, _, o[1], o[2] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]])
				end
			end			elseif n == 3 then
			return function(text)
				_, _, o[1], o[2], o[3] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]])
				end
			end		
		elseif n == 4 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]])
				end
			end			elseif n == 5 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]])
				end
			end			elseif n == 6 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]])
				end
			end			elseif n == 7 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]])
				end
			end			elseif n == 8 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]]), f[cp[8]](o[cp[8]])
				end
			end			elseif n == 9 then
			return function(text)
				_, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9] = string.find(text, cp.p)
				if o[1] then
					return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]]), f[cp[8]](o[cp[8]]), f[cp[9]](o[cp[9]])
				end
			end		
		end		
	end
end


function lib:PrintTable(args)
	for k, v in pairs(args) do
		ChatFrame1:AddMessage(tostring(k) .. " = " .. tostring(v));
	end
	ChatFrame1:AddMessage("");
end

-- Used to test the correcteness of ParserLib on different languages.
function lib:TestPatterns(sendToClients)
	
	
	self:LoadEverything()
	
	
	-- Creating the combat messages.	
	local testNumber = 123
	local message
	local messages = self:GetCompost():Acquire()
	for patternName in pairs(self.patternTable) do
		messages[patternName] = self:GetCompost():Acquire()
		messages[patternName].message = getglobal(patternName)
		for i, v in pairs(self.patternTable[patternName]) do
			if i ~= "tc" and type(v) == "number" and v < 100 and i~=1 then
				messages[patternName][v] =  self:GetInfoFieldName(self.patternTable[patternName][1], i)
			end
		end
		for i, v in ipairs(messages[patternName]) do
			if string.find(v, "^amount") then
				messages[patternName].message = string.gsub(messages[patternName].message, "%%%d?%$?d", testNumber, 1)
			else
				messages[patternName].message = string.gsub(messages[patternName].message, "%%%d?%$?s", string.upper(v), 1)
			end
		end
	end
	
	
	-- Begin the test.
	
	local info, msg
	local startTime = GetTime() 
	local startMem = gcinfo()
	
	for _, event in pairs(self.supportedEvents) do
		for _, pattern in pairs(self:LoadPatternList(event)) do
			msg = messages[pattern].message
			if sendToClients then self:OnEvent(event, msg)	end
			if self:ParseMessage(msg, event) then
				info = self.info
				for i, v in ipairs(messages[pattern]) do
					if not info[v] 
						or ( string.find(v, "^amount") and info[v] ~= testNumber ) 
						or ( not string.find(v, "^amount") and info[v] ~= string.upper(v) ) then
						self:Print("Event: " .. event)
						self:Print("Pattern: " .. pattern)
						self:Print("Message: " .. msg)
						self:PrintTable(messages[pattern])
						self:PrintTable(info)
					end
				end
			end
		end
	end
	
	self:Print( string.format("Test completed in %.4fs, memory cost %.2fKB.", GetTime() - startTime, gcinfo() - startMem) )
	
	self:GetCompost():Reclaim(messages, 1)
	
end

function lib:LoadEverything()
	
	-- Load all patterns and events.
	for _, v in pairs(self.supportedEvents) do
		for _, w in pairs(self:LoadPatternList(v)) do
			if not self.patternTable[w] then
				self.patternTable[w] = self:LoadPatternInfo(w)
			end
		end
	end
	
end

function lib:PrintTimers()
	if not self.timer then return end
	
	local total = 0
	for i in pairs(self.timer) do
		total = total + self.timer[i]
	end
	
	if not self.timerIndex then 
		self.timerIndex = {}
		for i in pairs(self.timer) do
			table.insert(self.timerIndex, i)
		end
		table.sort(self.timerIndex)
	end
	
	DEFAULT_CHAT_FRAME:AddMessage("Time\t%\tDescription")
	for i, idx in pairs(self.timerIndex) do
		DEFAULT_CHAT_FRAME:AddMessage(string.format("%.3f\t%.1f\t%s", self.timer[idx], 100*self.timer[idx]/total, idx) )
	end	
	
	DEFAULT_CHAT_FRAME:AddMessage(total .. "\t100\tTotal")
end
-- Used to load eventTable elements on demand.
function lib:LoadPatternList(eventName)
	local list
	
	--------------- Melee Hits ----------------	
	
	if eventName == "CHAT_MSG_COMBAT_SELF_HITS" then		
		
		if not self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] then
			
			self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] = 
			self:LoadPatternCategoryTree(
				{
					"HitSelf",
					"EnvSelf",
				}
			)
			
		end
		
		list = self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] 
		
	elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS" 
		or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS" 
		or eventName == "CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS" 
		or eventName == "CHAT_MSG_COMBAT_PARTY_HITS" 
		or eventName == "CHAT_MSG_COMBAT_PET_HITS" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] then 
			self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] = 
			self:LoadPatternCategoryTree( {
				"HitOtherOther",
				"EnvOther",
			} )
		end
		list = self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"]
		
		
	elseif eventName == "CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS" 
		or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS"  then
		
		if not self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] then 
			self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] = 
			self:LoadPatternCategoryTree( {
				{
					"HitOtherOther",
					"HitOtherSelf",
				},
				"EnvOther",
			} )
		end
		list = self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"]
		
		
		
		--------------- Melee Misses ----------------	
		
		
		
	elseif eventName == "CHAT_MSG_COMBAT_SELF_MISSES" then
		if not self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"] then
			self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"] = 
			self:LoadPatternCategoryTree( {
				"MissSelf",
			} )
		end
		list = self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"]
		
	elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES"
		or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES"
		or eventName == "CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"
		or eventName == "CHAT_MSG_COMBAT_PARTY_MISSES"
		or eventName == "CHAT_MSG_COMBAT_PET_MISSES" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"] then
			self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"] = 
			self:LoadPatternCategoryTree( {
				"MissOtherOther",
			} )
		end
		
		list = self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"]	
		
		
	elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES"
		or eventName == "CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"] then
			self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"] = 
			self:LoadPatternCategoryTree( {
				{
					"MissOtherOther",
					"MissOtherSelf",
				}
			} )
		end
		
		list = self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"]	
		
		--------------- Spell Buffs ----------------	
	elseif eventName == "CHAT_MSG_SPELL_SELF_BUFF" then
		if not self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] then
			
			if GetLocale() ~= "deDE"  then
				self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] =  self:LoadPatternCategoryTree(
					{
						"HealSelf",
						"EnchantSelf",
						"CastSelf",
						"PerformSelf",
						"DispelFailSelf",
						"SPELLCASTSELFSTART",
						"SPELLPERFORMSELFSTART",
						{
							"DrainSelf",
							"PowerGainSelf",
							"ExtraAttackSelf",
						},
						"SPELLSPLITDAMAGESELFOTHER",
						{
							"ProcResistSelf",
							"SpellMissSelf",
						}
					}
				)
				
			else
				self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] =  self:LoadPatternCategoryTree(
					{
						"HealSelf",
						"CastSelf",
						"PerformSelf",
						"DispelFailSelf",
						"SPELLCASTSELFSTART",
						"SPELLPERFORMSELFSTART",
						{
							"DrainSelf",
							"PowerGainSelf",
							"ExtraAttackSelf",
						},
						"SPELLSPLITDAMAGESELFOTHER",
						{
							"ProcResistSelf",
							"SpellMissSelf",
						}
					}
				)
			end
			
		end		
		
		list = self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"]
		
		
	elseif eventName == "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF"
		or eventName == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF"  
		or eventName == "CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF"  
		or eventName == "CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF"  
		or eventName == "CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF" 
		or eventName == "CHAT_MSG_SPELL_PARTY_BUFF" 
		or eventName == "CHAT_MSG_SPELL_PET_BUFF" then
		
		if not self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] then
			
			if GetLocale() ~= "deDE"  then
				self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] = self:LoadPatternCategoryTree(
					{
						{
							"HealOther",
							"PowerGainOther",
							"ExtraAttackOther",
							"DrainOther",
						},
						"SPELLCASTOTHERSTART",
						{
							"EnchantOther",
							"CastOther",
							"PerformOther",
						},
						"SPELLPERFORMOTHERSTART",
						"SpellMissOther",
						"ProcResistOther",
						"SplitDamageOther",
						"DispelFailOther",
					}
				)
				
			else -- Remove "EnchantOther" from German, since it's 100% ambiguous with SIMPLECASTOTHEROTHER, which is unsolvable.
				self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] = self:LoadPatternCategoryTree(
					{
						{
							"HealOther",
							"PowerGainOther",
							"ExtraAttackOther",
							"DrainOther",
						},
						"SPELLCASTOTHERSTART",
						{
							"CastOther",
							"PerformOther",
						},
						"SPELLPERFORMOTHERSTART",
						"SpellMissOther",
						"ProcResistOther",
						"SplitDamageOther",
						"DispelFailOther",
					}
				)
			end
		end
		
		list = self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"]
		
		
		--------------- Spell Damages ----------------	
		
	elseif eventName == "CHAT_MSG_SPELL_SELF_DAMAGE" then	
		if not self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"] then
			self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"] = 
			self:LoadPatternCategoryTree( {
				"SpellHitSelf",
				{
					"CastSelf",
					"DurabilityDamageSelf",
				},
				"PerformSelf",
				"SpellMissSelf",
				"SPELLCASTSELFSTART",
				"SPELLPERFORMSELFSTART",
				"InterruptSelf",
				"DispelFailSelf",
				"ExtraAttackSelf",
				"DrainSelf",
				
			} )
			
		end		
		list = self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"]	
		
		
	elseif eventName == "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE" 
		or eventName == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE"  
		or eventName == "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE"  
		or eventName == "CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE"  
		or eventName == "CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE" 
		or eventName == "CHAT_MSG_SPELL_PARTY_DAMAGE"  
		or eventName == "CHAT_MSG_SPELL_PET_DAMAGE"  then
		
		if not self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] then
			self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] = 
			self:LoadPatternCategoryTree( {
				"SpellHitOther",
				"SPELLCASTOTHERSTART",
				"SPELLPERFORMOTHERSTART",
				"DrainOther",
				"SpellMissOther",
				{
					"INSTAKILLOTHER",
					"INSTAKILLSELF",
				},
				{
					"PROCRESISTOTHEROTHER",
					"PROCRESISTOTHERSELF",
				},
				"SplitDamageOther",
				{
					"CastOther",
					"InterruptOther",
					"DurabilityDamageOther",
				},
				"PerformOther",
				"ExtraAttackOther",
				{
					"DISPELFAILEDOTHEROTHER",
					"DISPELFAILEDOTHERSELF",						
				},
			})
			
		end
		list = self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"]
		
		
		--------------- Periodic Buffs ----------------	
		
	elseif eventName == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" then
		
		if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] then
			
			self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] = 
			self:LoadPatternCategoryTree( {
				{
					"HotOther",
					"HotSelf",
				},
				{
					"BuffSelf",
					"BuffOther",
					"PowerGainOther",
					"PowerGainSelf",
				},
				"DrainSelf",
				"DotSelf",	-- Don't think this will hapen but add it anyway.
			} )
		end		
		list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"]	
		
		
	elseif eventName == "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS" then
		
		if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] then
			self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] = 
			self:LoadPatternCategoryTree( {
				{
					"HotOther",
					--						"DrainOther",	-- Dont think this would happen but add it anyway.
				},
				{
					"BuffOther",
					"PowerGainOther",
					"DrainOther",	-- When other players use Skull of Impending Doom.
				},
				"DotOther",	-- Dont think this will happen but add anyway.
				"DebuffOther", -- Was fired on older WoW version.
			} )
		end
		
		list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"]
		
		
		
		--------------- Periodic Damages ----------------	
		
	elseif eventName == "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE" then
		if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] then
			
			self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] = 
			self:LoadPatternCategoryTree( {
				{
					"DotSelf",
					"DotOther",
				},
				{
					"DebuffSelf",
					"DebuffOther",
				},
				{
					"SPELLLOGABSORBOTHEROTHER",
					"SPELLLOGABSORBOTHERSELF",
					"SPELLLOGABSORBSELFSELF",
					"SPELLLOGABSORBSELFOTHER",
				},
				"DrainSelf",
			}	)
		end	
		list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"]	
		
		
		
	elseif eventName == "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"
		or eventName == "CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE" then
		
		if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] then
			self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] = 
			self:LoadPatternCategoryTree( {
				"DebuffOther",
				"DotOther",
				{
					"SPELLLOGABSORBOTHEROTHER",
					"SPELLLOGABSORBSELFOTHER",
				},					
				"DrainOther",
				{
					"PowerGainOther",
					"BuffOther", -- Was fired on older WoW version.					
				}
			} )
		end			
		list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"]
		
		--------------- Damage Shields ----------------	
		
		
	elseif eventName == "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF" then
		if not self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] then
			self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] = {
				"SPELLRESISTOTHEROTHER",
				"SPELLRESISTSELFOTHER",
				"DAMAGESHIELDOTHEROTHER",
				"DAMAGESHIELDSELFOTHER",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] , PatternCompare)
		end
		list = self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"]	
		
	elseif eventName == "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS" then
		if not self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] then
			self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] = {
				"SPELLRESISTOTHEROTHER",
				"SPELLRESISTOTHERSELF",
				"DAMAGESHIELDOTHEROTHER",
				"DAMAGESHIELDOTHERSELF",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"]
		
		
		
		--------------- Auras ----------------	
		
	elseif eventName == "CHAT_MSG_SPELL_AURA_GONE_PARTY"
		or eventName == "CHAT_MSG_SPELL_AURA_GONE_OTHER" then
		
		if not self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] then
			self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] = {
				"AURAREMOVEDOTHER",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] , PatternCompare)
		end
		list = self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"]
		
	elseif  eventName == "CHAT_MSG_SPELL_AURA_GONE_SELF" then
		
		if not self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] then
			self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] = {
				"AURAREMOVEDOTHER",
				"AURAREMOVEDSELF",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] , PatternCompare)
		end
		list = self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"]
		
	elseif eventName == "CHAT_MSG_SPELL_BREAK_AURA" then
		
		if not self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] then
			self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] = {
				"AURADISPELSELF",
				"AURADISPELOTHER",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"]			
		
		
		
	elseif eventName == "CHAT_MSG_SPELL_ITEM_ENCHANTMENTS" then
		
		if not self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] then
			self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] = {
				"ITEMENCHANTMENTADDSELFSELF",
				"ITEMENCHANTMENTADDSELFOTHER",
				"ITEMENCHANTMENTADDOTHEROTHER",
				"ITEMENCHANTMENTADDOTHERSELF",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] , PatternCompare)
		end			
		list = self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"]	
		
		--------------- Trade Skills ----------------	
		
		
	elseif eventName == "CHAT_MSG_SPELL_TRADESKILLS" then
		if not self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"] then
			self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"] = {
				"TRADESKILL_LOG_FIRSTPERSON",
				"TRADESKILL_LOG_THIRDPERSON",
				"FEEDPET_LOG_FIRSTPERSON",
				"FEEDPET_LOG_THIRDPERSON",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"], PatternCompare )
		end		
		list = self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"]	
		
	elseif eventName == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER" then
		
		if not self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] then
			self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] = {
				"SPELLFAILPERFORMSELF",
				"SPELLFAILCASTSELF",
			}
			table.sort(self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"], PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] 	
		
		
	elseif eventName == "CHAT_MSG_COMBAT_FACTION_CHANGE" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] then
			
			self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] = {
				"FACTION_STANDING_CHANGED",
				"FACTION_STANDING_DECREASED",
				"FACTION_STANDING_INCREASED",
			}
			table.sort(self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"]
		
	elseif eventName == "CHAT_MSG_COMBAT_HONOR_GAIN" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] then
			self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] = {
				"COMBATLOG_HONORAWARD",
				"COMBATLOG_HONORGAIN",
				"COMBATLOG_DISHONORGAIN",
			}
			table.sort(self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"]
	elseif eventName == "CHAT_MSG_COMBAT_XP_GAIN" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] then
			self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] = {
				"COMBATLOG_XPGAIN",
				"COMBATLOG_XPGAIN_EXHAUSTION1",
				"COMBATLOG_XPGAIN_EXHAUSTION1_GROUP",
				"COMBATLOG_XPGAIN_EXHAUSTION1_RAID",
				"COMBATLOG_XPGAIN_EXHAUSTION2",
				"COMBATLOG_XPGAIN_EXHAUSTION2_GROUP",
				"COMBATLOG_XPGAIN_EXHAUSTION2_RAID",
				"COMBATLOG_XPGAIN_EXHAUSTION4",
				"COMBATLOG_XPGAIN_EXHAUSTION4_GROUP",
				"COMBATLOG_XPGAIN_EXHAUSTION4_RAID",
				"COMBATLOG_XPGAIN_EXHAUSTION5",
				"COMBATLOG_XPGAIN_EXHAUSTION5_GROUP",
				"COMBATLOG_XPGAIN_EXHAUSTION5_RAID",
				"COMBATLOG_XPGAIN_FIRSTPERSON",
				"COMBATLOG_XPGAIN_FIRSTPERSON_GROUP",
				"COMBATLOG_XPGAIN_FIRSTPERSON_RAID",
				"COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED",
				"COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_GROUP",
				"COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_RAID",
				"COMBATLOG_XPLOSS_FIRSTPERSON_UNNAMED",
			}
			table.sort(self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"]		
		
	elseif eventName == "CHAT_MSG_COMBAT_FRIENDLY_DEATH" 
		or eventName == "CHAT_MSG_COMBAT_HOSTILE_DEATH" then
		
		if not self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] then
			self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] = {
				"SELFKILLOTHER",
				"PARTYKILLOTHER",
				"UNITDESTROYEDOTHER",
				"UNITDIESOTHER",
				"UNITDIESSELF",
			}
			
			table.sort(self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] , PatternCompare)
		end		
		list = self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"]	
		
	end
	
	
	if not list then
		-- self:Print(string.format("Event '%s' not found.", eventName), 1, 0,0); -- debug
	end
	
	return list
	
end

function lib:LoadPatternCategory(category)
	
	local list
	
	if category == "AuraChange" then		list = {
		"AURACHANGEDOTHER",
		"AURACHANGEDSELF",
	}
elseif category == "AuraDispel" then		list = {
		"AURADISPELOTHER",
		"AURADISPELSELF",
	}
elseif category == "BuffOther" then		list = {
		"AURAADDEDOTHERHELPFUL",
		"AURAAPPLICATIONADDEDOTHERHELPFUL",
	}
elseif category == "BuffSelf" then		list = {
		"AURAADDEDSELFHELPFUL",
		"AURAAPPLICATIONADDEDSELFHELPFUL",
	}
elseif category == "CastOther" then	list = {
		"SIMPLECASTOTHEROTHER",
		"SIMPLECASTOTHERSELF",
		"SPELLTERSE_OTHER",
	}
elseif category == "CastSelf" then	list = {
		"SIMPLECASTSELFOTHER",
		"SIMPLECASTSELFSELF",
		"SPELLTERSE_SELF",
	}
elseif category == "DebuffOther" then		list = {
		"AURAADDEDOTHERHARMFUL",
		"AURAAPPLICATIONADDEDOTHERHARMFUL",
	}
elseif category == "DebuffSelf" then		list = {
		"AURAADDEDSELFHARMFUL",
		"AURAAPPLICATIONADDEDSELFHARMFUL",
	}
elseif category == "DispelFailOther" then	list = {
		"DISPELFAILEDOTHEROTHER",
		"DISPELFAILEDOTHERSELF",
		
	}
elseif category == "DispelFailSelf" then		list = {
		"DISPELFAILEDSELFOTHER",
		"DISPELFAILEDSELFSELF",
	}
elseif category == "DmgShieldOther" then list = {
		"DAMAGESHIELDOTHEROTHER",
		"DAMAGESHIELDOTHERSELF",
	}
elseif category == "DmgShieldSelf" then		list = {
		"DAMAGESHIELDSELFOTHER",
	}
elseif category == "DurabilityDamageSelf" then list = {
		"SPELLDURABILITYDAMAGEALLSELFOTHER",
		"SPELLDURABILITYDAMAGESELFOTHER",	
	}
elseif category == "DurabilityDamageOther" then list = {
		"SPELLDURABILITYDAMAGEALLOTHEROTHER",
		"SPELLDURABILITYDAMAGEALLOTHERSELF", 
		"SPELLDURABILITYDAMAGEOTHEROTHER", 
		"SPELLDURABILITYDAMAGEOTHERSELF",
	}
elseif category == "EnchantOther" then		list  =  {
		"ITEMENCHANTMENTADDOTHEROTHER",
		"ITEMENCHANTMENTADDOTHERSELF",
	}
elseif category == "EnchantSelf" then		list = {
		"ITEMENCHANTMENTADDSELFOTHER",
		"ITEMENCHANTMENTADDSELFSELF",
	}
elseif category == "ExtraAttackOther" then		list = {
		"SPELLEXTRAATTACKSOTHER",
		"SPELLEXTRAATTACKSOTHER_SINGULAR",
	}		
elseif category == "ExtraAttackSelf" then		list = {
		"SPELLEXTRAATTACKSSELF",
		"SPELLEXTRAATTACKSSELF_SINGULAR",
	}		
elseif category == "Fade" then		list = {
		"AURAREMOVEDOTHER",
		"AURAREMOVEDSELF",
	}
elseif category == "HealOther" then		list = {
		"HEALEDCRITOTHEROTHER",
		"HEALEDCRITOTHERSELF",
		"HEALEDOTHEROTHER",
		"HEALEDOTHERSELF",
	}
elseif category == "HealSelf" then		list = {
		"HEALEDCRITSELFOTHER",
		"HEALEDCRITSELFSELF",
		"HEALEDSELFOTHER",
		"HEALEDSELFSELF",
	}
elseif category == "HitOtherOther" then		list = {
		"COMBATHITCRITOTHEROTHER",
		"COMBATHITCRITSCHOOLOTHEROTHER",
		"COMBATHITOTHEROTHER",
		"COMBATHITSCHOOLOTHEROTHER",
	}
elseif category == "HitOtherSelf" then		list = {
		
		"COMBATHITCRITOTHERSELF",
		"COMBATHITCRITSCHOOLOTHERSELF",
		"COMBATHITOTHERSELF",
		"COMBATHITSCHOOLOTHERSELF",
	}
elseif category == "HitSelf" then		list = {
		"COMBATHITSCHOOLSELFOTHER",
		"COMBATHITSELFOTHER",
		"COMBATHITCRITSCHOOLSELFOTHER",
		"COMBATHITCRITSELFOTHER",
	}
elseif category == "MissOtherOther" then	list = {
		"MISSEDOTHEROTHER",
		"VSABSORBOTHEROTHER",
		"VSBLOCKOTHEROTHER",
		"VSDEFLECTOTHEROTHER",
		"VSDODGEOTHEROTHER",
		"VSEVADEOTHEROTHER",
		"VSIMMUNEOTHEROTHER",
		"VSPARRYOTHEROTHER",
		"VSRESISTOTHEROTHER",
		"IMMUNEDAMAGECLASSOTHEROTHER",
		"IMMUNEOTHEROTHER",
		
	}	
elseif category == "MissOtherSelf" then	list = {
		"MISSEDOTHERSELF",
		"VSABSORBOTHERSELF",
		"VSBLOCKOTHERSELF",
		"VSDEFLECTOTHERSELF",
		"VSDODGEOTHERSELF",
		"VSEVADEOTHERSELF",
		"VSIMMUNEOTHERSELF",
		"VSPARRYOTHERSELF",
		"VSRESISTOTHERSELF",	
		"IMMUNEDAMAGECLASSOTHERSELF",
		"IMMUNEOTHERSELF",
	}	
elseif category == "MissSelf" then	list = {
		"MISSEDSELFOTHER",
		"VSABSORBSELFOTHER",
		"VSBLOCKSELFOTHER",
		"VSDEFLECTSELFOTHER",
		"VSDODGESELFOTHER",
		"VSEVADESELFOTHER",
		"VSIMMUNESELFOTHER",
		"VSPARRYSELFOTHER",
		"VSRESISTSELFOTHER",
		"IMMUNEDAMAGECLASSSELFOTHER",
		"IMMUNESELFOTHER",
		"IMMUNESELFSELF",
	}
elseif category == "PowerGainOther" then		list = {
		"POWERGAINOTHEROTHER",
		"POWERGAINOTHERSELF",
	}
elseif category == "PerformOther" then list = {
		"OPEN_LOCK_OTHER",
		"SIMPLEPERFORMOTHEROTHER",
		"SIMPLEPERFORMOTHERSELF",
		"SPELLTERSEPERFORM_OTHER",
	}
elseif category == "PerformSelf" then	list = {
		"OPEN_LOCK_SELF",
		"SIMPLEPERFORMSELFOTHER",
		"SIMPLEPERFORMSELFSELF",
		"SPELLTERSEPERFORM_SELF",
	}
elseif category == "ProcResistOther" then list = {
		"PROCRESISTOTHEROTHER",
		"PROCRESISTOTHERSELF",
	}
elseif category == "ProcResistSelf" then	list = {
		"PROCRESISTSELFOTHER",
		"PROCRESISTSELFSELF",
	}
elseif category == "EnvOther" then list = {
		"VSENVIRONMENTALDAMAGE_DROWNING_OTHER",
		"VSENVIRONMENTALDAMAGE_FALLING_OTHER",
		"VSENVIRONMENTALDAMAGE_FATIGUE_OTHER",
		"VSENVIRONMENTALDAMAGE_FIRE_OTHER",
		"VSENVIRONMENTALDAMAGE_LAVA_OTHER",
		"VSENVIRONMENTALDAMAGE_SLIME_OTHER",
	}
elseif category == "EnvSelf" then	list = {
		"VSENVIRONMENTALDAMAGE_DROWNING_SELF",
		"VSENVIRONMENTALDAMAGE_FALLING_SELF",
		"VSENVIRONMENTALDAMAGE_FATIGUE_SELF",
		"VSENVIRONMENTALDAMAGE_FIRE_SELF",
		"VSENVIRONMENTALDAMAGE_LAVA_SELF",
		"VSENVIRONMENTALDAMAGE_SLIME_SELF",
	}
	-- HoT effects on others. (not casted by others)
elseif category == "HotOther" then	list = {
		"PERIODICAURAHEALOTHEROTHER",
		"PERIODICAURAHEALSELFOTHER",
	}
	-- HoT effects on you. (not casted by you)
elseif category == "HotSelf" then	list = {
		"PERIODICAURAHEALSELFSELF",
		"PERIODICAURAHEALOTHERSELF",
	}
elseif category == "PowerGainSelf" then	list = {
		"POWERGAINSELFSELF",
		"POWERGAINSELFOTHER",
	}
elseif category == "BuffOther" then	list = {
		"AURAAPPLICATIONADDEDOTHERHELPFUL",
		"AURAADDEDOTHERHELPFUL",
	}
elseif category == "BuffSelf" then	list = {
		"AURAADDEDSELFHELPFUL",
		"AURAAPPLICATIONADDEDSELFHELPFUL",
	}
elseif category == "DrainSelf" then	list = {	
		"SPELLPOWERLEECHSELFOTHER",
		"SPELLPOWERDRAINSELFOTHER",
		"SPELLPOWERDRAINSELFSELF",
	}
elseif category == "DrainOther" then	list = {	
		"SPELLPOWERLEECHOTHEROTHER",
		"SPELLPOWERLEECHOTHERSELF",
		"SPELLPOWERDRAINOTHEROTHER",
		"SPELLPOWERDRAINOTHERSELF",
	}
	-- DoT effects on others (not casted by others)
elseif category == "DotOther" then	list = {
		"PERIODICAURADAMAGEOTHEROTHER",
		"PERIODICAURADAMAGESELFOTHER",
	}
	-- DoT effects on you (not casted by you)
elseif category == "DotSelf" then	list = {
		"PERIODICAURADAMAGEOTHERSELF",
		"PERIODICAURADAMAGESELFSELF",
	}
elseif category == "SpellHitOther" then	list = {
		"SPELLLOGCRITOTHEROTHER",
		"SPELLLOGCRITOTHERSELF",
		"SPELLLOGCRITSCHOOLOTHEROTHER",
		"SPELLLOGCRITSCHOOLOTHERSELF",
		"SPELLLOGOTHEROTHER",
		"SPELLLOGOTHERSELF",
		"SPELLLOGSCHOOLOTHEROTHER",
		"SPELLLOGSCHOOLOTHERSELF",
	}
elseif category == "SpellHitSelf" then	list = {
		"SPELLLOGCRITSELFOTHER",
		"SPELLLOGCRITSELFSELF",
		"SPELLLOGCRITSCHOOLSELFOTHER",
		"SPELLLOGCRITSCHOOLSELFSELF",
		"SPELLLOGSELFOTHER",
		"SPELLLOGSELFSELF",
		"SPELLLOGSCHOOLSELFOTHER",
		"SPELLLOGSCHOOLSELFSELF",
	}
elseif category == "SpellMissSelf" then		list = {
		"IMMUNESPELLSELFOTHER",
		"IMMUNESPELLSELFSELF",
		"SPELLBLOCKEDSELFOTHER",
		"SPELLDEFLECTEDSELFOTHER",
		"SPELLDEFLECTEDSELFSELF",
		"SPELLDODGEDSELFOTHER",
		"SPELLDODGEDSELFSELF",
		"SPELLEVADEDSELFOTHER",
		"SPELLEVADEDSELFSELF",
		"SPELLIMMUNESELFOTHER",
		"SPELLIMMUNESELFSELF",
		"SPELLLOGABSORBSELFOTHER",
		"SPELLLOGABSORBSELFSELF",
		"SPELLMISSSELFOTHER",
		"SPELLMISSSELFSELF",
		"SPELLPARRIEDSELFOTHER",
		"SPELLPARRIEDSELFSELF",
		"SPELLREFLECTSELFOTHER",
		"SPELLREFLECTSELFSELF",
		"SPELLRESISTSELFOTHER",
		"SPELLRESISTSELFSELF",
	}
elseif category == "SpellMissOther" then		list = {
		"IMMUNESPELLOTHEROTHER",
		"IMMUNESPELLOTHERSELF",
		"SPELLBLOCKEDOTHEROTHER",
		"SPELLBLOCKEDOTHERSELF",
		"SPELLDODGEDOTHEROTHER",
		"SPELLDODGEDOTHERSELF",
		"SPELLDEFLECTEDOTHEROTHER",
		"SPELLDEFLECTEDOTHERSELF",
		"SPELLEVADEDOTHEROTHER",
		"SPELLEVADEDOTHERSELF",
		"SPELLIMMUNEOTHEROTHER",
		"SPELLIMMUNEOTHERSELF",
		"SPELLLOGABSORBOTHEROTHER",
		"SPELLLOGABSORBOTHERSELF",
		"SPELLMISSOTHEROTHER",
		"SPELLMISSOTHERSELF",
		"SPELLPARRIEDOTHEROTHER",
		"SPELLPARRIEDOTHERSELF",
		"SPELLREFLECTOTHEROTHER",
		"SPELLREFLECTOTHERSELF",
		"SPELLRESISTOTHEROTHER",
		"SPELLRESISTOTHERSELF",
	}	
elseif category == "InterruptOther" then	list = {
		"SPELLINTERRUPTOTHEROTHER",
		"SPELLINTERRUPTOTHERSELF",
	}
elseif category == "InterruptSelf" then	list = {
		"SPELLINTERRUPTSELFOTHER",
	}
elseif category == "SplitDamageOther" then list = {
		"SPELLSPLITDAMAGEOTHEROTHER",
		"SPELLSPLITDAMAGEOTHERSELF",
	}		
else return { category }
	end
	
	return list
	
end

-- Load categories recursively. First layer will not be sorted.
function lib:LoadPatternCategoryTree(catTree, reSort)
	if type(catTree) ~= "table" then return end
	
	local resultList = {}
	local list
	
	for i, v in pairs(catTree) do
		
		if type(v) == "table" then
			list = self:LoadPatternCategoryTree(v, true)
		else -- should be string.		
			list = self:LoadPatternCategory(v)
			table.sort(list, PatternCompare)
		end
		
		for j, w in pairs(list) do
			table.insert(resultList, w)
		end
		
	end
	
	if reSort then
		table.sort(resultList, PatternCompare)
	end
	
	return resultList
	
	
end

-- Used to load patternTable elements on demand.
function lib:LoadPatternInfo(patternName)
	
	local patternInfo
	
	if patternName == "AURAADDEDOTHERHELPFUL" then
		patternInfo = { "buff", 1, 2, nil, }
	elseif patternName == "AURAADDEDSELFHELPFUL" then
		patternInfo = { "buff", ParserLib_SELF, 1, nil, }
	elseif patternName == "AURAAPPLICATIONADDEDOTHERHELPFUL" then
		patternInfo = { "buff", 1, 2, 3, }
	elseif patternName == "AURAAPPLICATIONADDEDSELFHELPFUL" then
		patternInfo = { "buff", ParserLib_SELF, 1, 2, }
		
	elseif patternName == "OPEN_LOCK_OTHER" then
		patternInfo = { "cast", 1, 2, 3, nil, true, }
	elseif patternName == "OPEN_LOCK_SELF" then 
		patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, true, }
	elseif patternName == "SIMPLECASTOTHEROTHER" then
		patternInfo = { "cast", 1, 2, 3, nil, nil, }
	elseif patternName == "SIMPLECASTOTHERSELF" then
		patternInfo = { "cast", 1, 2, ParserLib_SELF, nil, nil, }
	elseif patternName == "SIMPLECASTSELFOTHER" then
		patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, nil, }
	elseif patternName == "SIMPLECASTSELFSELF" then
		patternInfo = { "cast", ParserLib_SELF, 1, ParserLib_SELF, nil, nil, }
	elseif patternName == "SIMPLEPERFORMOTHEROTHER" then
		patternInfo = { "cast", 1, 2, 3, nil, true, }
	elseif patternName == "SIMPLEPERFORMOTHERSELF" then
		patternInfo = { "cast", 1, 2, ParserLib_SELF, nil, true, }
	elseif patternName == "SIMPLEPERFORMSELFOTHER" then
		patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, true, }
	elseif patternName == "SIMPLEPERFORMSELFSELF" then
		patternInfo = { "cast", ParserLib_SELF, 1, ParserLib_SELF, nil, true, }
	elseif patternName == "SPELLCASTOTHERSTART" then
		patternInfo = { "cast", 1, 2, nil, true, nil, }
	elseif patternName == "SPELLCASTSELFSTART" then
		patternInfo = { "cast", ParserLib_SELF, 1, nil, true, nil, }
	elseif patternName == "SPELLPERFORMOTHERSTART" then
		patternInfo = { "cast", 1, 2, nil, true, true, }
	elseif patternName == "SPELLPERFORMSELFSTART" then
		patternInfo = { "cast", ParserLib_SELF, 1, nil, true, true, }
	elseif patternName == "SPELLTERSEPERFORM_OTHER" then
		patternInfo = { "cast", 1, 2, nil, nil, true, }
	elseif patternName == "SPELLTERSEPERFORM_SELF" then
		patternInfo = { "cast", ParserLib_SELF, 1, nil, nil, true, }
	elseif patternName == "SPELLTERSE_OTHER" then
		patternInfo = { "cast", 1, 2, nil, nil, nil, }
	elseif patternName == "SPELLTERSE_SELF" then
		patternInfo = { "cast", ParserLib_SELF, 1, nil, nil, nil, }
		
	elseif patternName == "TRADESKILL_LOG_FIRSTPERSON" then
		patternInfo = { "create", ParserLib_SELF, 1, }
	elseif patternName == "TRADESKILL_LOG_THIRDPERSON" then
		patternInfo = { "create", 1, 2, }
		
	elseif patternName == "PARTYKILLOTHER" then
		patternInfo = { "death", 1, 2, nil, nil, }
	elseif patternName == "SELFKILLOTHER" then
		patternInfo = { "death", 1, ParserLib_SELF, nil, nil, }
	elseif patternName == "UNITDESTROYEDOTHER" then
		patternInfo = { "death", 1, nil, nil, true, }
	elseif patternName == "UNITDIESOTHER" then
		patternInfo = { "death", 1, nil, nil, nil, }
	elseif patternName == "UNITDIESSELF" then
		patternInfo = { "death", ParserLib_SELF, nil, nil, nil, }
	elseif patternName == "INSTAKILLOTHER" then
		patternInfo = { "death", 1, nil, 2, nil, }
	elseif patternName == "INSTAKILLSELF" then
		patternInfo = { "death", ParserLib_SELF, nil, true, nil }
		
		
	elseif patternName == "AURAADDEDOTHERHARMFUL" then
		patternInfo = { "debuff", 1, 2, nil, }
	elseif patternName == "AURAADDEDSELFHARMFUL" then
		patternInfo = { "debuff", ParserLib_SELF, 1, nil, }
	elseif patternName == "AURAAPPLICATIONADDEDOTHERHARMFUL" then
		patternInfo = { "debuff", 1, 2, 3, }
	elseif patternName == "AURAAPPLICATIONADDEDSELFHARMFUL" then
		patternInfo = { "debuff", ParserLib_SELF, 1, 2, }
		
	elseif patternName == "AURADISPELOTHER" then
		patternInfo = { "dispel", 1, 2, nil, nil, }
	elseif patternName == "AURADISPELSELF" then
		patternInfo = { "dispel", ParserLib_SELF, 1, nil, nil, }
	elseif patternName == "DISPELFAILEDOTHEROTHER" then
		patternInfo = { "dispel", 2, 3, 1, true, }
	elseif patternName == "DISPELFAILEDOTHERSELF" then
		patternInfo = { "dispel", ParserLib_SELF, 2, 1, true, }
	elseif patternName == "DISPELFAILEDSELFOTHER" then
		patternInfo = { "dispel", 1, 2, ParserLib_SELF, true, }
	elseif patternName == "DISPELFAILEDSELFSELF" then
		patternInfo = { "dispel", ParserLib_SELF, 1, ParserLib_SELF, true, }
		
	elseif patternName == "SPELLPOWERDRAINOTHEROTHER" then
		patternInfo = { "drain", 1, 5, 2, 3, 4, }
	elseif patternName == "SPELLPOWERDRAINOTHERSELF" then
		patternInfo = { "drain", 1, ParserLib_SELF, 2, 3, 4, }
	elseif patternName == "SPELLPOWERDRAINSELFOTHER" then
		patternInfo = { "drain", ParserLib_SELF, 4, 1, 2, 3, }
	elseif patternName == "SPELLPOWERDRAINSELFSELF" then
		patternInfo = { "drain", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, }
		
	elseif patternName == "SPELLDURABILITYDAMAGEALLOTHEROTHER" then
		patternInfo = { "durability", 1, 2, 3, nil, }
	elseif patternName == "SPELLDURABILITYDAMAGEALLOTHERSELF" then
		patternInfo = { "durability", 1, 2, ParserLib_SELF, nil, }
	elseif patternName == "SPELLDURABILITYDAMAGEALLSELFOTHER" then
		patternInfo = { "durability", ParserLib_SELF, 1, 2, nil, }
	elseif patternName == "SPELLDURABILITYDAMAGEOTHEROTHER" then
		patternInfo = { "durability", 1, 2, 3, 4, }
	elseif patternName == "SPELLDURABILITYDAMAGEOTHERSELF" then
		patternInfo = { "durability", 1, 2, ParserLib_SELF, 3, }
	elseif patternName == "SPELLDURABILITYDAMAGESELFOTHER" then
		patternInfo = { "durability", ParserLib_SELF, 1, 2, 3, }
		
	elseif patternName == "ITEMENCHANTMENTADDOTHEROTHER" then
		patternInfo = { "enchant", 1, 3, 2, 4, }
	elseif patternName == "ITEMENCHANTMENTADDOTHERSELF" then
		patternInfo = { "enchant", 1, ParserLib_SELF, 2, 3, }
	elseif patternName == "ITEMENCHANTMENTADDSELFOTHER" then
		patternInfo = { "enchant", ParserLib_SELF, 2, 1, 3, }
	elseif patternName == "ITEMENCHANTMENTADDSELFSELF" then
		patternInfo = { "enchant", ParserLib_SELF, ParserLib_SELF, 1, 2, }
		
	elseif patternName == "VSENVIRONMENTALDAMAGE_DROWNING_OTHER" then
		patternInfo = { "environment", 1, 2, "drown", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_DROWNING_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "drown", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FALLING_OTHER" then
		patternInfo = { "environment", 1, 2, "fall", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FALLING_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "fall", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FATIGUE_OTHER" then
		patternInfo = { "environment", 1, 2, "exhaust", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FATIGUE_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "exhaust", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FIRE_OTHER" then
		patternInfo = { "environment", 1, 2, "fire", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_FIRE_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "fire", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_LAVA_OTHER" then
		patternInfo = { "environment", 1, 2, "lava", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_LAVA_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "lava", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_SLIME_OTHER" then
		patternInfo = { "environment", 1, 2, "slime", }
	elseif patternName == "VSENVIRONMENTALDAMAGE_SLIME_SELF" then
		patternInfo = { "environment", ParserLib_SELF, 1, "slime", }
		
	elseif patternName == "COMBATLOG_XPGAIN" then
		patternInfo = { "experience", 2, nil, nil, nil, nil, nil, nil, nil, 1, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1_GROUP" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, 5, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1_RAID" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, 5, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2_GROUP" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, 5, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2_RAID" then
		patternInfo = { "experience", 2, 1, 3, 4, nil, nil, 5, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4_GROUP" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, 5, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4_RAID" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, 5, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5_GROUP" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, 5, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5_RAID" then
		patternInfo = { "experience", 2, 1, nil, nil, 3, 4, 5, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON" then
		patternInfo = { "experience", 2, 1, nil, nil, nil, nil, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_GROUP" then
		patternInfo = { "experience", 2, 1, nil, nil, nil, nil, nil, 3, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_RAID" then
		patternInfo = { "experience", 2, 1, nil, nil, nil, nil, 3, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED" then
		patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, nil, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_GROUP" then
		patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, 2, nil, }
	elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_RAID" then
		patternInfo = { "experience", 1, nil, nil, nil, nil, nil, 2, nil, nil, }
	elseif patternName == "COMBATLOG_XPLOSS_FIRSTPERSON_UNNAMED" then
		patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, nil, nil, }
		
	elseif patternName == "SPELLEXTRAATTACKSOTHER" then
		patternInfo = { "extraattack", 1, 3, 2, }
	elseif patternName == "SPELLEXTRAATTACKSOTHER_SINGULAR" then
		patternInfo = { "extraattack", 1, 3, 2, }
	elseif patternName == "SPELLEXTRAATTACKSSELF" then
		patternInfo = { "extraattack", ParserLib_SELF, 2, 1, }
	elseif patternName == "SPELLEXTRAATTACKSSELF_SINGULAR" then
		patternInfo = { "extraattack", ParserLib_SELF, 2, 1, }
		
	elseif patternName == "AURAREMOVEDOTHER" then
		patternInfo = { "fade", 2, 1, }
	elseif patternName == "AURAREMOVEDSELF" then
		patternInfo = { "fade", ParserLib_SELF, 1, }
		
	elseif patternName == "SPELLFAILCASTSELF" then
		patternInfo = { "fail", ParserLib_SELF, 1, 2, }
	elseif patternName == "SPELLFAILPERFORMSELF" then
		patternInfo = { "fail", ParserLib_SELF, 1, 2, }
		
	elseif patternName == "FEEDPET_LOG_FIRSTPERSON" then
		patternInfo = { "feedpet", ParserLib_SELF, 1, }
	elseif patternName == "FEEDPET_LOG_THIRDPERSON" then
		patternInfo = { "feedpet", 1, 2, }
		
	elseif patternName == "POWERGAINOTHEROTHER" then
		patternInfo = { "gain", 4, 1, 5, 2, 3, }
	elseif patternName == "POWERGAINOTHERSELF" then
		patternInfo = { "gain", 3, ParserLib_SELF, 4, 1, 2, }
	elseif patternName == "POWERGAINSELFOTHER" then
		patternInfo = { "gain", ParserLib_SELF, 1, 4, 2, 3, }
	elseif patternName == "POWERGAINSELFSELF" then
		patternInfo = { "gain", ParserLib_SELF, ParserLib_SELF, 3, 1, 2, }
		
	elseif patternName == "HEALEDCRITOTHEROTHER" then
		patternInfo = { "heal", 1, 3, 2, 4, true, nil, }
	elseif patternName == "HEALEDCRITOTHERSELF" then
		patternInfo = { "heal", 1, ParserLib_SELF, 2, 3, true, nil, }
	elseif patternName == "HEALEDCRITSELFOTHER" then
		patternInfo = { "heal", ParserLib_SELF, 2, 1, 3, true, nil, }
	elseif patternName == "HEALEDCRITSELFSELF" then
		patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 1, 2, true, nil, }
	elseif patternName == "HEALEDOTHEROTHER" then
		patternInfo = { "heal", 1, 3, 2, 4, nil, nil, }
	elseif patternName == "HEALEDOTHERSELF" then
		patternInfo = { "heal", 1, ParserLib_SELF, 2, 3, nil, nil, }
	elseif patternName == "HEALEDSELFOTHER" then
		patternInfo = { "heal", ParserLib_SELF, 2, 1, 3, nil, nil, }
	elseif patternName == "HEALEDSELFSELF" then
		patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, nil, }
	elseif patternName == "PERIODICAURAHEALOTHEROTHER" then
		patternInfo = { "heal", 3, 1, 4, 2, nil, true, }
	elseif patternName == "PERIODICAURAHEALOTHERSELF" then
		patternInfo = { "heal", 2, ParserLib_SELF, 3, 1, nil, true, }
	elseif patternName == "PERIODICAURAHEALSELFOTHER" then
		patternInfo = { "heal", ParserLib_SELF, 1, 3, 2, nil, true, }
	elseif patternName == "PERIODICAURAHEALSELFSELF" then
		patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 2, 1, nil, true, }
		
	elseif patternName == "COMBATHITCRITOTHEROTHER" then
		patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, nil, true, nil, nil, }
	elseif patternName == "COMBATHITCRITOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, nil, true, nil, nil, }
	elseif patternName == "COMBATHITCRITSCHOOLOTHEROTHER" then
		patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, 4, true, nil, nil, }
	elseif patternName == "COMBATHITCRITSCHOOLOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, 3, true, nil, nil, }
	elseif patternName == "COMBATHITCRITSCHOOLSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, 3, true, nil, nil, }
	elseif patternName == "COMBATHITCRITSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, nil, true, nil, nil, }
	elseif patternName == "COMBATHITOTHEROTHER" then
		patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, nil, nil, nil, nil, }
	elseif patternName == "COMBATHITOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, nil, nil, nil, nil, }
	elseif patternName == "COMBATHITSCHOOLOTHEROTHER" then
		patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, 4, nil, nil, nil, }
	elseif patternName == "COMBATHITSCHOOLOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, 3, nil, nil, nil, }
	elseif patternName == "COMBATHITSCHOOLSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, 3, nil, nil, nil, }
	elseif patternName == "COMBATHITSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, nil, nil, nil, nil, }
	elseif patternName == "DAMAGESHIELDOTHEROTHER" then
		patternInfo = { "hit", 1, 4, ParserLib_DAMAGESHIELD, 2, 3, nil, nil, nil, }
	elseif patternName == "DAMAGESHIELDOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_DAMAGESHIELD, 2, 3, nil, nil, nil, }
	elseif patternName == "DAMAGESHIELDSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 3, ParserLib_DAMAGESHIELD, 1, 2, nil, nil, nil, }
	elseif patternName == "PERIODICAURADAMAGEOTHEROTHER" then
		patternInfo = { "hit", 4, 1, 5, 2, 3, nil, true, nil, }
	elseif patternName == "PERIODICAURADAMAGEOTHERSELF" then
		patternInfo = { "hit", 3, ParserLib_SELF, 4, 1, 2, nil, true, nil, }
	elseif patternName == "PERIODICAURADAMAGESELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 1, 4, 2, 3, nil, true, nil, }
	elseif patternName == "PERIODICAURADAMAGESELFSELF" then
		patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 3, 1, 2, nil, true, nil, }
	elseif patternName == "SPELLLOGCRITOTHEROTHER" then
		patternInfo = { "hit", 1, 3, 2, 4, nil, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSCHOOLOTHEROTHER" then
		patternInfo = { "hit", 1, 3, 2, 4, 5, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSCHOOLOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, 4, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSCHOOLSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, 4, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSCHOOLSELFSELF" then
		patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, true, nil, nil, }
	elseif patternName == "SPELLLOGCRITSELFSELF" then
		patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, true, nil, nil, }
	elseif patternName == "SPELLLOGOTHEROTHER" then
		patternInfo = { "hit", 1, 3, 2, 4, nil, nil, nil, nil, }
	elseif patternName == "SPELLLOGOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, nil, nil, nil, }
	elseif patternName == "SPELLLOGSCHOOLOTHEROTHER" then
		patternInfo = { "hit", 1, 3, 2, 4, 5, nil, nil, nil, }
	elseif patternName == "SPELLLOGSCHOOLOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, 4, nil, nil, nil, }
	elseif patternName == "SPELLLOGSCHOOLSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, 4, nil, nil, nil, }
	elseif patternName == "SPELLLOGSCHOOLSELFSELF" then
		patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, nil, nil, nil, }
	elseif patternName == "SPELLLOGSELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, nil, nil, nil, }
	elseif patternName == "SPELLLOGSELFSELF" then
		patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, nil, nil, nil, }
	elseif patternName == "SPELLSPLITDAMAGEOTHEROTHER" then
		patternInfo = { "hit", 1, 3, 2, 4, nil, nil, nil, true, }
	elseif patternName == "SPELLSPLITDAMAGEOTHERSELF" then
		patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, nil, nil, true, }
	elseif patternName == "SPELLSPLITDAMAGESELFOTHER" then
		patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, nil, nil, true, }
		
	elseif patternName == "COMBATLOG_DISHONORGAIN" then
		patternInfo = { "honor", nil, 1, nil, }
	elseif patternName == "COMBATLOG_HONORAWARD" then
		patternInfo = { "honor", 1, nil, nil, }
	elseif patternName == "COMBATLOG_HONORGAIN" then
		patternInfo = { "honor", 3, 1, 2, }
		
	elseif patternName == "SPELLINTERRUPTOTHEROTHER" then
		patternInfo = { "interrupt", 1, 2, 3, }
	elseif patternName == "SPELLINTERRUPTOTHERSELF" then
		patternInfo = { "interrupt", 1, ParserLib_SELF, 2, }
	elseif patternName == "SPELLINTERRUPTSELFOTHER" then
		patternInfo = { "interrupt", ParserLib_SELF, 1, 2, }
		
	elseif patternName == "SPELLPOWERLEECHOTHEROTHER" then
		patternInfo = { "leech", 1, 5, 2, 3, 4, 6, 7, 8, }
	elseif patternName == "SPELLPOWERLEECHOTHERSELF" then
		patternInfo = { "leech", 1, ParserLib_SELF, 2, 3, 4, 5, 6, 7, }
	elseif patternName == "SPELLPOWERLEECHSELFOTHER" then
		patternInfo = { "leech", ParserLib_SELF, 4, 1, 2, 3, ParserLib_SELF, 5, 6, }
		
	elseif patternName == "IMMUNEDAMAGECLASSOTHEROTHER" then
		patternInfo = { "miss", 2, 1, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNEDAMAGECLASSOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNEDAMAGECLASSSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNEOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNEOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNESELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNESELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, ParserLib_MELEE, "immune", }
	elseif patternName == "IMMUNESPELLOTHEROTHER" then
		patternInfo = { "miss", 2, 1, 3, "immune", }
	elseif patternName == "IMMUNESPELLOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "immune", }
	elseif patternName == "IMMUNESPELLSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, 2, "immune", }
	elseif patternName == "IMMUNESPELLSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "immune", }
	elseif patternName == "MISSEDOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "miss", }
	elseif patternName == "MISSEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "miss", }
	elseif patternName == "MISSEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "miss", }
	elseif patternName == "PROCRESISTOTHEROTHER" then
		patternInfo = { "miss", 2, 1, 3, "resist", }
	elseif patternName == "PROCRESISTOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "resist", }
	elseif patternName == "PROCRESISTSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, 2, "resist", }
	elseif patternName == "PROCRESISTSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "resist", }
	elseif patternName == "SPELLBLOCKEDOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "block", }
	elseif patternName == "SPELLBLOCKEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "block", }
	elseif patternName == "SPELLBLOCKEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "block", }
	elseif patternName == "SPELLDEFLECTEDOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "deflect", }
	elseif patternName == "SPELLDEFLECTEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "deflect", }
	elseif patternName == "SPELLDEFLECTEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "deflect", }
	elseif patternName == "SPELLDEFLECTEDSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "deflect", }
	elseif patternName == "SPELLDODGEDOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "dodge", }
	elseif patternName == "SPELLDODGEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "dodge", }
	elseif patternName == "SPELLDODGEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "dodge", }
	elseif patternName == "SPELLDODGEDSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "dodge", }
	elseif patternName == "SPELLEVADEDOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "evade", }
	elseif patternName == "SPELLEVADEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "evade", }
	elseif patternName == "SPELLEVADEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "evade", }
	elseif patternName == "SPELLEVADEDSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "evade", }
	elseif patternName == "SPELLIMMUNEOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "immune", }
	elseif patternName == "SPELLIMMUNEOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "immune", }
	elseif patternName == "SPELLIMMUNESELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "immune", }
	elseif patternName == "SPELLIMMUNESELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "immune", }
	elseif patternName == "SPELLLOGABSORBOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "absorb", }
	elseif patternName == "SPELLLOGABSORBOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "absorb", }
	elseif patternName == "SPELLLOGABSORBSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "absorb", }
	elseif patternName == "SPELLLOGABSORBSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "absorb", }
	elseif patternName == "SPELLMISSOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "miss", }
	elseif patternName == "SPELLMISSOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "miss", }
	elseif patternName == "SPELLMISSSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "miss", }
	elseif patternName == "SPELLMISSSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "miss", }
	elseif patternName == "SPELLPARRIEDOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "parry", }
	elseif patternName == "SPELLPARRIEDOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "parry", }
	elseif patternName == "SPELLPARRIEDSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "parry", }
	elseif patternName == "SPELLPARRIEDSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "parry", }
	elseif patternName == "SPELLREFLECTOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "reflect", }
	elseif patternName == "SPELLREFLECTOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "reflect", }
	elseif patternName == "SPELLREFLECTSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "reflect", }
	elseif patternName == "SPELLREFLECTSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "reflect", }
	elseif patternName == "SPELLRESISTOTHEROTHER" then
		patternInfo = { "miss", 1, 3, 2, "resist", }
	elseif patternName == "SPELLRESISTOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, 2, "resist", }
	elseif patternName == "SPELLRESISTSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 2, 1, "resist", }
	elseif patternName == "SPELLRESISTSELFSELF" then
		patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "resist", }
	elseif patternName == "VSABSORBOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "absorb", }
	elseif patternName == "VSABSORBOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "absorb", }
	elseif patternName == "VSABSORBSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "absorb", }
	elseif patternName == "VSBLOCKOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "block", }
	elseif patternName == "VSBLOCKOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "block", }
	elseif patternName == "VSBLOCKSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "block", }
	elseif patternName == "VSDEFLECTOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "deflect", }
	elseif patternName == "VSDEFLECTOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "deflect", }
	elseif patternName == "VSDEFLECTSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "deflect", }
	elseif patternName == "VSDODGEOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "dodge", }
	elseif patternName == "VSDODGEOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "dodge", }
	elseif patternName == "VSDODGESELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "dodge", }
	elseif patternName == "VSEVADEOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "evade", }
	elseif patternName == "VSEVADEOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "evade", }
	elseif patternName == "VSEVADESELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "evade", }
	elseif patternName == "VSIMMUNEOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "immune", }
	elseif patternName == "VSIMMUNEOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", }
	elseif patternName == "VSIMMUNESELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", }
	elseif patternName == "VSPARRYOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "parry", }
	elseif patternName == "VSPARRYOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "parry", }
	elseif patternName == "VSPARRYSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "parry", }
	elseif patternName == "VSRESISTOTHEROTHER" then
		patternInfo = { "miss", 1, 2, ParserLib_MELEE, "resist", }
	elseif patternName == "VSRESISTOTHERSELF" then
		patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "resist", }
	elseif patternName == "VSRESISTSELFOTHER" then
		patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "resist", }
		
	elseif patternName == "FACTION_STANDING_CHANGED" then
		patternInfo = { "reputation", 2, nil, 1, nil, }
	elseif patternName == "FACTION_STANDING_DECREASED" then
		patternInfo = { "reputation", 1, 2, nil, true, }
	elseif patternName == "FACTION_STANDING_INCREASED" then
		patternInfo = { "reputation", 1, 2, nil, nil, }
	end
	
	if not patternInfo then
		-- self:Print("LoadPatternInfo(): Cannot find " .. patternName ); -- debug
		return
	end
	
	local pattern = getglobal(patternName);	-- Get the pattern from GlobalStrings.lua
	
	-- Add a function to fix some global strings.
	if FixGlobalString then
		local fixPattern = FixGlobalString(patternName, pattern);
		if pattern ~= fixPattern then
			--self:Print("PP : "..pattern)  -- debug
			--self:Print("FP : "..fixPattern)  -- debug
			setglobal(patternName, fixPattern);
			pattern = fixPattern
		end
	end
	
	-- How many regexp tokens in this pattern?
	local tc = 0
	for _ in string_gmatch(pattern, "%%%d?%$?([sd])") do tc = tc + 1 end
	
	-- Convert string.format tokens into LUA regexp tokens.
	pattern = { self:ConvertPattern(pattern, true) }		
	
	local n = table.getn(pattern)	
	if n > 1 then	-- Extra return values are the remapped token sequences.
		
		for j in pairs(patternInfo) do
			if type(patternInfo[j]) == "number" and patternInfo[j] < 100 then
				patternInfo[j] = pattern[patternInfo[j]+1]	-- Remap to correct token sequence.
			end
		end
		
	end	
	
	patternInfo.tc = tc
	patternInfo.pattern = pattern[1]
	
	return patternInfo
	
end

-- Fields of the patternTable.
lib.infoMap = {
	hit = { "source", "victim", "skill", "amount", "element", "isCrit", "isDOT", "isSplit" },
	heal = { "source", "victim", "skill", "amount", "isCrit", "isDOT" },
	miss = { "source", "victim", "skill", "missType" },
	death = { "victim", "source", "skill", "isItem" },
	debuff = { "victim", "skill", "amountRank" },
	buff = { "victim", "skill", "amountRank" },
	fade = { "victim", "skill" },
	cast = { "source", "skill", "victim", "isBegin", "isPerform" },	
	gain = { "source", "victim", "skill", "amount", "attribute" },
	drain = { "source", "victim", "skill", "amount", "attribute" },
	leech = { "source", "victim", "skill", "amount", "attribute", "sourceGained", "amountGained", "attributeGained" },
	dispel = { "victim", "skill", "source", "isFailed" },
	extraattack = { "victim", "skill", "amount" },
	environment = { "victim", "amount", "damageType" },
	experience = { "amount", "source", "bonusAmount", "bonusType", "penaltyAmount", "penaltyType", "amountRaidPenalty", "amountGroupBonus", "victim" },
	reputation = { "faction", "amount", "rank", "isNegative" },
	feedpet = { "victim", "item" },
	enchant = { "source", "victim", "skill", "item" },
	fail = { "source", "skill", "reason" },
	interrupt = { "source", "victim", "skill" },
	create = { "source", "item" },
	honor = { "amount", "source", "sourceRank" }, -- if amount == nil then isDishonor = true.
	durability = { "source", "skill", "victim", "item" }, -- is not item then isAllItems = true 
	unknown = { "message" },	
}

function lib:GetInfoFieldName(infoType, fieldIndex)
	if self.infoMap[infoType] then
		return self.infoMap[infoType][fieldIndex-1]	-- Skip the first field in patternTable which is 'type'.
	end
end

--------------------------------
--      Load this bitch!      --
--------------------------------
libobj:Register(lib)