code:
--[[
Name: AceDB-2.0
Revision: $Rev: 17797 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceDB-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDB-2.0
Description: Mixin to allow for fast, clean, and featureful saved variable
             access.
Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0
]]

local MAJOR_VERSION = "AceDB-2.0"
local MINOR_VERSION = "$Revision: 17797 $"

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

if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end

local ACTIVE, ENABLED, STATE, TOGGLE_ACTIVE, MAP_ACTIVESUSPENDED, SET_PROFILE, SET_PROFILE_USAGE, PROFILE, PLAYER_OF_REALM, CHOOSE_PROFILE_DESC, CHOOSE_PROFILE_GUI, COPY_PROFILE_DESC, COPY_PROFILE_GUI, OTHER_PROFILE_DESC, OTHER_PROFILE_GUI, OTHER_PROFILE_USAGE, CHARACTER, REALM, CLASS

local function safecall(func,a,b,c,d,e,f,g,h,i)
	local success, err = pcall(func,a,b,c,d,e,f,g,h,i)
	if not success then geterrorhandler()(err) end
end

if GetLocale() == "deDE" then
	ACTIVE = "Aktiv"
	ENABLED = "Aktiviert"
	STATE = "Status"
	TOGGLE_ACTIVE = "Stoppt/Aktiviert dieses Addon."
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Aktiv|r", [false] = "|cffff0000Gestoppt|r" }
	SET_PROFILE = "Setzt das Profil f\195\188r dieses Addon."
	SET_PROFILE_USAGE = "{Charakter || Klasse || Realm || <Profilname>}"
	PROFILE = "Profil"
	PLAYER_OF_REALM = "%s von %s"
	CHOOSE_PROFILE_DESC = "W\195\164hle ein Profil."
	CHOOSE_PROFILE_GUI = "W\195\164hle"
	COPY_PROFILE_DESC = "Kopiert Einstellungen von einem anderem Profil."
	COPY_PROFILE_GUI = "Kopiere von"
	OTHER_PROFILE_DESC = "W\195\164hle ein anderes Profil."
	OTHER_PROFILE_GUI = "Anderes"
	OTHER_PROFILE_USAGE = "<Profilname>"

	CHARACTER = "Charakter: "
	REALM = "Realm: "
	CLASS = "Klasse: "
elseif GetLocale() == "frFR" then
	ACTIVE = "Actif"
	ENABLED = "Activ\195\169"
	STATE = "Etat"
	TOGGLE_ACTIVE = "Suspend/active cet addon."
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Actif|r", [false] = "|cffff0000Suspendu|r" }
	SET_PROFILE = "S\195\169lectionne le profil pour cet addon."
	SET_PROFILE_USAGE = "{perso || classe || royaume || <nom de profil>}"
	PROFILE = "Profil"
	PLAYER_OF_REALM = "%s de %s"
	CHOOSE_PROFILE_DESC = "Choisissez un profil."
	CHOOSE_PROFILE_GUI = "Choix"
	COPY_PROFILE_DESC = "Copier les param\195\168tres d'un autre profil."
	COPY_PROFILE_GUI = "Copier \195\160 partir de"
	OTHER_PROFILE_DESC = "Choisissez un autre profil."
	OTHER_PROFILE_GUI = "Autre"
	OTHER_PROFILE_USAGE = "<nom de profil>"

	CHARACTER = "Personnage: "
	REALM = "Royaume: "
	CLASS = "Classe: "
elseif GetLocale() == "koKR" then
	ACTIVE = "활성화"
	ENABLED = "활성화"
	STATE = "상태"
	TOGGLE_ACTIVE = "이 애드온 중지/계속 실행"
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00활성화|r", [false] = "|cffff0000중지됨|r" }
	SET_PROFILE = "이 애드온에 프로필 설정"
	SET_PROFILE_USAGE = "{캐릭터명 || 직업 || 서버명 || <프로필명>}"
	PROFILE = "프로필"
	PLAYER_OF_REALM = "%s (%s 서버)"
	CHOOSE_PROFILE_DESC = "프로파일을 선택합니다."
	CHOOSE_PROFILE_GUI = "선택"
	COPY_PROFILE_DESC = "다른 프로파일에서 설정을 복사합니다."
	COPY_PROFILE_GUI = "복사"
	OTHER_PROFILE_DESC = "다른 프로파일을 선택합니다."
	OTHER_PROFILE_GUI = "기타"
	OTHER_PROFILE_USAGE = "<프로파일명>"

	CHARACTER = "캐릭터: "
	REALM = "서버: "
	CLASS = "직업: "
elseif GetLocale() == "zhTW" then
	ACTIVE = "啟動"
	ENABLED = "啟用"
	STATE = "狀態"
	TOGGLE_ACTIVE = "暫停/重啟這個插件。"
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00啟動|r", [false] = "|cffff0000已暫停|r" }
	SET_PROFILE = "設定這插件的記錄檔。"
	SET_PROFILE_USAGE = "{角色 || 聯業 || 伺服器 || <記錄檔名稱>}"
	PROFILE = "記錄檔"
	PLAYER_OF_REALM = "%s 於 %s"
	CHOOSE_PROFILE_DESC = "選擇一個記錄檔"
	CHOOSE_PROFILE_GUI = "選擇"
	COPY_PROFILE_DESC = "由其他記錄檔複製設定。"
	COPY_PROFILE_GUI = "複製由"
	OTHER_PROFILE_DESC = "選擇其他記錄檔。"
	OTHER_PROFILE_GUI = "其他"
	OTHER_PROFILE_USAGE = "<記錄檔名稱>"

	CHARACTER = "角色："
	REALM = "伺服器："
	CLASS = "聯業："
elseif GetLocale() == "zhCN" then
	ACTIVE = "\230\156\137\230\149\136"
	ENABLED = "\229\144\175\231\148\168"
	STATE = "\231\138\182\230\128\129"
	TOGGLE_ACTIVE = "\230\154\130\229\129\156/\230\129\162\229\164\141 \230\173\164\230\143\146\228\187\182."
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00\230\156\137\230\149\136|r", [false] = "|cffff0000\230\154\130\229\129\156|r" }
	SET_PROFILE = "\232\174\190\231\189\174\233\133\141\231\189\174\230\150\135\228\187\182\228\184\186\232\191\153\230\143\146\228\187\182."
	SET_PROFILE_USAGE = "{\229\173\151\231\172\166 || \233\128\137\228\187\182\231\177\187 || \229\159\159 || <\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>}"
	PROFILE = "\233\133\141\231\189\174\230\150\135\228\187\182"
	PLAYER_OF_REALM = "%s \231\154\132 %s"
	CHOOSE_PROFILE_DESC = "\233\128\137\230\139\169\233\133\141\231\189\174\230\150\135\228\187\182."
	CHOOSE_PROFILE_GUI = "\233\128\137\230\139\169"
	COPY_PROFILE_DESC = "\229\164\141\229\136\182\232\174\190\231\189\174\228\187\142\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182."
	COPY_PROFILE_GUI = "\229\164\141\229\136\182\228\187\142"
	OTHER_PROFILE_DESC = "\233\128\137\230\139\169\229\143\166\228\184\128\228\184\170\233\133\141\231\189\174\230\150\135\228\187\182."
	OTHER_PROFILE_GUI = "\229\133\182\228\187\150"
	OTHER_PROFILE_USAGE = "<\233\133\141\231\189\174\230\150\135\228\187\182\229\144\141\229\173\151>"

	CHARACTER = "\229\173\151\231\172\166: "
	REALM = "\229\159\159: "
	CLASS = "\233\128\137\228\187\182\231\177\187: "
else -- enUS
	ACTIVE = "Active"
	ENABLED = "Enabled"
	STATE = "State"
	TOGGLE_ACTIVE = "Suspend/resume this addon."
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Active|r", [false] = "|cffff0000Suspended|r" }
	SET_PROFILE = "Set profile for this addon."
	SET_PROFILE_USAGE = "{char || class || realm || <profile name>}"
	PROFILE = "Profile"
	PLAYER_OF_REALM = "%s of %s"
	CHOOSE_PROFILE_DESC = "Choose a profile."
	CHOOSE_PROFILE_GUI = "Choose"
	COPY_PROFILE_DESC = "Copy settings from another profile."
	COPY_PROFILE_GUI = "Copy from"
	OTHER_PROFILE_DESC = "Choose another profile."
	OTHER_PROFILE_GUI = "Other"
	OTHER_PROFILE_USAGE = "<profile name>"

	CHARACTER = "Character: "
	REALM = "Realm: "
	CLASS = "Class: "
end

local AceOO = AceLibrary("AceOO-2.0")
local AceEvent
local Mixin = AceOO.Mixin
local AceDB = Mixin {
						"RegisterDB",
						"RegisterDefaults",
						"ResetDB",
						"SetProfile",
						"GetProfile",
						"ToggleActive",
						"IsActive",
						"AcquireDBNamespace",
					}
local Dewdrop = AceLibrary:HasInstance("Dewdrop-2.0") and AceLibrary("Dewdrop-2.0")

local _G = getfenv(0)

local table_setn
do
	local version = GetBuildInfo()
	if string.find(version, "^2%.") then
		-- 2.0.0
		table_setn = function() end
	else
		table_setn = table.setn
	end
end

local function inheritDefaults(t, defaults)
	if not defaults then
		return t
	end
	for k,v in pairs(defaults) do
		if k == "*" then
			local v = v
			if type(v) == "table" then
				setmetatable(t, {
					__index = function(self, key)
						if key == nil then
							return nil
						end
						self[key] = {}
						inheritDefaults(self[key], v)
						return self[key]
					end
				} )
			else
				setmetatable(t, {
					__index = function(self, key)
						if key == nil then
							return nil
						end
						self[key] = v
						return self[key]
					end
				} )
			end
			for key in pairs(t) do
				if (defaults[key] == nil or key == "*") and type(t[key]) == "table" then
					inheritDefaults(t[key], v)
				end
			end
		else
			if type(v) == "table" then
				if type(t[k]) ~= "table" then
					t[k] = {}
				end
				inheritDefaults(t[k], v)
			elseif t[k] == nil then
				t[k] = v
			end
		end
	end
	return t
end

local _,race = UnitRace("player")
local faction
if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" then
	faction = FACTION_HORDE
else
	faction = FACTION_ALLIANCE
end
local charID = string.format(PLAYER_OF_REALM, UnitName("player"), (string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1")))
local realm = string.gsub(GetRealmName(), "^%s*(.-)%s*$", "%1")
local realmID = realm .. " - " .. faction
local classID = UnitClass("player")

AceDB.CHAR_ID = charID
AceDB.REALM_ID = realmID
AceDB.CLASS_ID = classID

AceDB.FACTION = faction
AceDB.REALM = realm
AceDB.NAME = UnitName("player")

local new, del
do
	local list = setmetatable({}, {__mode="k"})
	function new()
		local t = next(list)
		if t then
			list[t] = nil
			return t
		else
			return {}
		end
	end

	function del(t)
		setmetatable(t, nil)
		for k in pairs(t) do
			t[k] = nil
		end
		table_setn(t, 0)
		list[t] = true
	end
end

local caseInsensitive_mt = {
	__index = function(self, key)
		if type(key) ~= "string" then
			return nil
		end
		local lowerKey = string.lower(key)
		for k,v in pairs(self) do
			if string.lower(k) == lowerKey then
				return self[k]
			end
		end
	end,
	__newindex = function(self, key, value)
		if type(key) ~= "string" then
			return error("table index is nil", 2)
		end
		local lowerKey = string.lower(key)
		for k in pairs(self) do
			if string.lower(k) == lowerKey then
				rawset(self, k, nil)
				rawset(self, key, value)
				return
			end
		end
		rawset(self, key, value)
	end
}

local db_mt = { __index = function(db, key)
	if key == "char" then
		if db.charName then
			if type(_G[db.charName]) ~= "table" then
				_G[db.charName] = {}
			end
			if type(_G[db.charName].global) ~= "table" then
				_G[db.charName].global = {}
			end
			rawset(db, 'char', _G[db.charName].global)
		else
			if type(db.raw.chars) ~= "table" then
				db.raw.chars = {}
			end
			local id = charID
			if type(db.raw.chars[id]) ~= "table" then
				db.raw.chars[id] = {}
			end
			rawset(db, 'char', db.raw.chars[id])
		end
		if db.defaults and db.defaults.char then
			inheritDefaults(db.char, db.defaults.char)
		end
		return db.char
	elseif key == "realm" then
		if type(db.raw.realms) ~= "table" then
			db.raw.realms = {}
		end
		local id = realmID
		if type(db.raw.realms[id]) ~= "table" then
			db.raw.realms[id] = {}
		end
		rawset(db, 'realm', db.raw.realms[id])
		if db.defaults and db.defaults.realm then
			inheritDefaults(db.realm, db.defaults.realm)
		end
		return db.realm
	elseif key == "account" then
		if type(db.raw.account) ~= "table" then
			db.raw.account = {}
		end
		rawset(db, 'account', db.raw.account)
		if db.defaults and db.defaults.account then
			inheritDefaults(db.account, db.defaults.account)
		end
		return db.account
	elseif key == "class" then
		if type(db.raw.classes) ~= "table" then
			db.raw.classes = {}
		end
		local id = classID
		if type(db.raw.classes[id]) ~= "table" then
			db.raw.classes[id] = {}
		end
		rawset(db, 'class', db.raw.classes[id])
		if db.defaults and db.defaults.class then
			inheritDefaults(db.class, db.defaults.class)
		end
		return db.class
	elseif key == "profile" then
		if type(db.raw.profiles) ~= "table" then
			db.raw.profiles = setmetatable({}, caseInsensitive_mt)
		else
			setmetatable(db.raw.profiles, caseInsensitive_mt)
		end
		local id = db.raw.currentProfile[charID]
		if id == "char" then
			id = "char/" .. charID
		elseif id == "class" then
			id = "class/" .. classID
		elseif id == "realm" then
			id = "realm/" .. realmID
		end
		if type(db.raw.profiles[id]) ~= "table" then
			db.raw.profiles[id] = {}
		end
		rawset(db, 'profile', db.raw.profiles[id])
		if db.defaults and db.defaults.profile then
			inheritDefaults(db.profile, db.defaults.profile)
		end
		return db.profile
	elseif key == "raw" or key == "defaults" or key == "name" or key == "charName" or key == "namespaces" then
		return nil
	end
	error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
	error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
end }

local CrawlForSerialization
local CrawlForDeserialization

local function SerializeObject(o)
	local t = { o:Serialize() }
	t[0] = o.class:GetLibraryVersion()
	CrawlForSerialization(t)
	return t
end

local function DeserializeObject(t)
	CrawlForDeserialization(t)
	local className = t[0]
	for i = 20, 1, -1 do
		if t[i] then
			table_setn(t, i)
			break
		end
	end
	local o = AceLibrary(className):Deserialize(unpack(t))
	table_setn(t, 0)
	return o
end

local function IsSerializable(t)
	return AceOO.inherits(t, AceOO.Class) and t.class and type(t.class.Deserialize) == "function" and type(t.Serialize) == "function" and type(t.class.GetLibraryVersion) == "function"
end

function CrawlForSerialization(t)
	local tmp = new()
	for k,v in pairs(t) do
		tmp[k] = v
	end
	for k,v in pairs(tmp) do
		if type(v) == "table" and type(v[0]) ~= "userdata" then
			if IsSerializable(v) then
				v = SerializeObject(v)
				t[k] = v
			else
				CrawlForSerialization(v)
			end
		end
		if type(k) == "table" and type(k[0]) ~= "userdata" then
			if IsSerializable(k) then
				t[k] = nil
				t[SerializeObject(k)] = v
			else
				CrawlForSerialization(k)
			end
		end
		tmp[k] = nil
		k = nil
	end
	tmp = del(tmp)
end

local function IsDeserializable(t)
	return type(t[0]) == "string" and AceLibrary:HasInstance(t[0])
end

function CrawlForDeserialization(t)
	local tmp = new()
	for k,v in pairs(t) do
		tmp[k] = v
	end
	for k,v in pairs(tmp) do
		if type(v) == "table" then
			if IsDeserializable(v) then
				t[k] = DeserializeObject(v)
				del(v)
				v = t[k]
			elseif type(v[0]) ~= "userdata" then
				CrawlForDeserialization(v)
			end
		end
		if type(k) == "table" then
			if IsDeserializable(k) then
				t[k] = nil
				t[DeserializeObject(k)] = v
				del(k)
			elseif type(k[0]) ~= "userdata" then
				CrawlForDeserialization(k)
			end
		end
		tmp[k] = nil
		k = nil
	end
	tmp = del(tmp)
end

local namespace_mt = { __index = function(namespace, key)
	local db = namespace.db
	local name = namespace.name
	if key == "char" then
		if db.charName then
			if type(_G[db.charName]) ~= "table" then
				_G[db.charName] = {}
			end
			if type(_G[db.charName].namespaces) ~= "table" then
				_G[db.charName].namespaces = {}
			end
			if type(_G[db.charName].namespaces[name]) ~= "table" then
				_G[db.charName].namespaces[name] = {}
			end
			rawset(namespace, 'char', _G[db.charName].namespaces[name])
		else
			if type(db.raw.namespaces) ~= "table" then
				db.raw.namespaces = {}
			end
			if type(db.raw.namespaces[name]) ~= "table" then
				db.raw.namespaces[name] = {}
			end
			if type(db.raw.namespaces[name].chars) ~= "table" then
				db.raw.namespaces[name].chars = {}
			end
			local id = charID
			if type(db.raw.namespaces[name].chars[id]) ~= "table" then
				db.raw.namespaces[name].chars[id] = {}
			end
			rawset(namespace, 'char', db.raw.namespaces[name].chars[id])
		end
		if namespace.defaults and namespace.defaults.char then
			inheritDefaults(namespace.char, namespace.defaults.char)
		end
		return namespace.char
	elseif key == "realm" then
		if type(db.raw.namespaces) ~= "table" then
			db.raw.namespaces = {}
		end
		if type(db.raw.namespaces[name]) ~= "table" then
			db.raw.namespaces[name] = {}
		end
		if type(db.raw.namespaces[name].realms) ~= "table" then
			db.raw.namespaces[name].realms = {}
		end
		local id = realmID
		if type(db.raw.namespaces[name].realms[id]) ~= "table" then
			db.raw.namespaces[name].realms[id] = {}
		end
		rawset(namespace, 'realm', db.raw.namespaces[name].realms[id])
		if namespace.defaults and namespace.defaults.realm then
			inheritDefaults(namespace.realm, namespace.defaults.realm)
		end
		return namespace.realm
	elseif key == "account" then
		if type(db.raw.namespaces) ~= "table" then
			db.raw.namespaces = {}
		end
		if type(db.raw.namespaces[name]) ~= "table" then
			db.raw.namespaces[name] = {}
		end
		if type(db.raw.namespaces[name].account) ~= "table" then
			db.raw.namespaces[name].account = {}
		end
		rawset(namespace, 'account', db.raw.namespaces[name].account)
		if namespace.defaults and namespace.defaults.account then
			inheritDefaults(namespace.account, namespace.defaults.account)
		end
		return namespace.account
	elseif key == "class" then
		if type(db.raw.namespaces) ~= "table" then
			db.raw.namespaces = {}
		end
		if type(db.raw.namespaces[name]) ~= "table" then
			db.raw.namespaces[name] = {}
		end
		if type(db.raw.namespaces[name].classes) ~= "table" then
			db.raw.namespaces[name].classes = {}
		end
		local id = classID
		if type(db.raw.namespaces[name].classes[id]) ~= "table" then
			db.raw.namespaces[name].classes[id] = {}
		end
		rawset(namespace, 'class', db.raw.namespaces[name].classes[id])
		if namespace.defaults and namespace.defaults.class then
			inheritDefaults(namespace.class, namespace.defaults.class)
		end
		return namespace.class
	elseif key == "profile" then
		if type(db.raw.namespaces) ~= "table" then
			db.raw.namespaces = {}
		end
		if type(db.raw.namespaces[name]) ~= "table" then
			db.raw.namespaces[name] = {}
		end
		if type(db.raw.namespaces[name].profiles) ~= "table" then
			db.raw.namespaces[name].profiles = setmetatable({}, caseInsensitive_mt)
		else
			setmetatable(db.raw.namespaces[name].profiles, caseInsensitive_mt)
		end
		local id = db.raw.currentProfile[charID]
		if id == "char" then
			id = "char/" .. charID
		elseif id == "class" then
			id = "class/" .. classID
		elseif id == "realm" then
			id = "realm/" .. realmID
		end
		if type(db.raw.namespaces[name].profiles[id]) ~= "table" then
			db.raw.namespaces[name].profiles[id] = {}
		end
		rawset(namespace, 'profile', db.raw.namespaces[name].profiles[id])
		if namespace.defaults and namespace.defaults.profile then
			inheritDefaults(namespace.profile, namespace.defaults.profile)
		end
		return namespace.profile
	elseif key == "defaults" or key == "name" or key == "db" then
		return nil
	end
	error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
	error(string.format('Cannot access key %q in db table. You may want to use db.profile[%q]', tostring(key), tostring(key)), 2)
end }

function AceDB:InitializeDB(addonName)
	local db = self.db

	if not db then
		if addonName then
			AceDB.addonsLoaded[addonName] = true
		end
		return
	end

	if db.raw then
		-- someone manually initialized
		return
	end

	if type(_G[db.name]) ~= "table" then
		_G[db.name] = {}
	else
		CrawlForDeserialization(_G[db.name])
	end
	if type(_G[db.charName]) == "table" then
		CrawlForDeserialization(_G[db.charName])
	end
	rawset(db, 'raw', _G[db.name])
	if not db.raw.currentProfile then
		db.raw.currentProfile = {}
	end
	if not db.raw.currentProfile[charID] then
		db.raw.currentProfile[charID] = "Default"
	end
	if db.raw.disabled then
		setmetatable(db.raw.disabled, caseInsensitive_mt)
	end
	if self['acedb-profile-copylist'] then
		local t = self['acedb-profile-copylist']
		for k,v in pairs(t) do
			t[k] = nil
		end
		if db.raw.profiles then
			for k in pairs(db.raw.profiles) do
				if string.find(k, '^char/') then
					local name = string.sub(k, 6)
					if name ~= charID then
						t[k] =  CHARACTER .. name
					end
				elseif string.find(k, '^realm/') then
					local name = string.sub(k, 7)
					if name ~= realmID then
						t[k] =  REALM .. name
					end
				elseif string.find(k, '^class/') then
					local name = string.sub(k, 7)
					if name ~= classID then
						t[k] =  CLASS .. name
					end
				end
			end
		end
	end
	if self['acedb-profile-list'] then
		local t = self['acedb-profile-list']
		for k,v in pairs(t) do
			t[k] = nil
		end
		t.char = CHARACTER .. charID
		t.realm = REALM .. realmID
		t.class = CLASS .. classID
		t.Default = "Default"
		if db.raw.profiles then
			for k in pairs(db.raw.profiles) do
				if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
					t[k] = k
				end
			end
		end
	end
	setmetatable(db, db_mt)
end

function AceDB:OnEmbedInitialize(target, name)
	if name then
		self:ADDON_LOADED(name)
	end
	self.InitializeDB(target, name)
end

function AceDB:RegisterDB(name, charName)
	AceDB:argCheck(name, 2, "string")
	AceDB:argCheck(charName, 3, "string", "nil")
	if self.db then
		AceDB:error("Cannot call \"RegisterDB\" if self.db is set.")
	end
	local stack = debugstack()
	local addonName = string.gsub(stack, ".-\n.-\\AddOns\\(.-)\\.*", "%1")
	self.db = {
		name = name,
		charName = charName
	}
	if AceDB.addonsLoaded[addonName] then
		AceDB.InitializeDB(self, addonName)
	else
		AceDB.addonsToBeInitialized[self] = addonName
	end
	AceDB.registry[self] = true
end

function AceDB:RegisterDefaults(kind, defaults, a3)
	local name
	if a3 then
		name, kind, defaults = kind, defaults, a3
		AceDB:argCheck(name, 2, "string")
		AceDB:argCheck(kind, 3, "string")
		AceDB:argCheck(defaults, 4, "table")
		if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
			AceDB:error("Bad argument #3 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
		end
	else
		AceDB:argCheck(kind, 2, "string")
		AceDB:argCheck(defaults, 3, "table")
		if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" then
			AceDB:error("Bad argument #2 to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", or \"realm\" expected, got %q)", kind)
		end
	end
	if type(self.db) ~= "table" or type(self.db.name) ~= "string" then
		AceDB:error("Cannot call \"RegisterDefaults\" unless \"RegisterDB\" has been previously called.")
	end
	local db
	if name then
		local namespace = self:AcquireDBNamespace(name)
		if namespace.defaults and namespace.defaults[kind] then
			AceDB:error("\"RegisterDefaults\" has already been called for %q::%q.", name, kind)
		end
		db = namespace
	else
		if self.db.defaults and self.db.defaults[kind] then
			AceDB:error("\"RegisterDefaults\" has already been called for %q.", kind)
		end
		db = self.db
	end
	if not db.defaults then
		rawset(db, 'defaults', {})
	end
	db.defaults[kind] = defaults
	if rawget(db, kind) then
		inheritDefaults(db[kind], defaults)
	end
end

function AceDB:ResetDB(kind)
	AceDB:argCheck(kind, 2, "nil", "string")
	if not self.db or not self.db.raw then
		AceDB:error("Cannot call \"ResetDB\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
	end
	local db = self.db
	if kind == nil then
		if db.charName then
			_G[db.charName] = nil
		end
		_G[db.name] = nil
		rawset(db, 'raw', nil)
		AceDB.InitializeDB(self)
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'account', nil)
				rawset(v, 'char', nil)
				rawset(v, 'class', nil)
				rawset(v, 'profile', nil)
				rawset(v, 'realm', nil)
			end
		end
	elseif kind == "account" then
		db.raw.account = nil
		rawset(db, 'account', nil)
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'account', nil)
			end
		end
	elseif kind == "char" then
		if db.charName then
			_G[db.charName] = nil
		else
			if db.raw.chars then
				db.raw.chars[charID] = nil
			end
			if db.raw.namespaces then
				for name,v in pairs(db.raw.namespaces) do
					if v.chars then
						v.chars[charID] = nil
					end
				end
			end
		end
		rawset(db, 'char', nil)
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'char', nil)
			end
		end
	elseif kind == "realm" then
		if db.raw.realms then
			db.raw.realms[realmID] = nil
		end
		rawset(db, 'realm', nil)
		if db.raw.namespaces then
			for name,v in pairs(db.raw.namespaces) do
				if v.realms then
					v.realms[realmID] = nil
				end
			end
		end
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'realm', nil)
			end
		end
	elseif kind == "class" then
		if db.raw.realms then
			db.raw.realms[classID] = nil
		end
		rawset(db, 'class', nil)
		if db.raw.namespaces then
			for name,v in pairs(db.raw.namespaces) do
				if v.classes then
					v.classes[classID] = nil
				end
			end
		end
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'class', nil)
			end
		end
	elseif kind == "profile" then
		local id = db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"
		if id == "char" then
			id = "char/" .. charID
		elseif id == "class" then
			id = "class/" .. classID
		elseif id == "realm" then
			id = "realm/" .. realmID
		end
		if db.raw.profiles then
			db.raw.profiles[id] = nil
		end
		rawset(db, 'profile', nil)
		if db.raw.namespaces then
			for name,v in pairs(db.raw.namespaces) do
				if v.profiles then
					v.profiles[id] = nil
				end
			end
		end
		if db.namespaces then
			for name,v in pairs(db.namespaces) do
				rawset(v, 'profile', nil)
			end
		end
	end
end

local function cleanDefaults(t, defaults)
	if defaults then
		for k,v in pairs(defaults) do
			if k == "*" then
				if type(v) == "table" then
					for k in pairs(t) do
						if (defaults[k] == nil or k == "*") and type(t[k]) == "table" then
							if cleanDefaults(t[k], v) then
								t[k] = nil
							end
						end
					end
				else
					for k in pairs(t) do
						if (defaults[k] == nil or k == "*") and t[k] == v then
							t[k] = nil
						end
					end
				end
			else
				if type(v) == "table" then
					if type(t[k]) == "table" then
						if cleanDefaults(t[k], v) then
							t[k] = nil
						end
					end
				elseif t[k] == v then
					t[k] = nil
				end
			end
		end
	end
	return t and not next(t)
end

function AceDB:GetProfile()
	if not self.db or not self.db.raw then
		return nil
	end
	if not self.db.raw.currentProfile then
		self.db.raw.currentProfile = {}
	end
	if not self.db.raw.currentProfile[charID] then
		self.db.raw.currentProfile[charID] = "Default"
	end
	local profile = self.db.raw.currentProfile[charID]
	if profile == "char" then
		return "char", "char/" .. charID
	elseif profile == "class" then
		return "class", "class/" .. classID
	elseif profile == "realm" then
		return "realm", "realm/" .. realmID
	end
	return profile, profile
end

local function copyTable(to, from)
	setmetatable(to, nil)
	for k,v in pairs(from) do
		if type(k) == "table" then
			k = copyTable({}, k)
		end
		if type(v) == "table" then
			v = copyTable({}, v)
		end
		to[k] = v
	end
	table_setn(to, table.getn(from))
	setmetatable(to, from)
	return to
end

function AceDB:SetProfile(name, copyFrom)
	AceDB:argCheck(name, 2, "string")
	AceDB:argCheck(copyFrom, 3, "string", "nil")
	if not self.db or not self.db.raw then
		AceDB:error("Cannot call \"SetProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
	end
	local db = self.db
	local copy = false
	local lowerName = string.lower(name)
	local lowerCopyFrom = copyFrom and string.lower(copyFrom)
	if string.sub(lowerName, 1, 5) == "char/" or string.sub(lowerName, 1, 6) == "realm/" or string.sub(lowerName, 1, 6) == "class/" then
		if string.sub(lowerName, 1, 5) == "char/" then
			name, copyFrom = "char", name
		else
			name, copyFrom = string.sub(lowerName, 1, 5), name
		end
		lowerName = string.lower(name)
		lowerCopyFrom = string.lower(copyFrom)
	end
	if copyFrom then
		if string.sub(lowerCopyFrom, 1, 5) == "char/" then
			AceDB:assert(lowerName == "char", "If argument #3 starts with `char/', argument #2 must be `char'")
		elseif string.sub(lowerCopyFrom, 1, 6) == "realm/" then
			AceDB:assert(lowerName == "realm", "If argument #3 starts with `realm/', argument #2 must be `realm'")
		elseif string.sub(lowerCopyFrom, 1, 6) == "class/" then
			AceDB:assert(lowerName == "class", "If argument #3 starts with `class/', argument #2 must be `class'")
		else
			AceDB:assert(lowerName ~= "char" and lowerName ~= "realm" and lowerName ~= "class", "If argument #3 does not start with a special prefix, that prefix cannot be copied to.")
		end
		if not db.raw.profiles or not db.raw.profiles[copyFrom] then
			AceDB:error("Cannot copy profile %q, it does not exist.", copyFrom)
		elseif (string.sub(lowerName, 1, 5) == "char/" and string.sub(lowerName, 6) == string.lower(charID)) or (string.sub(lowerName, 1, 6) == "realm/" and string.sub(lowerName, 7) == string.lower(realmID)) or (string.sub(lowerName, 1, 6) == "class/" and string.sub(lowerName, 7) == string.lower(classID)) then
			AceDB:error("Cannot copy profile %q, it is currently in use.", name)
		end
	end
	local oldName = db.raw.currentProfile[charID]
	if string.lower(oldName) == string.lower(name) then
		return
	end
	local oldProfileData = db.profile
	local realName = name
	if lowerName == "char" then
		realName = name .. "/" .. charID
	elseif lowerName == "realm" then
		realName = name .. "/" .. realmID
	elseif lowerName == "class" then
		realName = name .. "/" .. classID
	end
	local current = self.class
	while current and current ~= AceOO.Class do
		if current.mixins then
			for mixin in pairs(current.mixins) do
				if type(mixin.OnEmbedProfileDisable) == "function" then
					safecall(mixin.OnEmbedProfileDisable, mixin, self, realName)
				end
			end
		end
		current = current.super
	end
	if type(self.OnProfileDisable) == "function" then
		safecall(self.OnProfileDisable, self, realName)
	end
	local active = self:IsActive()
	db.raw.currentProfile[charID] = name
	rawset(db, 'profile', nil)
	if db.namespaces then
		for k,v in pairs(db.namespaces) do
			rawset(v, 'profile', nil)
		end
	end
	if copyFrom then
		for k,v in pairs(db.profile) do
			db.profile[k] = nil
		end
		copyTable(db.profile, db.raw.profiles[copyFrom])
		inheritDefaults(db.profile, db.defaults and db.defaults.profile)
		if db.namespaces then
			for l,u in pairs(db.namespaces) do
				for k,v in pairs(u.profile) do
					u.profile[k] = nil
				end
				copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom])
				inheritDefaults(u.profile, u.defaults and u.defaults.profile)
			end
		end
	end
	local current = self.class
	while current and current ~= AceOO.Class do
		if current.mixins then
			for mixin in pairs(current.mixins) do
				if type(mixin.OnEmbedProfileEnable) == "function" then
					safecall(mixin.OnEmbedProfileEnable, mixin, self, oldName, oldProfileData, copyFrom)
				end
			end
		end
		current = current.super
	end
	if type(self.OnProfileEnable) == "function" then
		safecall(self.OnProfileEnable, self, oldName, oldProfileData, copyFrom)
	end
	if cleanDefaults(oldProfileData, db.defaults and db.defaults.profile) then
		db.raw.profiles[oldName] = nil
		if not next(db.raw.profiles) then
			db.raw.profiles = nil
		end
	end
	local newactive = self:IsActive()
	if active ~= newactive then
		if AceOO.inherits(self, "AceAddon-2.0") then
			local AceAddon = AceLibrary("AceAddon-2.0")
			if not AceAddon.addonsStarted[self] then
				return
			end
		end
		if newactive then
			local current = self.class
			while current and current ~= AceOO.Class do
				if current.mixins then
					for mixin in pairs(current.mixins) do
						if type(mixin.OnEmbedEnable) == "function" then
							safecall(mixin.OnEmbedEnable, mixin, self)
						end
					end
				end
				current = current.super
			end
			if type(self.OnEnable) == "function" then
				safecall(self.OnEnable, self)
			end
			if AceEvent then
				AceEvent:TriggerEvent("Ace2_AddonEnabled", self)
			end
		else
			local current = self.class
			while current and current ~= AceOO.Class do
				if current.mixins then
					for mixin in pairs(current.mixins) do
						if type(mixin.OnEmbedDisable) == "function" then
							safecall(mixin.OnEmbedDisable, mixin, self)
						end
					end
				end
				current = current.super
			end
			if type(self.OnDisable) == "function" then
				safecall(self.OnDisable, self)
			end
			if AceEvent then
				AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
			end
		end
	end
	if self['acedb-profile-list'] then
		if not self['acedb-profile-list'][name] then
			self['acedb-profile-list'][name] = name
		end
	end
	if Dewdrop then
		Dewdrop:Refresh(1)
		Dewdrop:Refresh(2)
		Dewdrop:Refresh(3)
		Dewdrop:Refresh(4)
		Dewdrop:Refresh(5)
	end
end

function AceDB:IsActive()
	return not self.db or not self.db.raw or not self.db.raw.disabled or not self.db.raw.disabled[self.db.raw.currentProfile[charID]]
end

function AceDB:ToggleActive(state)
	AceDB:argCheck(state, 2, "boolean", "nil")
	if not self.db or not self.db.raw then
		AceDB:error("Cannot call \"ToggleActive\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
	end
	local db = self.db
	if not db.raw.disabled then
		db.raw.disabled = setmetatable({}, caseInsensitive_mt)
	end
	local profile = db.raw.currentProfile[charID]
	local disable
	if state == nil then
		disable = not db.raw.disabled[profile]
	else
		disable = not state
		if disable == db.raw.disabled[profile] then
			return
		end
	end
	db.raw.disabled[profile] = disable or nil
	if AceOO.inherits(self, "AceAddon-2.0") then
		local AceAddon = AceLibrary("AceAddon-2.0")
		if not AceAddon.addonsStarted[self] then
			return
		end
	end
	if not disable then
		local current = self.class
		while current and current ~= AceOO.Class do
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if type(mixin.OnEmbedEnable) == "function" then
						safecall(mixin.OnEmbedEnable, mixin, self)
					end
				end
			end
			current = current.super
		end
		if type(self.OnEnable) == "function" then
			safecall(self.OnEnable, self)
		end
		if AceEvent then
			AceEvent:TriggerEvent("Ace2_AddonEnabled", self)
		end
	else
		local current = self.class
		while current and current ~= AceOO.Class do
			if current.mixins then
				for mixin in pairs(current.mixins) do
					if type(mixin.OnEmbedDisable) == "function" then
						safecall(mixin.OnEmbedDisable, mixin, self)
					end
				end
			end
			current = current.super
		end
		if type(self.OnDisable) == "function" then
			safecall(self.OnDisable, self)
		end
		if AceEvent then
			AceEvent:TriggerEvent("Ace2_AddonDisabled", self)
		end
	end
	return not disable
end

function AceDB:embed(target)
	self.super.embed(self, target)
	if not AceEvent then
		AceDB:error(MAJOR_VERSION .. " requires AceEvent-2.0")
	end
end

function AceDB:ADDON_LOADED(name)
	AceDB.addonsLoaded[name] = true
	for addon, addonName in pairs(AceDB.addonsToBeInitialized) do
		if name == addonName then
			AceDB.InitializeDB(addon, name)
			AceDB.addonsToBeInitialized[addon] = nil
		end
	end
end

function AceDB:PLAYER_LOGOUT()
	for addon in pairs(AceDB.registry) do
		local db = addon.db
		if db then
			setmetatable(db, nil)
			CrawlForSerialization(db.raw)
			if type(_G[db.charName]) == "table" then
				CrawlForSerialization(_G[db.charName])
			end
			if db.char and cleanDefaults(db.char, db.defaults and db.defaults.char) then
				if db.charName and _G[db.charName] and _G[db.charName].global == db.char then
					_G[db.charName].global = nil
					if not next(_G[db.charName]) then
						_G[db.charName] = nil
					end
				else
					if db.raw.chars then
						db.raw.chars[charID] = nil
						if not next(db.raw.chars) then
							db.raw.chars = nil
						end
					end
				end
			end
			if db.realm and cleanDefaults(db.realm, db.defaults and db.defaults.realm) then
				if db.raw.realms then
					db.raw.realms[realmID] = nil
					if not next(db.raw.realms) then
						db.raw.realms = nil
					end
				end
			end
			if db.class and cleanDefaults(db.class, db.defaults and db.defaults.class) then
				if db.raw.classes then
					db.raw.classes[classID] = nil
					if not next(db.raw.classes) then
						db.raw.classes = nil
					end
				end
			end
			if db.account and cleanDefaults(db.account, db.defaults and db.defaults.account) then
				db.raw.account = nil
			end
			if db.profile and cleanDefaults(db.profile, db.defaults and db.defaults.profile) then
				if db.raw.profiles then
					db.raw.profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil
					if not next(db.raw.profiles) then
						db.raw.profiles = nil
					end
				end
			end
			if db.namespaces and db.raw.namespaces then
				for name,v in pairs(db.namespaces) do
					if db.raw.namespaces[name] then
						setmetatable(v, nil)
						if v.char and cleanDefaults(v.char, v.defaults and v.defaults.char) then
							if db.charName and _G[db.charName] and _G[db.charName].namespaces and _G[db.charName].namespaces[name] == v then
								_G[db.charName].namespaces[name] = nil
								if not next(_G[db.charName].namespaces) then
									_G[db.charName].namespaces = nil
									if not next(_G[db.charName]) then
										_G[db.charName] = nil
									end
								end
							else
								if db.raw.namespaces[name].chars then
									db.raw.namespaces[name].chars[charID] = nil
									if not next(db.raw.namespaces[name].chars) then
										db.raw.namespaces[name].chars = nil
									end
								end
							end
						end
						if v.realm and cleanDefaults(v.realm, v.defaults and v.defaults.realm) then
							if db.raw.namespaces[name].realms then
								db.raw.namespaces[name].realms[realmID] = nil
								if not next(db.raw.namespaces[name].realms) then
									db.raw.namespaces[name].realms = nil
								end
							end
						end
						if v.class and cleanDefaults(v.class, v.defaults and v.defaults.class) then
							if db.raw.namespaces[name].classes then
								db.raw.namespaces[name].classes[classID] = nil
								if not next(db.raw.namespaces[name].classes) then
									db.raw.namespaces[name].classes = nil
								end
							end
						end
						if v.account and cleanDefaults(v.account, v.defaults and v.defaults.account) then
							db.raw.namespaces[name].account = nil
						end
						if v.profile and cleanDefaults(v.profile, v.defaults and v.defaults.profile) then
							if db.raw.namespaces[name].profiles then
								db.raw.namespaces[name].profiles[db.raw.currentProfile and db.raw.currentProfile[charID] or "Default"] = nil
								if not next(db.raw.namespaces[name].profiles) then
									db.raw.namespaces[name].profiles = nil
								end
							end
						end
						if not next(db.raw.namespaces[name]) then
							db.raw.namespaces[name] = nil
						end
					end
				end
				if not next(db.raw.namespaces) then
					db.raw.namespaces = nil
				end
			end
			if db.raw.disabled and not next(db.raw.disabled) then
				db.raw.disabled = nil
			end
			if db.raw.currentProfile then
				for k,v in pairs(db.raw.currentProfile) do
					if string.lower(v) == "default" then
						db.raw.currentProfile[k] = nil
					end
				end
				if not next(db.raw.currentProfile) then
					db.raw.currentProfile = nil
				end
			end
			if _G[db.name] and not next(_G[db.name]) then
				_G[db.name] = nil
			end
		end
	end
end

function AceDB:AcquireDBNamespace(name)
	AceDB:argCheck(name, 2, "string")
	local db = self.db
	if not db then
		AceDB:error("Cannot call `AcquireDBNamespace' before `RegisterDB' has been called.", 2)
	end
	if not db.namespaces then
		rawset(db, 'namespaces', {})
	end
	if not db.namespaces[name] then
		local namespace = {}
		db.namespaces[name] = namespace
		namespace.db = db
		namespace.name = name
		setmetatable(namespace, namespace_mt)
	end
	return db.namespaces[name]
end

function AceDB:GetAceOptionsDataTable(target)
	if not target['acedb-profile-list'] then
		target['acedb-profile-list'] = setmetatable({}, caseInsensitive_mt)
		local t = target['acedb-profile-list']
		for k,v in pairs(t) do
			t[k] = nil
		end
		t.char = CHARACTER .. charID
		t.realm = REALM .. realmID
		t.class = CLASS .. classID
		t.Default = "Default"
		if target.db and target.db.raw then
			local db = target.db
			if db.raw.profiles then
				for k in pairs(db.raw.profiles) do
					if not string.find(k, '^char/') and not string.find(k, '^realm/') and not string.find(k, '^class/') then
						t[k] = k
					end
				end
			end
		end
	end
	if not target['acedb-profile-copylist'] then
		target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
		if target.db and target.db.raw then
			local t = target['acedb-profile-copylist']
			local db = target.db

			if db.raw.profiles then
				for k in pairs(db.raw.profiles) do
					if string.find(k, '^char/') then
						local name = string.sub(k, 6)
						if name ~= charID then
							t[k] =  CHARACTER .. name
						end
					elseif string.find(k, '^realm/') then
						local name = string.sub(k, 7)
						if name ~= realmID then
							t[k] =  REALM .. name
						end
					elseif string.find(k, '^class/') then
						local name = string.sub(k, 7)
						if name ~= classID then
							t[k] =  CLASS .. name
						end
					end
				end
			end
		end
	end
	return {
		standby = {
			cmdName = STATE,
			guiName = ENABLED,
			name = ACTIVE,
			desc = TOGGLE_ACTIVE,
			type = "toggle",
			get = "IsActive",
			set = "ToggleActive",
			map = MAP_ACTIVESUSPENDED,
			order = -3,
		},
		profile = {
			type = 'group',
			name = PROFILE,
			desc = SET_PROFILE,
			order = -3.5,
			get = "GetProfile",
			args = {
				choose = {
					guiName = CHOOSE_PROFILE_GUI,
					cmdName = PROFILE,
					desc = CHOOSE_PROFILE_DESC,
					type = 'text',
					get = "GetProfile",
					set = "SetProfile",
					validate = target['acedb-profile-list']
				},
				copy = {
					guiName = COPY_PROFILE_GUI,
					cmdName = PROFILE,
					desc = COPY_PROFILE_DESC,
					type = 'text',
					get = "GetProfile",
					set = "SetProfile",
					validate = target['acedb-profile-copylist'],
					disabled = function()
						return not next(target['acedb-profile-copylist'])
					end,
				},
				other = {
					guiName = OTHER_PROFILE_GUI,
					cmdName = PROFILE,
					desc = OTHER_PROFILE_DESC,
					usage = OTHER_PROFILE_USAGE,
					type = 'text',
					get = "GetProfile",
					set = "SetProfile",
				}
			}
		},
	}
end

local function activate(self, oldLib, oldDeactivate)
	AceDB = self
	AceEvent = AceLibrary:HasInstance("AceEvent-2.0") and AceLibrary("AceEvent-2.0")

	self.super.activate(self, oldLib, oldDeactivate)

	for t in pairs(self.embedList) do
		if t.db then
			rawset(t.db, 'char', nil)
			rawset(t.db, 'realm', nil)
			rawset(t.db, 'class', nil)
			rawset(t.db, 'account', nil)
			rawset(t.db, 'profile', nil)
			setmetatable(t.db, db_mt)
		end
	end

	if oldLib then
		self.addonsToBeInitialized = oldLib.addonsToBeInitialized
		self.addonsLoaded = oldLib.addonsLoaded
		self.registry = oldLib.registry
	end
	if not self.addonsToBeInitialized then
		self.addonsToBeInitialized = {}
	end
	if not self.addonsLoaded then
		self.addonsLoaded = {}
	end
	if not self.registry then
		self.registry = {}
	end

	if oldLib then
		oldDeactivate(oldLib)
	end
end

local function external(self, major, instance)
	if major == "AceEvent-2.0" then
		AceEvent = instance

		AceEvent:embed(self)

		self:RegisterEvent("ADDON_LOADED")
		self:RegisterEvent("PLAYER_LOGOUT")
	elseif major == "Dewdrop-2.0" then
		Dewdrop = instance
	end
end

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

repo_name: SweedJesus/sjLinkSpoof

license: unknown


code:
--[[
Name: AceDebug-2.0
Revision: $Rev: 17638 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceDebug-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceDebug-2.0
Description: Mixin to allow for simple debugging capabilities.
Dependencies: AceLibrary, AceOO-2.0
]]

local MAJOR_VERSION = "AceDebug-2.0"
local MINOR_VERSION = "$Revision: 17638 $"

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

if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end

local DEBUGGING, TOGGLE_DEBUGGING

if GetLocale() == "frFR" then
	DEBUGGING = "D\195\169boguage"
	TOGGLE_DEBUGGING = "Activer/d\195\169sactiver le d\195\169boguage"
elseif GetLocale() == "deDE" then
	DEBUGGING = "Debuggen"
	TOGGLE_DEBUGGING = "Aktiviert/Deaktiviert Debugging"
elseif GetLocale() == "koKR" then
	DEBUGGING = "디버깅"
	TOGGLE_DEBUGGING = "디버깅 기능 사용함/사용안함"
elseif GetLocale() == "zhTW" then
	DEBUGGING = "除錯"
	TOGGLE_DEBUGGING = "啟用/停用除錯功能"
elseif GetLocale() == "zhCN" then
	DEBUGGING = "\232\176\131\232\175\149"
	TOGGLE_DEBUGGING = "\229\144\175\231\148\168/\231\166\129\231\148\168 \232\176\131\232\175\149"
else -- enUS
	DEBUGGING = "Debugging"
	TOGGLE_DEBUGGING = "Enable/disable debugging"
end

local table_setn
do
	local version = GetBuildInfo()
	if string.find(version, "^2%.") then
		-- 2.0.0
		table_setn = function() end
	else
		table_setn = table.setn
	end
end

local math_mod = math.mod or math.fmod

local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local AceDebug = AceOO.Mixin {"Debug", "CustomDebug", "IsDebugging", "SetDebugging", "SetDebugLevel", "LevelDebug", "CustomLevelDebug", "GetDebugLevel"}

local function print(text, r, g, b, frame, delay)
	(frame or DEFAULT_CHAT_FRAME):AddMessage(text, r, g, b, 1, delay or 5)
end

local tmp

function AceDebug:CustomDebug(r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
	if not self.debugging then return end

	local output = string.format("|cff7fff7f(DEBUG) %s:[%s%3d]|r",  tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)

	if string.find(tostring(a1), "%%") then
		output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
	else
		if not tmp then
			tmp = {}
		end

		-- This block dynamically rebuilds the tmp array stopping on the first nil.
		table.insert(tmp, output)

		table.insert(tmp, tostring(a1))
		table.insert(tmp, a2)
		table.insert(tmp, a3)
		table.insert(tmp, a4)
		table.insert(tmp, a5)
		table.insert(tmp, a6)
		table.insert(tmp, a7)
		table.insert(tmp, a8)
		table.insert(tmp, a9)
		table.insert(tmp, a10)
		table.insert(tmp, a11)
		table.insert(tmp, a12)
		table.insert(tmp, a13)
		table.insert(tmp, a14)
		table.insert(tmp, a15)
		table.insert(tmp, a16)
		table.insert(tmp, a17)
		table.insert(tmp, a18)
		table.insert(tmp, a19)
		table.insert(tmp, a20)
		while tmp[table.getn(tmp)] == nil do
			table.remove(tmp)
		end
		for k = 1, table.getn(tmp) do
			tmp[k] = tostring(tmp[k])
		end

		output = table.concat(tmp, " ")

		for k,v in pairs(tmp) do
			tmp[k] = nil
		end
		table_setn(tmp, 0)
	end

	print(output, r, g, b, frame or self.debugFrame, delay)
end

function AceDebug:Debug(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
	AceDebug.CustomDebug(self, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

function AceDebug:IsDebugging()
	return self.debugging
end

function AceDebug:SetDebugging(debugging)
	self.debugging = debugging
end

-- Takes a number 1-3
-- Level 1: Critical messages that every user should receive
-- Level 2: Should be used for local debugging (function calls, etc)
-- Level 3: Very verbose debugging, will dump everything and anything
-- If set to nil, you will receive no debug information
function AceDebug:SetDebugLevel(level)
    AceDebug:argCheck(level, 1, "number", "nil")
    if not level then
        self.debuglevel = nil
        return
    end
    if level < 1 or level > 3 then
        AceDebug:error("Bad argument #1 to `SetDebugLevel`, must be a number 1-3")
    end
    self.debuglevel = level
end

function AceDebug:GetDebugLevel()
    return self.debuglevel
end

function AceDebug:CustomLevelDebug(level, r, g, b, frame, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
	if not self.debugging or not self.debuglevel then return end
    AceDebug:argCheck(level, 1, "number")
    if level < 1 or level > 3 then
        AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
    end
    if level > self.debuglevel then return end

	local output = string.format("|cff7fff7f(DEBUG) %s:[%s.%3d]|r",  tostring(self), date("%H:%M:%S"), math_mod(GetTime(), 1) * 1000)

	if string.find(tostring(a1), "%%") then
		output = output .. " " .. string.format(tostring(a1), tostring(a2), tostring(a3), tostring(a4), tostring(a5), tostring(a6), tostring(a7), tostring(a8), tostring(a9), tostring(a10), tostring(a11), tostring(a12), tostring(a13), tostring(a14), tostring(a15), tostring(a16), tostring(a17), tostring(a18), tostring(a19), tostring(a20))
	else
		if not tmp then
			tmp = {}
		end

		-- This block dynamically rebuilds the tmp array stopping on the first nil.
		table.insert(tmp, output)

		table.insert(tmp, tostring(a1))
		table.insert(tmp, a2)
		table.insert(tmp, a3)
		table.insert(tmp, a4)
		table.insert(tmp, a5)
		table.insert(tmp, a6)
		table.insert(tmp, a7)
		table.insert(tmp, a8)
		table.insert(tmp, a9)
		table.insert(tmp, a10)
		table.insert(tmp, a11)
		table.insert(tmp, a12)
		table.insert(tmp, a13)
		table.insert(tmp, a14)
		table.insert(tmp, a15)
		table.insert(tmp, a16)
		table.insert(tmp, a17)
		table.insert(tmp, a18)
		table.insert(tmp, a19)
		table.insert(tmp, a20)
		while tmp[table.getn(tmp)] == nil do
			table.remove(tmp)
		end
		for k = 1, table.getn(tmp) do
			tmp[k] = tostring(tmp[k])
		end

		output = table.concat(tmp, " ")

		for k,v in pairs(tmp) do
			tmp[k] = nil
		end
		table_setn(tmp, 0)
	end

	print(output, r, g, b, frame or self.debugFrame, delay)
end

function AceDebug:LevelDebug(level, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
	if not self.debugging or not self.debuglevel then return end
    AceDebug:argCheck(level, 1, "number")
    if level < 1 or level > 3 then
        AceDebug:error("Bad argument #1 to `LevelDebug`, must be a number 1-3")
    end
    if level > self.debuglevel then return end

	AceDebug.CustomLevelDebug(self, level, nil, nil, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

local options
function AceDebug:GetAceOptionsDataTable(target)
	if not options then
		options = {
			debug = {
				name = DEBUGGING,
				desc = TOGGLE_DEBUGGING,
				type = "toggle",
				get = "IsDebugging",
				set = "SetDebugging",
				order = -2,
			}
		}
	end
	return options
end
AceLibrary:Register(AceDebug, MAJOR_VERSION, MINOR_VERSION, AceDebug.activate)
AceDebug = AceLibrary(MAJOR_VERSION)


code:
--[[
Name: AceEvent-2.0
Revision: $Rev: 17803 $
Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
Inspired By: Ace 1.x by Turan (turan@gryphon.com)
Website: http://www.wowace.com/
Documentation: http://www.wowace.com/index.php/AceEvent-2.0
SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0
Description: Mixin to allow for event handling, scheduling, and inter-addon
communication.
Dependencies: AceLibrary, AceOO-2.0
]]

local MAJOR_VERSION = "AceEvent-2.0"
local MINOR_VERSION = "$Revision: 17803 $"

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

if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check
if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end

local AceOO = AceLibrary:GetInstance("AceOO-2.0")
local Mixin = AceOO.Mixin
local AceEvent = Mixin {
    "RegisterEvent",
    "RegisterAllEvents",
    "UnregisterEvent",
    "UnregisterAllEvents",
    "TriggerEvent",
    "ScheduleEvent",
    "ScheduleRepeatingEvent",
    "CancelScheduledEvent",
    "CancelAllScheduledEvents",
    "IsEventRegistered",
    "IsEventScheduled",
    "RegisterBucketEvent",
    "UnregisterBucketEvent",
    "UnregisterAllBucketEvents",
    "IsBucketEventRegistered",
}

local table_setn
do
    local version = GetBuildInfo()
    if string.find(version, "^2%.") then
        -- 2.0.0
        table_setn = function() end
    else
        table_setn = table.setn
    end
end

local weakKey = {__mode="k"}
local new, del
do
    local list = setmetatable({}, weakKey)
    function new()
        local t = next(list)
        if t then
            list[t] = nil
            return t
        else
            return {}
        end
    end

    function del(t)
        setmetatable(t, nil)
        for k in pairs(t) do
            t[k] = nil
        end
        list[t] = true
    end
end

local FAKE_NIL
local RATE

local eventsWhichHappenOnce = {
    PLAYER_LOGIN = true,
    AceEvent_FullyInitialized = true,
    VARIABLES_LOADED = true,
    PLAYER_LOGOUT = true,
}

local registeringFromAceEvent
function AceEvent:RegisterEvent(event, method, once)
    AceEvent:argCheck(event, 2, "string")
    if self == AceEvent and not registeringFromAceEvent then
        AceEvent:argCheck(method, 3, "function")
        self = method
    else
        AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number")
        if type(method) == "boolean" or type(method) == "number" then
            AceEvent:argCheck(once, 4, "nil")
            once, method = method, event
        end
    end
    AceEvent:argCheck(once, 4, "number", "boolean", "nil")
    if eventsWhichHappenOnce[event] then
        once = true
    end
    local throttleRate
    if type(once) == "number" then
        throttleRate, once = once
    end
    if not method then
        method = event
    end
    if type(method) == "string" and type(self[method]) ~= "function" then
        AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
    else
        assert(type(method) == "function" or type(method) == "string")
    end

    local AceEvent_registry = AceEvent.registry
    if not AceEvent_registry[event] then
        AceEvent_registry[event] = new()
        AceEvent.frame:RegisterEvent(event)
    end

    local remember = true
    if AceEvent_registry[event][self] then
        remember = false
    end
    AceEvent_registry[event][self] = method

    local AceEvent_onceRegistry = AceEvent.onceRegistry
    if once then
        if not AceEvent_onceRegistry then
            AceEvent.onceRegistry = new()
            AceEvent_onceRegistry = AceEvent.onceRegistry
        end
        if not AceEvent_onceRegistry[event] then
            AceEvent_onceRegistry[event] = new()
        end
        AceEvent_onceRegistry[event][self] = true
    else
        if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
            AceEvent_onceRegistry[event][self] = nil
            if not next(AceEvent_onceRegistry[event]) then
                AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
            end
        end
    end

    local AceEvent_throttleRegistry = AceEvent.throttleRegistry
    if throttleRate then
        if not AceEvent_throttleRegistry then
            AceEvent.throttleRegistry = new()
            AceEvent_throttleRegistry = AceEvent.throttleRegistry
        end
        if not AceEvent_throttleRegistry[event] then
            AceEvent_throttleRegistry[event] = new()
        end
        if AceEvent_throttleRegistry[event][self] then
            AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
        end
        AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey)
        local t = AceEvent_throttleRegistry[event][self]
        t[RATE] = throttleRate
    else
        if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then
            if AceEvent_throttleRegistry[event][self] then
                AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
            end
            if not next(AceEvent_throttleRegistry[event]) then
                AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
            end
        end
    end

    if remember then
        AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event)
    end
end

local ALL_EVENTS

function AceEvent:RegisterAllEvents(method)
    if self == AceEvent then
        AceEvent:argCheck(method, 1, "function")
        self = method
    else
        AceEvent:argCheck(method, 1, "string", "function")
        if type(method) == "string" and type(self[method]) ~= "function" then
            AceEvent:error("Cannot register all events to method %q, it does not exist", method)
        end
    end

    local AceEvent_registry = AceEvent.registry
    if not AceEvent_registry[ALL_EVENTS] then
        AceEvent_registry[ALL_EVENTS] = new()
        AceEvent.frame:RegisterAllEvents()
    end

    AceEvent_registry[ALL_EVENTS][self] = method
end

local _G = getfenv(0)
local memstack, timestack = {}, {}
local memdiff, timediff
function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    AceEvent:argCheck(event, 2, "string")
    local AceEvent_registry = AceEvent.registry
    if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then
        return
    end
    local _G_event = _G.event
    _G.event = event
    local lastEvent = AceEvent.currentEvent
    AceEvent.currentEvent = event

    local AceEvent_onceRegistry = AceEvent.onceRegistry
    local AceEvent_debugTable = AceEvent.debugTable
    if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then
        local tmp = new()
        for obj, method in pairs(AceEvent_onceRegistry[event]) do
            tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil
        end
        local obj = next(tmp)
        while obj do
            local mem, time
            if AceEvent_debugTable then
                if not AceEvent_debugTable[event] then
                    AceEvent_debugTable[event] = new()
                end
                if not AceEvent_debugTable[event][obj] then
                    AceEvent_debugTable[event][obj] = new()
                    AceEvent_debugTable[event][obj].mem = 0
                    AceEvent_debugTable[event][obj].time = 0
                    AceEvent_debugTable[event][obj].count = 0
                end
                if memdiff then
                    table.insert(memstack, memdiff)
                    table.insert(timestack, timediff)
                end
                memdiff, timediff = 0, 0
                mem, time = gcinfo(), GetTime()
            end
            local method = tmp[obj]
            AceEvent.UnregisterEvent(obj, event)
            if type(method) == "string" then
                local obj_method = obj[method]
                if obj_method then
                    obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                end
            elseif method then -- function
                method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
            end
            if AceEvent_debugTable then
                local dmem, dtime = memdiff, timediff
                mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
                AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
                AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
                AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1

                memdiff, timediff = table.remove(memstack), table.remove(timestack)
                if memdiff then
                    memdiff = memdiff + mem + dmem
                    timediff = timediff + time + dtime
                end
            end
            tmp[obj] = nil
            obj = next(tmp)
        end
        del(tmp)
    end

    local AceEvent_throttleRegistry = AceEvent.throttleRegistry
    local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event]
    if AceEvent_registry[event] then
        local tmp = new()
        for obj, method in pairs(AceEvent_registry[event]) do
            tmp[obj] = method
        end
        local obj = next(tmp)
        while obj do
            local method = tmp[obj]
            local continue = false
            if throttleTable and throttleTable[obj] then
                local a1 = a1
                if a1 == nil then
                    a1 = FAKE_NIL
                end
                if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then
                    throttleTable[obj][a1] = GetTime()
                else
                    continue = true
                end
            end
            if not continue then
                local mem, time
                if AceEvent_debugTable then
                    if not AceEvent_debugTable[event] then
                        AceEvent_debugTable[event] = new()
                    end
                    if not AceEvent_debugTable[event][obj] then
                        AceEvent_debugTable[event][obj] = new()
                        AceEvent_debugTable[event][obj].mem = 0
                        AceEvent_debugTable[event][obj].time = 0
                        AceEvent_debugTable[event][obj].count = 0
                    end
                    if memdiff then
                        table.insert(memstack, memdiff)
                        table.insert(timestack, timediff)
                    end
                    memdiff, timediff = 0, 0
                    mem, time = gcinfo(), GetTime()
                end
                if type(method) == "string" then
                    local obj_method = obj[method]
                    if obj_method then
                        obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                    end
                elseif method then -- function
                    method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                end
                if AceEvent_debugTable then
                    local dmem, dtime = memdiff, timediff
                    mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
                    AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
                    AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
                    AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1

                    memdiff, timediff = table.remove(memstack), table.remove(timestack)
                    if memdiff then
                        memdiff = memdiff + mem + dmem
                        timediff = timediff + time + dtime
                    end
                end
            end
            tmp[obj] = nil
            obj = next(tmp)
        end
        del(tmp)
    end
    if AceEvent_registry[ALL_EVENTS] then
        local tmp = new()
        for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do
            tmp[obj] = method
        end
        local obj = next(tmp)
        while obj do
            local method = tmp[obj]
            local mem, time
            if AceEvent_debugTable then
                if not AceEvent_debugTable[event] then
                    AceEvent_debugTable[event] = new()
                end
                if not AceEvent_debugTable[event][obj] then
                    AceEvent_debugTable[event][obj] = new()
                    AceEvent_debugTable[event][obj].mem = 0
                    AceEvent_debugTable[event][obj].time = 0
                    AceEvent_debugTable[event][obj].count = 0
                end
                if memdiff then
                    table.insert(memstack, memdiff)
                    table.insert(timestack, timediff)
                end
                memdiff, timediff = 0, 0
                mem, time = gcinfo(), GetTime()
            end
            if type(method) == "string" then
                local obj_method = obj[method]
                if obj_method then
                    obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
                end
            elseif method then -- function
                method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
            end
            if AceEvent_debugTable then
                local dmem, dtime = memdiff, timediff
                mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff
                AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem
                AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time
                AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1

                memdiff, timediff = table.remove(memstack), table.remove(timestack)
                if memdiff then
                    memdiff = memdiff + mem + dmem
                    timediff = timediff + time + dtime
                end
            end
            tmp[obj] = nil
            obj = next(tmp)
        end
        del(tmp)
    end
    _G.event = _G_event
    AceEvent.currentEvent = lastEvent
end

-- local accessors
local getn = table.getn
local tinsert = table.insert
local tremove = table.remove
local floor = math.floor
local GetTime = GetTime
local next = next
local pairs = pairs
local unpack = unpack

local delayRegistry
local tmp = {}
local function OnUpdate()
    local t = GetTime()
    for k,v in pairs(delayRegistry) do
        tmp[k] = true
    end
    for k in pairs(tmp) do
        local v = delayRegistry[k]
        if v then
            local v_time = v.time
            if not v_time then
                delayRegistry[k] = del(v)
            elseif v_time <= t then
                local v_repeatDelay = v.repeatDelay
                if v_repeatDelay then
                    -- use the event time, not the current time, else timing inaccuracies add up over time
                    v.time = v_time + v_repeatDelay
                end
                local event = v.event
                local mem, time
                if AceEvent_debugTable then
                    mem, time = gcinfo(), GetTime()
                end
                if type(event) == "function" then
                    event(unpack(v))
                else
                    AceEvent:TriggerEvent(event, unpack(v))
                end
                if AceEvent_debugTable then
                    mem, time = gcinfo() - mem, GetTime() - time
                    v.mem = v.mem + mem
                    v.timeSpent = v.timeSpent + time
                    v.count = v.count + 1
                end
                if not v_repeatDelay then
                    local x = delayRegistry[k]
                    if x and x.time == v_time then -- check if it was manually reset
                        delayRegistry[k] = del(v)
                    end
                end
            end
        end
    end
    for k in pairs(tmp) do
        tmp[k] = nil
    end
    if not next(delayRegistry) then
        AceEvent.frame:Hide()
    end
end

local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    local id
    if type(event) == "string" or type(event) == "table" then
        if type(event) == "table" then
            if not delayRegistry or not delayRegistry[event] then
                AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
            end
        end
        if type(delay) ~= "number" then
            id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
            AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number")
            AceEvent:argCheck(delay, 4, "number")
            self:CancelScheduledEvent(id)
        end
    else
        AceEvent:argCheck(event, 2, "string", "function")
        AceEvent:argCheck(delay, 3, "number")
    end

    if not delayRegistry then
        AceEvent.delayRegistry = new()
        delayRegistry = AceEvent.delayRegistry
        AceEvent.frame:SetScript("OnUpdate", OnUpdate)
    end
    local t
    if type(id) == "table" then
        for k in pairs(id) do
            id[k] = nil
        end
        t = id
    else
        t = new()
    end
    t[1] = a1
    t[2] = a2
    t[3] = a3
    t[4] = a4
    t[5] = a5
    t[6] = a6
    t[7] = a7
    t[8] = a8
    t[9] = a9
    t[10] = a10
    t[11] = a11
    t[12] = a12
    t[13] = a13
    t[14] = a14
    t[15] = a15
    t[16] = a16
    t[17] = a17
    t[18] = a18
    t[19] = a19
    t[20] = a20
    table_setn(t, 20)
    t.event = event
    t.time = GetTime() + delay
    t.self = self
    t.id = id or t
    t.repeatDelay = repeating and delay
    if AceEvent_debugTable then
        t.mem = 0
        t.count = 0
        t.timeSpent = 0
    end
    delayRegistry[t.id] = t
    AceEvent.frame:Show()
    return t.id
end

function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    if type(event) == "string" or type(event) == "table" then
        if type(event) == "table" then
            if not delayRegistry or not delayRegistry[event] then
                AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
            end
        end
        if type(delay) ~= "number" then
            AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
            AceEvent:argCheck(a1, 4, "number")
        end
    else
        AceEvent:argCheck(event, 2, "string", "function")
        AceEvent:argCheck(delay, 3, "number")
    end

    return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
    if type(event) == "string" or type(event) == "table" then
        if type(event) == "table" then
            if not delayRegistry or not delayRegistry[event] then
                AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.")
            end
        end
        if type(delay) ~= "number" then
            AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number")
            AceEvent:argCheck(a1, 4, "number")
        end
    else
        AceEvent:argCheck(event, 2, "string", "function")
        AceEvent:argCheck(delay, 3, "number")
    end

    return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
end

function AceEvent:CancelScheduledEvent(t)
    AceEvent:argCheck(t, 2, "string", "table")
    if delayRegistry then
        local v = delayRegistry[t]
        if v then
            delayRegistry[t] = del(v)
            if not next(delayRegistry) then
                AceEvent.frame:Hide()
            end
            return true
        end
    end
    return false
end

function AceEvent:IsEventScheduled(t)
    AceEvent:argCheck(t, 2, "string", "table")
    if delayRegistry then
        local v = delayRegistry[t]
        if v then
            return true, v.time - GetTime()
        end
    end
    return false, nil
end

function AceEvent:UnregisterEvent(event)
    AceEvent:argCheck(event, 2, "string")
    local AceEvent_registry = AceEvent.registry
    if AceEvent_registry[event] and AceEvent_registry[event][self] then
        AceEvent_registry[event][self] = nil
        local AceEvent_onceRegistry = AceEvent.onceRegistry
        if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then
            AceEvent_onceRegistry[event][self] = nil
            if not next(AceEvent_onceRegistry[event]) then
                AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event])
            end
        end
        local AceEvent_throttleRegistry = AceEvent.throttleRegistry
        if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then
            AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self])
            if not next(AceEvent_throttleRegistry[event]) then
                AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event])
            end
        end
        if not next(AceEvent_registry[event]) then
            AceEvent_registry[event] = del(AceEvent_registry[event])
            if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
                AceEvent.frame:UnregisterEvent(event)
            end
        end
    else
        if self == AceEvent then
            error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2)
        else
            AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self)
        end
    end
    AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
end

function AceEvent:UnregisterAllEvents()
    local AceEvent_registry = AceEvent.registry
    if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then
        AceEvent_registry[ALL_EVENTS][self] = nil
        if not next(AceEvent_registry[ALL_EVENTS]) then
            del(AceEvent_registry[ALL_EVENTS])
            AceEvent.frame:UnregisterAllEvents()
            for k,v in pairs(AceEvent_registry) do
                if k ~= ALL_EVENTS then
                    AceEvent.frame:RegisterEvent(k)
                end
            end
            AceEvent_registry[event] = nil
        end
    end
    local first = true
    for event, data in pairs(AceEvent_registry) do
        if first then
            if AceEvent_registry.AceEvent_EventUnregistered then
                event = "AceEvent_EventUnregistered"
            else
                first = false
            end
        end
        local x = data[self]
        data[self] = nil
        if x and event ~= ALL_EVENTS then
            if not next(data) then
                del(data)
                if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then
                    AceEvent.frame:UnregisterEvent(event)
                end
                AceEvent_registry[event] = nil
            end
            AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event)
        end
        if first then
            event = nil
        end
    end
    if AceEvent.onceRegistry then
        for event, data in pairs(AceEvent.onceRegistry) do
            data[self] = nil
        end
    end
end

function AceEvent:CancelAllScheduledEvents()
    if delayRegistry then
        for k,v in pairs(delayRegistry) do
            if v.self == self then
                delayRegistry[k] = del(v)
            end
        end
        if not next(delayRegistry) then
            AceEvent.frame:Hide()
        end
    end
end

function AceEvent:IsEventRegistered(event)
    AceEvent:argCheck(event, 2, "string")
    local AceEvent_registry = AceEvent.registry
    if self == AceEvent then
        return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false
    end
    if AceEvent_registry[event] and AceEvent_registry[event][self] then
        return true, AceEvent_registry[event][self]
    end
    return false, nil
end

local bucketfunc
function AceEvent:RegisterBucketEvent(event, delay, method)
    AceEvent:argCheck(event, 2, "string", "table")
    if type(event) == "table" then
        for k,v in pairs(event) do
            if type(k) ~= "number" then
                AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.")
            elseif type(v) ~= "string" then
                AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.")
            end
        end
    end
    AceEvent:argCheck(delay, 3, "number")
    if AceEvent == self then
        AceEvent:argCheck(method, 4, "function")
        self = method
    else
        if type(event) == "string" then
            AceEvent:argCheck(method, 4, "string", "function", "nil")
            if not method then
                method = event
            end
        else
            AceEvent:argCheck(method, 4, "string", "function")
        end

        if type(method) == "string" and type(self[method]) ~= "function" then
            AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method)
        end
    end
    if not AceEvent.buckets then
        AceEvent.buckets = new()
    end
    if not AceEvent.buckets[event] then
        AceEvent.buckets[event] = new()
    end
    if not AceEvent.buckets[event][self] then
        AceEvent.buckets[event][self] = new()
        AceEvent.buckets[event][self].current = new()
        AceEvent.buckets[event][self].self = self
    else
        AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id)
    end
    local bucket = AceEvent.buckets[event][self]
    bucket.method = method

    local func = function(arg1)
        bucket.run = true
        if arg1 then
            bucket.current[arg1] = true
        end
    end
    AceEvent.buckets[event][self].func = func
    if type(event) == "string" then
        AceEvent.RegisterEvent(self, event, func)
    else
        for _,v in ipairs(event) do
            AceEvent.RegisterEvent(self, v, func)
        end
    end
    if not bucketfunc then
        bucketfunc = function(bucket)
            local current = bucket.current
            local method = bucket.method
            local self = bucket.self
            if bucket.run then
                if type(method) == "string" then
                    self[method](self, current)
                elseif method then -- function
                    method(current)
                end
                for k in pairs(current) do
                    current[k] = nil
                    k = nil
                end
                bucket.run = false
            end
        end
    end
    bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket)
end

function AceEvent:IsBucketEventRegistered(event)
    AceEvent:argCheck(event, 2, "string", "table")
    return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self]
end

function AceEvent:UnregisterBucketEvent(event)
    AceEvent:argCheck(event, 2, "string", "table")
    if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then
        AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self)
    end

    local bucket = AceEvent.buckets[event][self]

    if type(event) == "string" then
        AceEvent.UnregisterEvent(self, event)
    else
        for _,v in ipairs(event) do
            AceEvent.UnregisterEvent(self, v)
        end
    end
    AceEvent:CancelScheduledEvent(bucket.id)

    del(bucket.current)
    AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self])
    if not next(AceEvent.buckets[event]) then
        AceEvent.buckets[event] = del(AceEvent.buckets[event])
    end
end

function AceEvent:UnregisterAllBucketEvents()
    if not AceEvent.buckets or not next(AceEvent.buckets) then
        return
    end
    for k,v in pairs(AceEvent.buckets) do
        if v == self then
            AceEvent.UnregisterBucketEvent(self, k)
            k = nil
        end
    end
end

function AceEvent:OnEmbedDisable(target)
    self.UnregisterAllEvents(target)

    self.CancelAllScheduledEvents(target)

    self.UnregisterAllBucketEvents(target)
end

function AceEvent:EnableDebugging()
    if not self.debugTable then
        self.debugTable = new()

        if delayRegistry then
            for k,v in pairs(self.delayRegistry) do
                if not v.mem then
                    v.mem = 0
                    v.count = 0
                    v.timeSpent = 0
                end
            end
        end
    end
end

function AceEvent:IsFullyInitialized()
    return self.postInit or false
end

function AceEvent:IsPostPlayerLogin()
    return self.playerLogin or false
end

function AceEvent:activate(oldLib, oldDeactivate)
    AceEvent = self

    if oldLib then
        self.onceRegistry = oldLib.onceRegistry
        self.throttleRegistry = oldLib.throttleRegistry
        self.delayRegistry = oldLib.delayRegistry
        self.buckets = oldLib.buckets
        self.registry = oldLib.registry
        self.frame = oldLib.frame
        self.debugTable = oldLib.debugTable
        self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true
        self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true
        self.ALL_EVENTS = oldLib.ALL_EVENTS
        self.FAKE_NIL = oldLib.FAKE_NIL
        self.RATE = oldLib.RATE
    end
    if not self.registry then
        self.registry = {}
    end
    if not self.frame then
        self.frame = CreateFrame("Frame", "AceEvent20Frame")
    end
    if not self.ALL_EVENTS then
        self.ALL_EVENTS = {}
    end
    if not self.FAKE_NIL then
        self.FAKE_NIL = {}
    end
    if not self.RATE then
        self.RATE = {}
    end
    ALL_EVENTS = self.ALL_EVENTS
    FAKE_NIL = self.FAKE_NIL
    RATE = self.RATE
    local inPlw = false
    local blacklist = {
        UNIT_INVENTORY_CHANGED = true,
        BAG_UPDATE = true,
        ITEM_LOCK_CHANGED = true,
        ACTIONBAR_SLOT_CHANGED = true,
    }
    self.frame:SetScript("OnEvent", function()
        local event = event
        if event == "PLAYER_ENTERING_WORLD" then
            inPlw = false
        elseif event == "PLAYER_LEAVING_WORLD" then
            inPlw = true
        end
        if event and (not inPlw or not blacklist[event]) then
            self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
        end
    end)
    if self.delayRegistry then
        delayRegistry = self.delayRegistry
        self.frame:SetScript("OnUpdate", OnUpdate)
    end

    self:UnregisterAllEvents()
    self:CancelAllScheduledEvents()

    registeringFromAceEvent = true
    self:RegisterEvent("LOOT_OPENED", function()
        SendAddonMessage("LOOT_OPENED", "", "RAID")
    end)
    registeringFromAceEvent = nil

    if not self.playerLogin then
        registeringFromAceEvent = true
        self:RegisterEvent("PLAYER_LOGIN", function()
            self.playerLogin = true
        end, true)
        registeringFromAceEvent = nil
    end

    if not self.postInit then
        local isReload = true
        local function func()
            self.postInit = true
            self:TriggerEvent("AceEvent_FullyInitialized")
            if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then
                self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE")
            end
            if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
                self:UnregisterEvent("MEETINGSTONE_CHANGED")
            end
            if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then
                self:UnregisterEvent("MINIMAP_ZONE_CHANGED")
            end
            if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then
                self:UnregisterEvent("LANGUAGE_LIST_CHANGED")
            end
        end
        registeringFromAceEvent = true
        local f = function()
            self.playerLogin = true
            self:ScheduleEvent("AceEvent_FullyInitialized", func, 1)
        end
        self:RegisterEvent("MEETINGSTONE_CHANGED", f, true)
        self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function()
            self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05)
        end)
        self:RegisterEvent("LANGUAGE_LIST_CHANGED", function()
            if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then
                registeringFromAceEvent = true
                self:UnregisterEvent("MEETINGSTONE_CHANGED")
                self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true)
                registeringFromAceEvent = nil
            end
        end)
        self:ScheduleEvent("AceEvent_FullyInitialized", func, 10)
        registeringFromAceEvent = nil
    end

    self.super.activate(self, oldLib, oldDeactivate)
    if oldLib then
        oldDeactivate(oldLib)
    end
end

AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate)
AceEvent = AceLibrary(MAJOR_VERSION)