-- LUALOCALS < ---------------------------------------------------------
local klots, minetest, pairs, setmetatable, vector
    = klots, minetest, pairs, setmetatable, vector
-- LUALOCALS > ---------------------------------------------------------

local leveldata = {}
klots.leveldata = leveldata

-- Current total number of levels.
local total_levels = 7

-- Populate standard allocation.
local offset = 8
local marginmin = 32 + 80
local marginmax = 47 + 80
local spacing = 960
local height = 320

do
	local dirs = {
		{x = 1, y = 0, z = 0},
		{x = 1, y = 0, z = 1},
		{x = 0, y = 0, z = 1},
		{x = -1, y = 0, z = 1},
		{x = -1, y = 0, z = 0},
		{x = -1, y = 0, z = -1},
		{x = 0, y = 0, z = -1},
		{x = 1, y = 0, z = -1},
	}
	local queue = {{x = 0, y = 0, z = 0}}
	local seen = {}
	local hash = minetest.hash_node_position
	local idx = 1
	while #leveldata < total_levels do
		local pos = queue[idx]
		idx = idx + 1
		local key = hash(pos)
		if not seen[key] then
			seen[key] = true
			local lv = #leveldata + 1
			leveldata[lv] = {
				pos = {
					x = pos.x * spacing + offset,
					y = height + offset - 0.49,
					z = pos.z * spacing + offset,
				},
				minp = {
					x = pos.x * spacing - marginmin,
					y = height - marginmin,
					z = pos.z * spacing - marginmin,
				},
				maxp = {
					x = pos.x * spacing + marginmax,
					y = height + marginmax,
					z = pos.z * spacing + marginmax,
				},
				nextlv = (lv == total_levels) and "win" or (lv + 1)
			}
			for i = 1, #dirs do
				queue[#queue + 1] = vector.add(pos, dirs[i])
			end
		end
	end
end

-- Put "win" in as a pseudo-level for convenience of using "win" as a
-- "next level key" but without this "level" being part of the
-- enumeration.
local levelmeta = {}
setmetatable(leveldata, {__index = levelmeta})
levelmeta.win = {
	pos = {x = 0, y = 20000, z = 0},
	minp = {x = 0, y = 20000, z = 0},
	maxp = {x = 0, y = 20000, z = 0},
	nextlv = "win"
}

function klots.level_from_pos(pos)
	if pos.y > 10000 then return "win", levelmeta.win end
	local bestdsqr, bestlv, bestdata
	for lv, data in pairs(leveldata) do
		-- Ignore y coordinate since it's effectively a 2D grid
		local dsqr = (data.pos.x - pos.x) ^ 2 + (data.pos.z - pos.z) ^ 2
		if (not bestdsqr) or (dsqr < bestdsqr) then
			bestdsqr = dsqr
			bestlv = lv
			bestdata = data
		end
	end
	return bestlv, bestdata
end

------------------------------------------------------------------------
-- EXCEPTIONS

-- Move level 1 down to the surface, adjust starting point so
-- the off-center start fits in 1 mapchunk.
leveldata[1].pos = {x = 7, y = 8.51, z = 1}
leveldata[1].minp.y = -marginmin
leveldata[1].maxp.y = marginmax
