if not sorcery then sorcery = {DEBUG=true} end
sorcery.register = {}
sorcery.registry = {
	defercheck = function() return true end
	-- used to warn about deferments that have not been discharged by a certain threshold
}
sorcery.registry.mk = function(name,db)
	local reg = {}
	if db == nil then
		-- predefined db
		db = sorcery.data[name]
	elseif db == false then -- auxiliary db, stored in registry itself
		reg.db = {}
		db = reg.db
	end
	local dat = {
		iters = {};
		state = {};
		defer = {};
	}
	reg.invoke = function(fnid,tgtid)
		local fno = dat.iters[fnid]
		if not fno then return false end

		local runid = fnid .. '@' ..tgtid
		if dat.state[runid] then return true end

		if fno.deps then for k,f in pairs(fno.deps) do
			if reg.invoke(f,tgtid) == false then return false end
		end end
		
		fno.fn(tgtid, db[tgtid])

		dat.state[runid] = true
		return true
	end
	reg.foreach = function(ident,deps,fn)
		dat.iters[ident] = {deps = deps, fn = fn}
		for k in pairs(db) do
			if reg.invoke(ident,k) == false then
				-- not all dependencies are available
				-- to run yet; must defer until then
				dat.defer[#dat.defer+1] = ident
				return false
			end
		end
		for i,dfn in pairs(dat.defer) do
			local deferred = dat.iters[dfn]
			for _,d in pairs(deferred.deps) do
				if not dat.iters[d] then goto skipdfmt end
			end
			-- a deferred function can now be run, do so
			table.remove(dat.defer,i)
			reg.foreach(dfn,deferred.deps,deferred.fn)
		::skipdfmt::end

		return true
	end
	reg.link = function(key,value)
		-- support simple arrays as well as kv stores
		if value == nil then
			value = key
			key = #db+1
		end
		db[key] = value
		for id in pairs(dat.iters) do 
			reg.invoke(id,key)
		end
	end
	reg.meld = function(tbl)
		for k,v in pairs(tbl) do reg.add(k,v) end
	end
	sorcery.register[name] = reg

	local nextfn = sorcery.registry.defercheck
	sorcery.registry.defercheck = function()
		if #dat.defer ~= 0 then
			print('WARNING: ' .. tostring(#dat.defer) .. ' deferred iterator(s) have not yet been discharged for registry “' .. name .. '”')
			local log = sorcery.log and function(text)
				sorcery.log('registry',text)
			end or print
			for i,v in pairs(dat.defer) do
				log('\t' .. tostring(i) .. ') ' .. v)
			end
			log('there is likely a missing dependency or dependency ordering problem. also make sure you have spelled the names of the iterator dependencies correctly')
			return false
		end
		nextfn()
	end
	return reg
end

if sorcery.DEBUG then
	local function dump(tbl,indent)
		indent = indent or 0
		local space = string.rep(' ',indent*4)
		for k,v in pairs(tbl) do
			if type(v) == 'table' then
				print(string.format('%s%s = {',space,k))
				dump(v,indent + 1)
				print(string.format('%s}', space))
			else
				print(string.format('%s%s = %q',space,k,tostring(v)))
			end
		end
	end 

	local metals = {
		oregonium = {desc = 'Oregonium'};
		nevadite = {desc = 'Nevadite'};
	}
	local myreg = sorcery.registry.mk('metals',metals)

	sorcery.register.metals.link('californium', {
		desc = "Californium";
	})

	sorcery.register.metals.foreach('sorcery:mkingot',{'sorcery:mkfrag'}, function(k,v)
		local ingot = 'sorcery:' .. k .. '_ingot'
		print('registered',ingot)
		v.parts = v.parts or {}
		v.parts.ingot = ingot;
	end)

	sorcery.registry.defercheck()

	sorcery.register.metals.link('washingtonium', {
		desc = "Washingtonium";
	})

	sorcery.register.metals.foreach('sorcery:mkfrag',{}, function(k,v)
		local fragment = 'sorcery:' .. k .. '_fragment'
		print('registered',fragment)
		v.parts = v.parts or {}
		v.parts.fragment = fragment;
	end)

	sorcery.register.metals.link('biloxite', {
		desc = "Biloxite";
	})
	
	dump(metals)
	if sorcery.registry.defercheck() then
		print('lingering deferments!')
	else
		print('all good!')
	end
end
