--[[
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)
