--------------
-- TODO: Check for terrain height

-- Defines the edge of a world
local edge = tonumber(minetest.settings:get("world_edge")) or 30000
-- Radius which should be checked for a good teleportation place
local radius = 2
--------------

if minetest.settings:get_bool("log_mods") then
	minetest.log("action", "World edge: " .. edge)
end

local count = 0
local waiting_list = {}
--[[ Explanation of waiting_list table
	Index = Player name
	Value = {
		player = Player to teleport
		pos = Destination
		obj = Attacked entity
		notified = When the player must wait longer...
	}
]]

minetest.register_globalstep(function(dtime)
	count = count + dtime
	if count < 3 then
		return
	end
	count = 0

	for k, v in pairs(waiting_list) do
		if v.player and v.player:is_player() then
			local pos = get_surface_pos(v.pos)
			if pos then
				v.obj:set_pos(pos)
				minetest.after(0.2, function(p, o)
					p:set_detach()
					o:remove()
				end, v.player, v.obj)
				waiting_list[k] = nil
			elseif not v.notified then
				v.notified = true
				minetest.chat_send_player(k, "Sorry, we have not found a free place yet. Please be patient.")
			end
		else
			v.obj:remove()
			waiting_list[k] = nil
		end
	end

	local newedge = edge - 5
	-- Check if the players are near the edge and teleport them
	local players = minetest.get_connected_players()
	for i, player in ipairs(players) do
		local name = player:get_player_name()
		if not waiting_list[name] then
			local pos = vector.round(player:get_pos())
			local newpos = nil
			if pos.x >= edge then
				newpos = {x = -newedge, y = 10, z = pos.z}
			elseif pos.x <= -edge then
				newpos = {x = newedge, y = 10, z = pos.z}
			end

			if pos.z >= edge then
				newpos = {x = pos.x, y = 10, z = -newedge}
			elseif pos.z <= -edge then
				newpos = {x = pos.x, y = 10, z = newedge}
			end

			-- Teleport the player
			if newpos then
				minetest.chat_send_player(name, "Please wait a few seconds. We will teleport you soon.")
				local obj = minetest.add_entity(newpos, "worldedge:lock")
				player:set_attach(obj, "", {x=0, y=0, z=0}, {x=0, y=0, z=0})
				waiting_list[name] = {
					player = player,
					pos = newpos,
					obj = obj
				}
				obj:set_pos(newpos)
			end
		end
	end
end)

function get_surface_pos(pos)
	local minp = {
		x = pos.x - radius - 1,
		y = -10,
		z = pos.z - radius - 1
	}
	local maxp = {
		x = pos.x + radius - 1,
		y = 50,
		z = pos.z + radius - 1
	}

	local c_air = minetest.get_content_id("air")
	local c_ignore = minetest.get_content_id("ignore")

	local vm = minetest.get_voxel_manip()
	local emin, emax = vm:read_from_map(minp, maxp)
	local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
	local data = vm:get_data()

	local seen_air = false
	local deepest_place = vector.new(pos)
	deepest_place.y = 50

	for x = minp.x, maxp.x do
	for z = minp.z, maxp.z do
		local solid = 0
		for y = deepest_place.y, -10, -1 do
			local node = data[area:index(x, y, z)]
			if y < deepest_place.y and node == c_air then
				deepest_place = vector.new(x, y, z)
				seen_air = true
			end
			if solid > 5 then
				-- Do not find caves!
				break
			end
			if node ~= c_air and node ~= c_ignore then
				solid = solid + 1
			end
		end
	end
	end

	if seen_air then
		return deepest_place
	else
		return false
	end
end

minetest.register_entity("worldedge:lock", {
	initial_properties = {
		is_visible = false
	},
	on_activate = function(staticdata, dtime_s)
		--self.object:set_armor_groups({immortal = 1})
	end
})
