do
	local path = minetest.get_modpath('sorcery');
	local get = function(unit)
		return dofile(path .. '/' .. unit .. '.lua')
	end
	local test = function(file) -- :/
		local hnd = io.open(file,'r')
		if hnd == nil then return false else
			hnd:close()
			return true
		end
	end
	local worldcfg = function(str)
		return minetest.get_worldpath() .. '/' .. str
	end
	local cfg = function(str) return worldcfg('sorcery/' .. str) end
	local selfname = minetest.get_current_modname()
	local stage = function(s,...)
		local f = sorcery.cfg(s .. '.lua')
		if test(f) then return loadfile(f)(...) or true end
		return false
	end

	sorcery = {
		self = selfname;
		path = path;
		load = function(name) get(name) end;
		worldcfg = worldcfg, cfg = cfg;
		stage = stage;
		log = function(module,text)
			if module then
				minetest.log('info',string.format('[%s :: %s] %s',selfname,module,text))
			else
				minetest.log('info',string.format('[%s] %s',selfname,text))
			end
		end;
		act = function(module,text)
			minetest.log('action',string.format('[%s :: %s] %s',selfname,module,text))
		end;
		unit = function(ns,sfx,override)
			local target
			if ns then
				sorcery[ns] = {}
				target = sorcery[ns]
			else target = sorcery end
			if override == true then override = ''
				elseif override then override = override .. '-' end
			local loaded = {}
			return function(lst)
				for i,name in pairs(lst) do
					if not loaded[name] then
						loaded[name] = true
						local fpath = ((ns and ns..'/') or '')..name
						local extra = cfg(string.format('%s%s-extra.lua', override,name))
						local replace = cfg(string.format('%s%s.lua', override,name))
						local default = get(fpath)
						if override and test(replace) then
							sorcery.log(name,'loading local replacement for ' .. fpath .. ' from ' .. replace)
							target[name] = loadfile(replace)(default)
						else
							target[name] = default
							if override and test(extra) then
								sorcery.log(name,'loading local extras for ' .. fpath .. ' from ' .. extra)
								local extbl = loadfile(extra)(default)
								for k,v in pairs(extbl) do target[name][k] = v end
							end
						end
					end
				end
			end
		end;
	}
end

-- unfortunately we can't just iterate over the files
-- and load them automatically, as interdependencies
-- exist (especially with /lib) and we need to be very
-- careful about the order they're loaded in

local data = sorcery.unit('data',nil,'lore')
local root = sorcery.unit()
sorcery.stage('bootstrap',data,root)

data {'ui'}
sorcery.unit('lib') {
	-- convenience
	'str';
	-- serialization
	'marshal', 'json';
	-- data structures
	'tbl', 'class';
	-- wrappers
	'color', 'image', 'ui';
	-- game
	'node', 'item';
}

sorcery.stage('worldbuilding',data,root)
root {'compat','matreg'}
if not sorcery.stage('loadlore', data, root) then
	data {
		'compat';
		'affinities'; 'gods';
		'calendar', 'signs';
		'resonance';
		'enchants', 'spells';
		'gems', 'metals';
		'potions', 'oils', 'greases',
			'draughts', 'elixirs',
			'philters', 'extracts';
	}
end

sorcery.load('registration') do
	local exclude = {'compat','ui'}
	for k,v in pairs(sorcery.data) do
		if not sorcery.lib.tbl.has(exclude,k) then
			sorcery.registry.mk(k,v)
		end
	end
end

sorcery.stage('startup',data)
for _,u in pairs {
	'vfx'; 'attunement'; 'context'; 'itemclass';
	'potions'; 'metal', 'gems'; 'leylines'; 'infuser';
	'altar'; 'wands'; 'tools', 'crafttools'; 'enchanter';
	'harvester'; 'metallurgy-hot', 'metallurgy-cold';
	'entities'; 'recipes'; 'coins'; 'interop';
	'tnodes'; 'forcefield'; 'farcaster'; 'portal';
	'cookbook', 'writing'; 'disassembly'; 'displacer';
	'gravitator'; 'precipitator'; 'calendar', 'astrolabe';
	'keypunch';

	'admin';
} do sorcery.load(u) end
sorcery.stage('finalize')

sorcery.registry.defercheck()
