--[[
Name: AceDB-2.0
Revision: $Rev: 56534 $
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
License: LGPL v2.1
]]

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

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

if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end

local function safecall(func,...)
	local success, err = pcall(func,...)
	if not success then geterrorhandler()(err) end
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, RESET_PROFILE, RESET_PROFILE_DESC, CHARACTER_COLON, REALM_COLON, CLASS_COLON, DEFAULT, ALTERNATIVE

-- Move these into "enUS" when they've been translated in all other locales
local DELETE_PROFILE = "Delete"
local DELETE_PROFILE_DESC = "Deletes a profile. Note that no check is made whether this profile is in use by other characters or not."
local DELETE_PROFILE_USAGE = "<profile name>"

if GetLocale() == "deDE" then
	DELETE_PROFILE = "L\195\182schen"
	DELETE_PROFILE_DESC = "L\195\182scht ein Profil. Beachte das nicht \195\188berpr\195\188ft wird ob das zu l\195\182schende Profil von anderen Charakteren genutzt wird oder nicht."
	DELETE_PROFILE_USAGE = "<profil name>"

	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>"
	RESET_PROFILE = "Resette das Profil"
	RESET_PROFILE_DESC = "Entfernt alle Einstellungen des gegenw\195\164rtigen Profils."

	CHARACTER_COLON = "Charakter: "
	REALM_COLON = "Realm: "
	CLASS_COLON = "Klasse: "

	DEFAULT = "Vorgabe"
	ALTERNATIVE = "Alternativ"
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>"
	RESET_PROFILE = "Reset profile" -- fix
	RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix

	CHARACTER_COLON = "Personnage: "
	REALM_COLON = "Royaume: "
	CLASS_COLON = "Classe: "

	DEFAULT = "Default" -- fix
	ALTERNATIVE = "Alternative" -- fix
elseif GetLocale() == "koKR" then
	DELETE_PROFILE = "삭제"
	DELETE_PROFILE_DESC = "프로필을 삭제합니다."
	DELETE_PROFILE_USAGE = "<프로필명>"

	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 = "<프로필명>"
	RESET_PROFILE = "프로필 초기화"
	RESET_PROFILE_DESC = "모든 세팅에서 현재 프로필을 초기화 합니다."

	CHARACTER_COLON = "캐릭터: "
	REALM_COLON = "서버: "
	CLASS_COLON = "직업: "

	DEFAULT = "기본값"
	ALTERNATIVE = "대체"
elseif GetLocale() == "zhTW" then
	DELETE_PROFILE = "刪除"
	DELETE_PROFILE_DESC = "刪除記錄檔。注意，有可能別的角色也使用這個記錄檔。"
	DELETE_PROFILE_USAGE = "<記錄檔名稱>"

	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 = "<記錄檔名稱>"
	RESET_PROFILE = "重設記錄檔"
	RESET_PROFILE_DESC = "清除目前的記錄檔上的所有設定。"

	CHARACTER_COLON = "角色: "
	REALM_COLON = "伺服器: "
	CLASS_COLON = "職業: "

	DEFAULT = "預設"
	ALTERNATIVE = "替代"
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>"
	RESET_PROFILE = "Reset profile" -- fix
	RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix

	CHARACTER_COLON = "\229\173\151\231\172\166: "
	REALM_COLON = "\229\159\159: "
	CLASS_COLON = "\233\128\137\228\187\182\231\177\187: "

	DEFAULT = "Default" -- fix
	ALTERNATIVE = "Alternative" -- fix
elseif GetLocale() == "esES" then
	ACTIVE = "Activo"
	ENABLED = "Activado"
	STATE = "Estado"
	TOGGLE_ACTIVE = "Parar/Continuar este accesorio"
	MAP_ACTIVESUSPENDED = { [true] = "|cff00ff00Activo|r", [false] = "|cffff0000Parado|r" }
	SET_PROFILE = "Selecciona el perfil para este accesorio."
	SET_PROFILE_USAGE = "{perso || clase || reino || <nombre del perfil>}"
	PROFILE = "Perfil"
	PLAYER_OF_REALM = "%s de %s"
	CHOOSE_PROFILE_DESC = "Elige un perfil."
	CHOOSE_PROFILE_GUI = "Elige"
	COPY_PROFILE_DESC = "Copiar de un perfil a otro"
	COPY_PROFILE_GUI = "Copiar desde"
	OTHER_PROFILE_DESC = "Elige otro perfil."
	OTHER_PROFILE_GUI = "Otro"
	OTHER_PROFILE_USAGE = "<nombre del perfil>"
	RESET_PROFILE = "Reset profile" -- fix
	RESET_PROFILE_DESC = "Clear all settings of the current profile." -- fix

	CHARACTER_COLON = "Personaje: "
	REALM_COLON = "Reino: "
	CLASS_COLON = "Clase: "

	DEFAULT = "Por defecto"
	ALTERNATIVE = "Alternativo"
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>"
	RESET_PROFILE = "Reset profile"
	RESET_PROFILE_DESC = "Clear all settings of the current profile."

	CHARACTER_COLON = "Character: "
	REALM_COLON = "Realm: "
	CLASS_COLON = "Class: "

	DEFAULT = "Default"
	ALTERNATIVE = "Alternative"
end
local convertFromOldCharID
do
	local matchStr = "^" .. PLAYER_OF_REALM:gsub("([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1"):gsub("%%s", "(.+)") .. "$"
	function convertFromOldCharID(str)
		local player, realm = str:match(matchStr)
		if not player then
			return str
		end
		return player .. " - " .. realm
	end
end

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

local _G = getfenv(0)

local function inheritDefaults(t, defaults)
	if not defaults then
		return t
	end
	for k,v in pairs(defaults) do
		if k == "*" or 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 == k) and type(t[key]) == "table" then
					inheritDefaults(t[key], v)
				end
			end
		else
			if type(v) == "table" then
				if type(rawget(t, k)) ~= "table" then
					t[k] = {}
				end
				inheritDefaults(t[k], v)
				if defaults["**"] then
					inheritDefaults(t[k], defaults["**"])
				end
			elseif rawget(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" or race == "BloodElf" then
	faction = FACTION_HORDE
else
	faction = FACTION_ALLIANCE
end
local server = GetRealmName():trim()
local charID = UnitName("player") .. " - " .. server
local realmID = server .. " - " .. faction
local classID = UnitClass("player")

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

AceDB.FACTION = faction
AceDB.REALM = server
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
		list[t] = true
	end
end

local caseInsensitive_mt = {
	__index = function(self, key)
		if type(key) ~= "string" then
			return nil
		end
		local lowerKey = key:lower()
		for k,v in pairs(self) do
			if k:lower() == 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 = key:lower()
		for k in pairs(self) do
			if k:lower() == 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 == "server" then
		if type(db.raw.servers) ~= "table" then
			db.raw.servers = {}
		end
		local id = server
		if type(db.raw.servers[id]) ~= "table" then
			db.raw.servers[id] = {}
		end
		rawset(db, 'server', db.raw.servers[id])
		if db.defaults and db.defaults.server then
			inheritDefaults(db.server, db.defaults.server)
		end
		return db.server
	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 == "faction" then
		if type(db.raw.factions) ~= "table" then
			db.raw.factions = {}
		end
		local id = faction
		if type(db.raw.factions[id]) ~= "table" then
			db.raw.factions[id] = {}
		end
		rawset(db, 'faction', db.raw.factions[id])
		if db.defaults and db.defaults.faction then
			inheritDefaults(db.faction, db.defaults.faction)
		end
		return db.faction
	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(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
	error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end }

local function RecalculateAceDBCopyFromList(target)
	local db = target.db
	local t = target['acedb-profile-copylist']
	for k,v in pairs(t) do
		t[k] = nil
	end
	local _,currentProfile = AceDB.GetProfile(target)
	if db and db.raw then
		if db.raw.profiles then
			for k in pairs(db.raw.profiles) do
				if currentProfile ~= k then
					if k:find("^char/") then
						local name = k:sub(6)
						local player, realm = name:match("^(.*) %- (.*)$")
						if player then
							name = PLAYER_OF_REALM:format(player, realm)
						end
						t[k] = CHARACTER_COLON .. name
					elseif k:find("^realm/") then
						local name = k:sub(7)
						t[k] = REALM_COLON .. name
					elseif k:find("^class/") then
						local name = k:sub(7)
						t[k] = CLASS_COLON .. name
					else
						t[k] = k
					end
				end
			end
		end
		if db.raw.namespaces then
			for _,n in pairs(db.raw.namespaces) do
				if n.profiles then
					for k in pairs(n.profiles) do
						if currentProfile ~= k then
							if k:find('^char/') then
								local name = k:sub(6)
								local player, realm = name:match("^(.*) %- (.*)$")
								if player then
									name = PLAYER_OF_REALM:format(player, realm)
								end
								t[k] = CHARACTER_COLON .. name
							elseif k:find('^realm/') then
								local name = k:sub(7)
								t[k] = REALM_COLON .. name
							elseif k:find('^class/') then
								local name = k:sub(7)
								t[k] = CLASS_COLON .. name
							else
								t[k] = k
							end
						end
					end
				end
			end
		end
	end
	if t.Default then
		t.Default = DEFAULT
	end
	if t.Alternative then
		t.Alternative = ALTERNATIVE
	end
end

local function RecalculateAceDBProfileList(target)
	local t = target['acedb-profile-list']
	for k,v in pairs(t) do
		t[k] = nil
	end
	t.char = CHARACTER_COLON .. PLAYER_OF_REALM:format(UnitName("player"), server)
	t.realm = REALM_COLON .. realmID
	t.class = CLASS_COLON .. classID
	t.Default = DEFAULT
	local db = target.db
	if db and db.raw then
		if db.raw.profiles then
			for k in pairs(db.raw.profiles) do
				if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then
					t[k] = k
				end
			end
		end
		if db.raw.namespaces then
			for _,n in pairs(db.raw.namespaces) do
				if n.profiles then
					for k in pairs(n.profiles) do
						if not k:find("^char/") and not k:find("^realm/") and not k:find("^class/") then
							t[k] = k
						end
					end
				end
			end
		end
		local curr = db.raw.currentProfile and db.raw.currentProfile[charID]
		if curr and not t[curr] then
			t[curr] = curr
		end
	end
	if t.Alternative then
		t.Alternative = ALTERNATIVE
	end
end

local CrawlForSerialization
local CrawlForDeserialization

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

local function DeserializeObject(t)
	CrawlForDeserialization(t)
	local className = t[0]
	t[0] = nil
	return AceLibrary(className):Deserialize(unpack(t))
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(rawget(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(rawget(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(rawget(t, 0)) == "string" and AceLibrary:HasInstance(rawget(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(rawget(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(rawget(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 == "server" 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].servers) ~= "table" then
			db.raw.namespaces[name].servers = {}
		end
		local id = server
		if type(db.raw.namespaces[name].servers[id]) ~= "table" then
			db.raw.namespaces[name].servers[id] = {}
		end
		rawset(namespace, 'server', db.raw.namespaces[name].servers[id])
		if namespace.defaults and namespace.defaults.server then
			inheritDefaults(namespace.server, namespace.defaults.server)
		end
		return namespace.server
	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 == "faction" 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].factions) ~= "table" then
			db.raw.namespaces[name].factions = {}
		end
		local id = faction
		if type(db.raw.namespaces[name].factions[id]) ~= "table" then
			db.raw.namespaces[name].factions[id] = {}
		end
		rawset(namespace, 'faction', db.raw.namespaces[name].factions[id])
		if namespace.defaults and namespace.defaults.faction then
			inheritDefaults(namespace.faction, namespace.defaults.faction)
		end
		return namespace.faction
	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(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end, __newindex = function(db, key, value)
	error(("Cannot access key %q in db table. You may want to use db.profile[%q]"):format(tostring(key), tostring(key)), 2)
end }

local tmp = {}
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 db.charName then
		if type(_G[db.charName]) ~= "table" then
			_G[db.charName] = {}
		else
			CrawlForDeserialization(_G[db.charName])
		end
	end
	rawset(db, 'raw', _G[db.name])
	if not db.raw.currentProfile then
		db.raw.currentProfile = {}
	else
		for k,v in pairs(db.raw.currentProfile) do
			tmp[convertFromOldCharID(k)] = v
			db.raw.currentProfile[k] = nil
		end
		for k,v in pairs(tmp) do
			db.raw.currentProfile[k] = v
			tmp[k] = nil
		end
	end
	if not db.raw.currentProfile[charID] then
		db.raw.currentProfile[charID] = AceDB.registry[self] or "Default"
	end
	if db.raw.profiles then
		for k,v in pairs(db.raw.profiles) do
			local new_k = k
			if k:find("^char/") then
				new_k = "char/" .. convertFromOldCharID(k:sub(6))
			end
			tmp[new_k] = v
			db.raw.profiles[k] = nil
		end
		for k,v in pairs(tmp) do
			db.raw.profiles[k] = v
			tmp[k] = nil
		end
	end
	if db.raw.disabledModules then -- AceModuleCore-2.0
		for k,v in pairs(db.raw.disabledModules) do
			local new_k = k
			if k:find("^char/") then
				new_k = "char/" .. convertFromOldCharID(k:sub(6))
			end
			tmp[new_k] = v
			db.raw.disabledModules[k] = nil
		end
		for k,v in pairs(tmp) do
			db.raw.disabledModules[k] = v
			tmp[k] = nil
		end
	end
	if db.raw.chars then
		for k,v in pairs(db.raw.chars) do
			tmp[convertFromOldCharID(k)] = v
			db.raw.chars[k] = nil
		end
		for k,v in pairs(tmp) do
			db.raw.chars[k] = v
			tmp[k] = nil
		end
	end
	if db.raw.namespaces then
		for l,u in pairs(db.raw.namespaces) do
			if u.chars then
				for k,v in pairs(u.chars) do
					tmp[convertFromOldCharID(k)] = v
					u.chars[k] = nil
				end
				for k,v in pairs(tmp) do
					u.chars[k] = v
					tmp[k] = nil
				end
			end
		end
	end
	if db.raw.disabled then
		setmetatable(db.raw.disabled, caseInsensitive_mt)
	end
	if self['acedb-profile-copylist'] then
		RecalculateAceDBCopyFromList(self)
	end
	if self['acedb-profile-list'] then
		RecalculateAceDBProfileList(self)
	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, defaultProfile)
	AceDB:argCheck(name, 2, "string")
	AceDB:argCheck(charName, 3, "string", "nil")
	AceDB:argCheck(defaultProfile, 4, "string", "nil")
	if self.db then
		AceDB:error("Cannot call \"RegisterDB\" if self.db is set.")
	end
	local stack = debugstack()
	local addonName = stack:gsub(".-\n.-\\AddOns\\(.-)\\.*", "%1")
	self.db = {
		name = name,
		charName = charName
	}
	AceDB.registry[self] = defaultProfile or "Default"
	if AceDB.addonsLoaded[addonName] then
		AceDB.InitializeDB(self, addonName)
	else
		AceDB.addonsToBeInitialized[self] = addonName
	end
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")
	else
		AceDB:argCheck(kind, 2, "string")
		AceDB:argCheck(defaults, 3, "table")
	end
	if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then
		AceDB:error("Bad argument #%d to `RegisterDefaults' (\"char\", \"class\", \"profile\", \"account\", \"realm\", \"server\", or \"faction\" expected, got %q)", a3 and 3 or 2, kind)
	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, a2)
	local name
	if a2 then
		name, kind = kind, a2
		AceDB:argCheck(name, 2, "nil", "string")
		AceDB:argCheck(kind, 3, "nil", "string")
	else
		AceDB:argCheck(kind, 2, "nil", "string")
		if kind ~= "char" and kind ~= "class" and kind ~= "profile" and kind ~= "account" and kind ~= "realm" and kind ~= "faction" and kind ~= "server" then
			name, kind = kind, nil
		end
	end
	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 not kind then
		if not name 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)
					rawset(v, 'server', nil)
					rawset(v, 'faction', nil)
				end
			end
		else
			if db.raw.namespaces then
				db.raw.namespaces[name] = nil
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'account', nil)
					rawset(v, 'char', nil)
					rawset(v, 'class', nil)
					rawset(v, 'profile', nil)
					rawset(v, 'realm', nil)
					rawset(v, 'server', nil)
					rawset(v, 'faction', nil)
				end
			end
		end
	elseif kind == "account" then
		if name then
			db.raw.account = nil
			rawset(db, 'account', nil)
			if db.raw.namespaces then
				for name,v in pairs(db.raw.namespaces) do
					v.account = nil
				end
			end
			if db.namespaces then
				for name,v in pairs(db.namespaces) do
					rawset(v, 'account', nil)
				end
			end
		else
			if db.raw.namespaces and db.raw.namespaces[name] then
				db.raw.namespaces[name].account = nil
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'account', nil)
				end
			end
		end
	elseif kind == "char" then
		if name 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
		else
			if db.charName then
				local x = _G[db.charName]
				if x.namespaces then
					x.namespaces[name] = nil
				end
			else
				if db.raw.namespaces then
					local v = db.namespaces[name]
					if v and v.chars then
						v.chars[charID] = nil
					end
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'char', nil)
				end
			end
		end
	elseif kind == "realm" then
		if not name 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
		else
			if db.raw.namespaces then
				local v = db.raw.namespaces[name]
				if v and v.realms then
					v.realms[realmID] = nil
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'realm', nil)
				end
			end
		end
	elseif kind == "server" then
		if not name then
			if db.raw.servers then
				db.raw.servers[server] = nil
			end
			rawset(db, 'server', nil)
			if db.raw.namespaces then
				for name,v in pairs(db.raw.namespaces) do
					if v.servers then
						v.servers[server] = nil
					end
				end
			end
			if db.namespaces then
				for name,v in pairs(db.namespaces) do
					rawset(v, 'server', nil)
				end
			end
		else
			if db.raw.namespaces then
				local v = db.raw.namespaces[name]
				if v and v.servers then
					v.servers[server] = nil
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'server', nil)
				end
			end
		end
	elseif kind == "faction" then
		if not name then
			if db.raw.factions then
				db.raw.factions[faction] = nil
			end
			rawset(db, 'faction', nil)
			if db.raw.namespaces then
				for name,v in pairs(db.raw.namespaces) do
					if v.factions then
						v.factions[faction] = nil
					end
				end
			end
			if db.namespaces then
				for name,v in pairs(db.namespaces) do
					rawset(v, 'faction', nil)
				end
			end
		else
			if db.raw.namespaces then
				local v = db.raw.namespaces[name]
				if v and v.factions then
					v.factions[faction] = nil
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'faction', nil)
				end
			end
		end
	elseif kind == "class" then
		if not name 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
		else
			if db.raw.namespaces then
				local v = db.raw.namespaces[name]
				if v and v.classes then
					v.classes[classID] = nil
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'class', nil)
				end
			end
		end
	elseif kind == "profile" then
		local id = db.raw.currentProfile and db.raw.currentProfile[charID] or AceDB.registry[self] or "Default"
		if id == "char" then
			id = "char/" .. charID
		elseif id == "class" then
			id = "class/" .. classID
		elseif id == "realm" then
			id = "realm/" .. realmID
		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, id)
					end
				end
			end
			current = current.super
		end
		if type(self.OnProfileDisable) == "function" then
			safecall(self.OnProfileDisable, self, id)
		end
		local active = self:IsActive()

		if not name then
			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
		else
			if db.raw.namespaces then
				local v = db.raw.namespaces[name]
				if v and v.profiles then
					v.profiles[id] = nil
				end
			end
			if db.namespaces then
				local v = db.namespaces[name]
				if v then
					rawset(v, 'profile', nil)
				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, id)
					end
				end
			end
			current = current.super
		end
		if type(self.OnProfileEnable) == "function" then
			safecall(self.OnProfileEnable, self, id)
		end
		local newactive = self:IsActive()
		if active ~= newactive then
			if newactive then
				local first = nil
				if AceOO.inherits(self, "AceAddon-2.0") then
					local AceAddon = AceLibrary("AceAddon-2.0")
					if not AceAddon.addonsStarted[self] then
						return
					end
					if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then
						AceAddon.addonsEnabled[self] = true
						first = true
					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.OnEmbedEnable) == "function" then
								safecall(mixin.OnEmbedEnable, mixin, self, first)
							end
						end
					end
					current = current.super
				end
				if type(self.OnEnable) == "function" then
					safecall(self.OnEnable, self, first)
				end
				if AceEvent then
					AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first)
				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
	else
		return -- skip event
	end
	if AceEvent then
		AceEvent:TriggerEvent("AceDB20_ResetDB", self, self.db.name, kind)
	end
end

local function cleanDefaults(t, defaults, blocker)
	if defaults then
		for k,v in pairs(t) do
			if (not blocker or (blocker[k] == nil and blocker['*'] == nil and blocker['**'] == nil)) and (defaults[k] ~= nil or defaults['*'] ~= nil or defaults['**'] ~= nil) then
				local u = defaults[k]
				if u == nil then
					u = defaults['*']
					if u == nil then
						u = defaults['**']
					end
				end
				if v == u then
					t[k] = nil
				elseif type(v) == "table" and type(u) == "table" then
					if cleanDefaults(v, u) then
						t[k] = nil
					else
						local w = defaults['**']
						if w ~= u then
							if cleanDefaults(v, w, u) then
								t[k] = nil
							end
						end
					end
				end
			end
		end
	end
	return t and next(t) == nil
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] = AceDB.registry[self] or "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
	setmetatable(to, from)
	return to
end

function AceDB:SetProfile(name)
	AceDB:argCheck(name, 2, "string")
	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 lowerName = name:lower()
	if lowerName:find("^char/") or lowerName:find("^realm/") or lowerName:find("^class/") then
		if lowerName:find("^char/") then
			name = "char"
		else
			name = lowerName:sub(1, 5)
		end
		lowerName = name:lower()
	end
	local oldName = db.raw.currentProfile[charID]
	if oldName:lower() == name:lower() 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
	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)
				end
			end
		end
		current = current.super
	end
	if type(self.OnProfileEnable) == "function" then
		safecall(self.OnProfileEnable, self, oldName, oldProfileData)
	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
		local first = nil
		if AceOO.inherits(self, "AceAddon-2.0") then
			local AceAddon = AceLibrary("AceAddon-2.0")
			if not AceAddon.addonsStarted[self] then
				return
			end
			if AceAddon.addonsEnabled and not AceAddon.addonsEnabled[self] then
				first = true
			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, first)
						end
					end
				end
				current = current.super
			end
			if type(self.OnEnable) == "function" then
				safecall(self.OnEnable, self, first)
			end
			if AceEvent then
				AceEvent:TriggerEvent("Ace2_AddonEnabled", self, first)
			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
		RecalculateAceDBProfileList(self)
	end
	if self['acedb-profile-copylist'] then
		RecalculateAceDBCopyFromList(self)
	end
	if Dewdrop then
		Dewdrop:Refresh()
	end
end

function AceDB:CopyProfileFrom(copyFrom)
	AceDB:argCheck(copyFrom, 2, "string")
	if not self.db or not self.db.raw then
		AceDB:error("Cannot call \"CopyProfileFrom\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
	end
	local db = self.db
	local lowerCopyFrom = copyFrom:lower()
	if not db.raw.profiles or not db.raw.profiles[copyFrom] then
		local good = false
		if db.raw.namespaces then
			for _,n in pairs(db.raw.namespaces) do
				if n.profiles and n.profiles[copyFrom] then
					good = true
					break
				end
			end
		end
		if not good then
			AceDB:error("Cannot copy from profile %q, it does not exist.", copyFrom)
		end
	end
	local currentProfile = db.raw.currentProfile[charID]
	if currentProfile:lower() == lowerCopyFrom then
		AceDB:error("Cannot copy from profile %q, it is currently in use.", copyFrom)
	end
	local oldProfileData = db.profile
	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, currentProfile)
				end
			end
		end
		current = current.super
	end
	if type(self.OnProfileDisable) == "function" then
		safecall(self.OnProfileDisable, self, realName)
	end
	local active = self:IsActive()
	for k,v in pairs(db.profile) do
		db.profile[k] = nil
	end
	if db.raw.profiles[copyFrom] then
		copyTable(db.profile, db.raw.profiles[copyFrom])
	end
	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
			if db.raw.namespaces[l].profiles[copyFrom] then
				copyTable(u.profile, db.raw.namespaces[l].profiles[copyFrom])
			end
			inheritDefaults(u.profile, u.defaults and u.defaults.profile)
		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, copyFrom, oldProfileData, copyFrom)
				end
			end
		end
		current = current.super
	end
	if type(self.OnProfileEnable) == "function" then
		safecall(self.OnProfileEnable, self, copyFrom, oldProfileData, copyFrom)
	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
		RecalculateAceDBProfileList(self)
	end
	if self['acedb-profile-copylist'] then
		RecalculateAceDBCopyFromList(self)
	end
	if Dewdrop then
		Dewdrop:Refresh()
	end
end

function AceDB:DeleteProfile(profile, noconfirm)
	AceDB:argCheck(profile , 2, "string")
	if not self.db or not self.db.raw then
		AceDB:error("Cannot call \"DeleteProfile\" before \"RegisterDB\" has been called and before \"ADDON_LOADED\" has been fired.")
	end
	local db = self.db

	local currentProfile = db.raw.currentProfile[charID]
	if currentProfile:lower() == profile:lower() then
		AceDB:error("Cannot delete profile %q, it is currently in use.", profile)
	end

	if not (noconfirm or IsShiftKeyDown()) then
		if not StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] then
			StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"] = {}
		end
		local t = StaticPopupDialogs["ACEDB20_CONFIRM_DELETE_DIALOG"]
		t.text = format("%s: %s?", DELETE_PROFILE, profile)
		t.button1 = DELETE_PROFILE
		t.button2 = CANCEL or "Cancel"
		t.OnAccept = function()
			self:DeleteProfile(profile, true)
		end
		t.timeout = 0
		t.whileDead = 1
		t.hideOnEscape = 1

		StaticPopup_Show("ACEDB20_CONFIRM_DELETE_DIALOG")
		return;
	end

	local good = false
	if db.raw.profiles and db.raw.profiles[profile] then
		good = true;
		db.raw.profiles[profile] = nil;
	end

	if db.raw.namespaces then
		for _,n in pairs(db.raw.namespaces) do
			if n.profiles and n.profiles[profile] then
				n.profiles[profile] = nil;
				good = true
			end
		end
	end

	if not good then
		AceDB:error("Cannot delete profile %q, it does not exist.", profile)
	end

	if self['acedb-profile-list'] then
		RecalculateAceDBProfileList(self)
	end
	if self['acedb-profile-copylist'] then
		RecalculateAceDBCopyFromList(self)
	end

	if Dewdrop then
		Dewdrop:Refresh()
	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, defaultProfile in pairs(AceDB.registry) do
		local db = addon.db
		if db then
			if type(addon.OnDatabaseCleanup) == "function" then
				safecall(addon.OnDatabaseCleanup, addon)
			end
			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.server and cleanDefaults(db.server, db.defaults and db.defaults.server) then
				if db.raw.servers then
					db.raw.servers[server] = nil
					if not next(db.raw.servers) then
						db.raw.servers = nil
					end
				end
			end
			if db.faction and cleanDefaults(db.faction, db.defaults and db.defaults.faction) then
				if db.raw.factions then
					db.raw.factions[faction] = nil
					if not next(db.raw.factions) then
						db.raw.factions = 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 defaultProfile 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.server and cleanDefaults(v.server, v.defaults and v.defaults.server) then
							if db.raw.namespaces[name].servers then
								db.raw.namespaces[name].servers[server] = nil
								if not next(db.raw.namespaces[name].servers) then
									db.raw.namespaces[name].servers = nil
								end
							end
						end
						if v.faction and cleanDefaults(v.faction, v.defaults and v.defaults.faction) then
							if db.raw.namespaces[name].factions then
								db.raw.namespaces[name].factions[faction] = nil
								if not next(db.raw.namespaces[name].factions) then
									db.raw.namespaces[name].factions = 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 defaultProfile 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 v:lower() == (defaultProfile or "Default"):lower() 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)
		RecalculateAceDBProfileList(target)
	end
	if not target['acedb-profile-copylist'] then
		target['acedb-profile-copylist'] = setmetatable({}, caseInsensitive_mt)
		RecalculateAceDBCopyFromList(target)
	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 = false,
					set = "CopyProfileFrom",
					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",
				},
				delete = {
					name = DELETE_PROFILE,
					desc = DELETE_PROFILE_DESC,
					usage = DELETE_PROFILE_USAGE,
					type = 'text',
					set = "DeleteProfile",
					get = false,
					validate = target['acedb-profile-copylist'],
					disabled = function()
						return not next(target['acedb-profile-copylist'])
					end,
				},
				reset = {
					name = RESET_PROFILE,
					desc = RESET_PROFILE_DESC,
					type = 'execute',
					func = function()
						target:ResetDB('profile')
					end,
					confirm = true,
				}
			}
		},
	}
end

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

	self.addonsToBeInitialized = oldLib and oldLib.addonsToBeInitialized or {}
	self.addonsLoaded = oldLib and oldLib.addonsLoaded or {}
	self.registry = oldLib and oldLib.registry or {}
	for k, v in pairs(self.registry) do
		if v == true then
			self.registry[k] = "Default"
		end
	end

	self:activate(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, 'server', nil)
			rawset(t.db, 'faction', nil)
			rawset(t.db, 'profile', nil)
			setmetatable(t.db, db_mt)
		end
	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)
