local lib = LibStub:NewLibrary("LibConfig-1.0", 2)
if not lib then return end
local reg = LibStub("AceConfigRegistry-3.0")
lib.data = lib.data or {}

function lib:CreateOptions(name, args, handler, slash, dbName, defaults, defaultName)
	local e = "Usage: CreateOptionsTable(\"name\", args, profile, handler): "
	assert(self ~= lib, e.."do not use LibConfig:CreateOptionsTable(), use your own 'self'")
	assert(type(args) == "table", e.."'args' - expected table")
	local o, db, dlg = {
		name = name,
		type = "group",
		handler = self,
		childGroups = "tab",
		inline = true,
		get = "GetOption",
		set = "SetOption",
		args = args
	}
	reg:RegisterOptionsTable(name, o)
	dlg = LibStub("AceConfigDialog-3.0"):AddToBlizOptions(name, name)
	lib.data[self] = {handler = handler, dlg = dlg, name = name}

	if slash then
		assert(type(slash) == "string", e.."slash - expected string")
		local function ChatCommand(c)
			if c == "" then InterfaceOptionsFrame_OpenToCategory(lib.data[self].dlg)
			else LibStub("AceConfigCmd-3.0"):HandleCommand(slash, name, c)
			end
		end
		LibStub("AceConsole-3.0"):Embed(self)
		self:RegisterChatCommand(slash, ChatCommand)
	end
	if dbName then
		local function OnProfileChanged(event, db, name)
			lib.data[self].profile = db.profile
			if self.OnProfileChanged then self:OnProfileChanged(event, db, name) end
			self:ApplySettings()
		end
		db = LibStub("AceDB-3.0"):New(dbName, defaults, defaultName or "Default")
		db:RegisterCallback("OnProfileChanged", OnProfileChanged)
		db:RegisterCallback("OnProfileReset", OnProfileChanged)
		o.args.profiles = LibStub("AceDBOptions-3.0"):GetOptionsTable(db)
		self.db = db
		OnProfileChanged("OnProfileChanged", db, db.keys.profile)
	end
end

function lib:GetOption(path, i)
	local p = lib.data[self].profile
	for i = 1, #path - 1 do
		if p[path[i]] then p = p[path[i]] end
	end
	p = p[path[#path]]
	if i then return p[i] else return p end	--return i and p[i] or p: wrong!
end

function lib:SetOption(path, i, v)
	local p, h = lib.data[self].profile, lib.data[self].handler
	for i = 1, #path - 1 do
		if p[path[i]] then
			p = p[path[i]]
			h = h and h[path[i]]
		end
	end
	if v == nil then p[path[#path]], v = i, path, nil
	else p[path[#path]][i] = v
	end
	h = h and h[path[#path]]
	if h then
		if type(h) == "table" then h.func(i, v, path)
		else h(i, v, path)
		end
	end
end

function lib:ExecuteHandler(handler, profile, path)
	if type(handler) == "table" then
		for i, v in pairs(handler) do
			tinsert(path, i)
			self:ExecuteHandler(v, profile[i], path)
			tremove(path)
		end
	elseif type(profile) == "table" then
		for i, v in pairs(profile) do
			handler(i, v, path)
		end
	else handler(profile, path)
	end
end

function lib:ApplySettings()
	local d = lib.data[self]
	self:ExecuteHandler(d.handler, d.profile, {})
end

function lib:NotifyChange()
	reg:NotifyChange(lib.data[self].name)
end

local mixin = {"CreateOptions", "GetOption", "SetOption",
	"ExecuteHandler", "ApplySettings", "NotifyChange"}
function lib:Embed(target)
	for i, v in pairs(mixin) do
		if target[v] ~= lib[v] then target[v] = lib[v] end
	end
	if not lib.data[target] then lib.data[target] = true end
end

--Upgrade old lib
for a in pairs(lib.data) do
	lib:Embed(a)
end
