local registered_levels = {}

cow_levels = {}

local register_level = function(def)
	local path = core.get_modpath("cow_levels").."/schems/"..def.schem
	local rschem = core.read_schematic(path, {write_yslice_prob="none"})
	if not rschem then
		core.log("error", "[cow_levels] Failed to read level schematic for level "..num.."!")
		return
	end
	def.size = rschem.size
	if def.read_objects_from_schematic then
		local i=1
		for z=1, rschem.size.z do
		for y=1, rschem.size.y do
		for x=1, rschem.size.x do
			local nodename = rschem.data[i].name
			if core.get_item_group(nodename, "spawner") ~= 0 then
				local ndef = core.registered_nodes[nodename]
				local pos = vector.new(x,y,z)
				pos = vector.offset(pos, -1, -1, -1)
				if ndef and ndef._cow_spawns then
					table.insert(def.objects, { pos = pos, entityname = ndef._cow_spawns, yaw = 0 })
				end
				rschem.data[i].name = "air"
				rschem.data[i].param2 = 0
			end
			i = i + 1
		end
		end
		end
	end
	def.read_schematic = rschem
	table.insert(registered_levels, def)
end


cow_levels.get_level_by_name = function(name)
	for l=1, #registered_levels do
		if registered_levels[l].name == name then
			return l
		end
	end
end

cow_levels.get_level_def = function(num)
	return registered_levels[num]
end

local build_level = function(num)
	local level = registered_levels[num]
	local path = core.get_modpath("cow_levels").."/schems/"..level.schem
	local rschem = level.read_schematic
	if not rschem then
		core.log("error", "[cow_levels] Failed to load level "..num.."!")
		return
	end
	local size = rschem.size
	local pos1 = level.pos
	local pos2 = vector.add(pos1, size)

	local place_level_schematic = function()
		core.place_schematic(pos1, rschem, "0", {}, true, {})

		local nodes_to_init = core.find_nodes_in_area(pos1, pos2, {"group:needs_level_init"})
		for n=1, #nodes_to_init do
			local node = core.get_node(nodes_to_init[n])
			local def = core.registered_nodes[node.name]
			if def and def._cow_on_level_init then
				core.log("info", "[cow_levels] Initializing node at "..core.pos_to_string(nodes_to_init[n]))
				def._cow_on_level_init(nodes_to_init[n])
			end
		end

		core.log("action", "[cow_levels] Built level "..num)
	end

	local after_emerge = function(blockpos, action, calls_remaining, param)
		if (action == core.EMERGE_FROM_MEMORY or action == core.EMERGE_FROM_DISK or action == core.EMERGE_GENERATED)
				 and calls_remaining == 0 then
			core.log("action", "[cow_levels] Level area for level "..num.." emerged")
			place_level_schematic()
		elseif action == core.EMERGE_CANCELLED then
			core.log("error", "[cow_levels] Emerging of level "..num.." cancelled! Level could not be generated")
		elseif action == core.EMERGE_ERRORED then
			core.log("error", "[cow_levels] Error while emerging level "..num.."!")
		end
	end
	core.log("action", "[cow_levels] Starting to emerge area for level "..num.."!")
	core.emerge_area(pos1, pos2, after_emerge)
end

cow_levels.build_levels = function()
	for l=1, #registered_levels do
		build_level(l)
	end
end

-- Dummy level for the main menu backdrop.
local level_menu = {
	-- This represents a "cow laboratory"
	name = "menu_lab",
	pos = vector.new(965, -16, -155),
	schem = "cow_levels_menu_lab.mts",
	spawn_offset = vector.new(18.5, 1.5, 6.5),
	objects = {
		{ entityname = "cow_objects:player_dummy", pos = vector.new(18.5, 1.5, 20.5), yaw = math.pi },
		{ entityname = "cow_objects:player_dummy", pos = vector.new(8.5, 1.5, 20.5), yaw = math.pi },
		{ entityname = "cow_objects:player_dummy", pos = vector.new(8.5, 1.5, 7.5), yaw = 0 },
	},
	yaw = 0,
	pitch = 0.64525,
}
-- Town level (very large!)
local level_town = {
	name = "town",
	pos = vector.new(939, 1, -218),
	schem = "cow_levels_town.mts",
	spawn_offset = vector.new(41, 11, 100),
	objects = {},
	read_objects_from_schematic = true,
	yaw = 0,
	pitch = 0,
}
core.register_on_mods_loaded(function()
	register_level(level_menu)
	register_level(level_town)
end)

cow_levels.level_exists = function(num)
	return registered_levels[num] ~= nil
end
cow_levels.get_level_count = function(num)
	return #registered_levels
end

cow_levels.init_level = function(num)
	local level = registered_levels[num]
	local objs = level.objects

	if not objs then
		return
	end
	local pos1 = level.pos
	for o=1, #objs do
		local i_obj = objs[o]
		local pos = vector.add(pos1, i_obj.pos)
		local obj = core.add_entity(pos, i_obj.entityname)
		if obj and i_obj.yaw then
			obj:set_yaw(i_obj.yaw)
		end
	end
end

cow_levels.get_level_spawn_pos = function(num)
	local level = registered_levels[num]
	local pos = vector.add(level.pos, level.spawn_offset)
	return pos
end

cow_levels.go_to_level = function(player, num, no_camera_change)
	local pos = cow_levels.get_level_spawn_pos(num)
	player:set_pos(pos)
	if no_camera_change ~= false then
		local level = registered_levels[num]
		player:set_look_horizontal(level.yaw or 0)
		player:set_look_vertical(level.pitch or 0)
	end
end

-- Generate levels, but only if NOT in Creative Mode!
if not core.is_creative_enabled("") then
	core.after(0, function()
		cow_levels.build_levels()
	end)
end
