map_master.registered_maps = {}

local map_def_prototype =
{
	middle = {x = 0, z = 0},


}
local function assert_vector(vec, msg)
	assert(type(vec.x) == "number" and
		type(vec.y) == "number" and
		type(vec.z) == "number", msg)
end


function map_master.register_map(name, def)
	assert(type(name) == "string",
		"Error registering map: name must be a string")

	assert_vector(def.size,
		"Error registering map: size must be a vector")
	assert(def.size.x > 0 and
		def.size.y > 0 and
		def.size.z > 0,
		"Error registering map: x, y and z of size must be positive")
	assert_vector(def.spawnpoint,
		"Error registering map: spawnpoint must be a vector")
	assert(type(def.path) == "string",
		"Error registering map: Path must be a string")


	local copy = {}
	for k, v in pairs(map_def_prototype)
	do
		copy[k] = v
	end
	for k, v in pairs(def)
	do
		copy[k] = v
	end


	map_master.registered_maps[name] = copy
end





local function set_spawnpoint(map)
	local minpos =
	{
		x = map.middle.x - map.size.x * 8 ,
		y = -19,
		z = map.middle.z - map.size.z * 8,
	}
	local spawn = vector.add(minpos, map.spawnpoint)
	movable_spawnpoint.set_spawnpoint(spawn)
end




local function place_schematics(map, done_callback)

	--defining variables that persist throughout iteration
	--by having them outside the iterating function
	local minpos =
	{
		x = map.middle.x - map.size.x * 8 ,
		y = -19,
		z = map.middle.z - map.size.z * 8,
	}

	local i, ii, iii = 0, 0, 0
	local ix = minpos.x
	local iy = minpos.y
	local iz = minpos.z

	--iterating funciton that will run asynchronously
	local function place_async()
		local success
		for I = 1, 10
		do
			local pos1 = vector.new(ix, iy, iz)
			local filename = map.path..i.."_"..ii.."_"..iii..".mts"
			success = minetest.place_schematic(
				pos1,
				filename,
				nil,
				nil,
				true)

			iii = iii + 1
			iz = iz + 16
			if iii == map.size.z
			then
				iii = 0
				iz = minpos.z
				ii = ii + 1
				iy = iy + 16

				if ii == map.size.y
				then
					ii = 0
					iy = minpos.y
					i = i + 1
					ix = ix + 16
				end
			end
			if i >= map.size.x or not success
			then
				break
			end
		end
		if i < map.size.x and success
		then
			--asynchronous looping
			minetest.after(0.05, place_async)
		elseif done_callback
		then
			done_callback(success)
		end
	end
	place_async()
end


local function place_border_volume(pos1, pos2, vm_buffer)
	local pmin =
	{
		x = math.min(pos1.x, pos2.x),
		y = math.min(pos1.y, pos2.y),
		z = math.min(pos1.z, pos2.z),
	}
	local pmax =
	{
		x = math.max(pos1.x, pos2.x),
		y = math.max(pos1.y, pos2.y),
		z = math.max(pos1.z, pos2.z),
	}
	local cid = minetest.get_content_id("invisible_wall")

	local vm = VoxelManip()
	local emin, emax = vm:read_from_map(pmin, pmax)

	local data = vm:get_data(vm_buffer)
	local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}

	for iz = pmin.z, pmax.z
	do
		for iy = pmin.y, pmax.y
		do
			for ix = pmin.x, pmax.x
			do
				data[area:index(ix, iy, iz)] = cid
			end
		end
	end
	vm:set_data(data)
	vm:write_to_map()
end

local function place_borders(map)
	local min =
	{
		x = map.middle.x - map.size.x * 8 - 1,
		y = -19,
		z = map.middle.z - map.size.z * 8 - 1,
	}
	local max =
	{
		x = map.middle.x + map.size.x * 8,
		y = map.size.y * 16,
		z = map.middle.z + map.size.z * 8,
	}

	--placing sides
	local vm_buffer = {}
	place_border_volume(min, {x = max.x, y = max.y, z = min.z}, vm_buffer)
	place_border_volume(min, {x = min.x, y = max.y, z = max.z}, vm_buffer)

	place_border_volume(max, {x = min.x, y = max.y, z = min.z}, vm_buffer)
	place_border_volume(max, {x = max.x, y = min.y, z = min.z}, vm_buffer)
	place_border_volume(max, {x = min.x, y = min.y, z = max.z}, vm_buffer)

end



--
function map_master.load_map(name, done_callback)
	local map = map_master.registered_maps[name]
	if not map
	then
		return false
	end


	place_schematics(map, done_callback)
	place_borders(map)
	set_spawnpoint(map)

	return true
end
