-- LUALOCALS < ---------------------------------------------------------
local error, get_mod_api, ipairs, minetest, pairs, piredo_player,
      posrel, startswith, string, vector
    = error, get_mod_api, ipairs, minetest, pairs, piredo_player,
      posrel, startswith, string, vector
local string_format, string_match, string_sub
    = string.format, string.match, string.sub
-- LUALOCALS > ---------------------------------------------------------

local modname = minetest.get_current_modname()
local api = get_mod_api()

local ambnode = modname .. ":ambient"
minetest.register_node(ambnode, {
		drawtype = "airlike",
		pointable = false,
		walkable = false,
		sunlight_propagates = true,
		paramtype = "light",
		light_source = 3,
		buildable_to = true,
		air_equivalent = true,
	})
function minetest.remove_node(pos)
	return minetest.set_node(pos, {name = ambnode})
end

local allschematics = {}
api.allschematics = allschematics
do
	local modpath = minetest.get_modpath(modname)
	local schempath = modpath .. "/schematics"
	for _, fn in pairs(minetest.get_dir_list(schempath, false)) do
		if string_sub(fn, -4) == ".mts" then
			allschematics[string_sub(fn, 1, -5)] = {
				mts = minetest.register_schematic(schempath .. "/" .. fn,
					{air = ambnode})
			}
		end
	end
end
allschematics.normal_garden.pos = vector.new(0, -1, 0)
allschematics.large_graveyard.pos = vector.new(0, -22, 0)
allschematics.special_final_room.pos = vector.new(0, -36, 0)
allschematics.special_final_room_2.pos = vector.new(0, -36, 0)

allschematics.large_forrest_maze.daytime = 0.5
allschematics.large_graveyard.daytime = 0.79
allschematics.large_lake.daytime = 0.5
allschematics.large_tower.daytime = 0.79
allschematics.normal_garden.daytime = 0.5
allschematics.normal_observatory.daytime = 0
allschematics.special_final_room.daytime = 0.3
allschematics.special_final_room_2.daytime = 0.3

allschematics.special_blocked.disallow_changes = true
allschematics.outside.disallow_changes = true
allschematics.outside_ruined.disallow_changes = true

allschematics.special_blocked.noscore = true
allschematics.outside.noscore = true
allschematics.outside_ruined.noscore = true
allschematics.special_final_room_2.noscore = true

do
	local depth = 36
	local height = 28
	local void = {name = modname .. ":void", force_place = true}
	local air = {name = "air", force_place = true}
	for _, xz in ipairs({20, 40}) do
		local data = {}
		for _ = 1, xz do
			for y = 1, depth + height do
				for _ = 1, xz do
					data[#data + 1] = (y > depth) and air or void
				end
			end
		end
		allschematics["clear_" .. xz] = {
			pos = vector.new(0, -depth, 0),
			mts = minetest.register_schematic({
					size = vector.new(xz, depth + height, xz),
					data = data
			})}
	end
end

-- N.B. There is a missing tree in the "outside ruined" ending area that allows
-- a player to see the old mansion below, and jump out and back into the map.
-- This should be fixed in the schematic, eventually, but for now, just hack in
-- a fix around it.
local raw_set_node = minetest.set_node
local function hotfix_helper(pos, roomdata, ...)
	if roomdata == allschematics.outside_ruined then
		local rel = posrel(pos)
		raw_set_node(rel(0, 1, 37), {name = "piredo_terrain:default__tree"})
		raw_set_node(rel(0, 2, 37), {name = "piredo_terrain:default__tree"})
	end
	return ...
end

function api.putschem(pos, room)
	local roomdata = allschematics[room] or error(string_format("room %q not found", room))
	if roomdata.pos then pos = vector.add(pos, roomdata.pos) end
	api.statedb.loaded = api.statedb.loaded or {}
	api.statedb.loaded[minetest.hash_node_position(pos)] =
	(not string_match(room, "^clear_")) and {pos = pos, room = room} or nil
	api.savedb()
	return hotfix_helper(pos, roomdata, minetest.place_schematic(pos, roomdata.mts))
end

function api.clearschems()
	if not api.statedb.loaded then return end
	local ppos = piredo_player.mainplayer():get_pos()
	for k, v in pairs(api.statedb.loaded) do
		local size = (string_match(v.room, "^large_")
			or string_match(v.room, "^outside")) and 40 or 20
		if ppos.x < (v.pos.x - 0.5)
		or ppos.z < (v.pos.z - 0.5)
		or ppos.x > (v.pos.x + size + 0.5)
		or ppos.z > (v.pos.z + size + 0.5)
		then
			api.putschem(v.pos, "clear_" .. size)
			api.statedb.loaded[k] = nil
		end
	end
	api.savedb()
end

function api.find_schematics(prefix)
	local t = {}
	for k in pairs(allschematics) do
		if startswith(k, prefix) then
			t[#t + 1] = k
		end
	end
	return t
end
