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

-- Defines the edge of a world
local edge = 30000
-- Radius which should be checked for a good teleportation place
local radius = 2
--------------

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...
	}
]]

local function tick()
	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:setpos(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:getpos())

			-- Sanity check for insane coordinates
			if pos.x > 31000 or pos.y > 31000 or pos.z > 31000
				or pos.x < -31000 or pos.y < -31000 or pos.z < -31000 then
				-- Move to spawn asap
				-- The server probably set invalid/insane coordinates. We have not saved the previous ones,
				-- So we need to teleport the player to the spawn to save them from an endless loop of
				-- Teleportation.
				local spawn = minetest.string_to_pos(minetest.setting_get("static_spawnpoint") or "0,0,0")
				minetest.chat_send_player(player:get_player_name(), "An internal error has occured. Your coordinates were corrupted. You are now teleported to the spawn." ..
					" Please report it to any staff member.")
				minetest.log("error", "[WorldEdge] Corrupted position detected for player " .. player:get_player_name())
				player:setpos(spawn)
			else -- Indent skipped, too many lines to change... We'll wait for "continue" to be introduced in Lua5.2

			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}
				if get_surface_pos(newpos) then
					newpos.y = get_surface_pos(newpos).y+1 -- /MFF (Mg|19/05//15)
				end -- /MFF (Mg|14/07/15)
			elseif pos.z <= -edge then
				newpos = {x = pos.x, y = 10, z = newedge}
				if get_surface_pos(newpos) then
					newpos.y = get_surface_pos(newpos).y+1 -- /MFF (Mg|19/05/15)
				end -- /MFF (Mg|14/07/15)
			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:setpos(newpos)
			end
			end
		end
	end
	minetest.after(3, tick)
end
tick()

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
})