-- LUALOCALS < ---------------------------------------------------------
local klots, math, minetest, pairs, vector
    = klots, math, minetest, pairs, vector
local math_atan2, math_pi
    = math.atan2, math.pi
-- LUALOCALS > ---------------------------------------------------------

local modstore = minetest.get_mod_storage()

local vector_equals = vector.equals
local vector_round = vector.round
local hash = minetest.hash_node_position

local ontpt = {}
klots.registered_on_player_teleports = ontpt
function klots.register_on_player_teleport(func)
	ontpt[#ontpt + 1] = func
end

local emerge_radius = 15
emerge_radius = {x = emerge_radius, y = emerge_radius, z = emerge_radius}
local function setfocus_final(player, pos, focpos)
	pos = {x = pos.x, y = pos.y + player:get_properties().eye_height, z = pos.z}
	local diff = vector.subtract(focpos, pos)
	player:set_look_horizontal(math_atan2(diff.z, diff.x) - math_pi / 2)
	player:set_look_vertical(math_atan2(-diff.y, vector.length({x = diff.x, y = 0, z = diff.z})))
end
local function setfocus(player, pos)
	local key = hash(vector_round(pos))
	minetest.emerge_area(
		vector.subtract(pos, emerge_radius),
		vector.add(pos, emerge_radius),
		function(blockpos, _, remain)
			local minp = vector.multiply(blockpos, 16)
			local maxp = vector.add(minp, {x = 15, y = 15, z = 15})
			local found = minetest.find_nodes_in_area(minp, maxp, "group:camera_focus")
			if #found < 1 then
				if remain < 1 then
					local focpos = modstore:get_string(key)
					focpos = focpos and focpos ~= "" and minetest.deserialize(focpos)
					return focpos and setfocus_final(player, pos, focpos)
				end
				return
			end
			local focpos = found[1]
			modstore:set_string(key, minetest.serialize(focpos))
			if not klots.editmode then minetest.remove_node(focpos) end
			return setfocus_final(player, pos, focpos)
		end)
end

local getdata = klots.register_playerstep(function(player, data)
		if not (data.waitpos and data.topos) then return end
		if data.cd and data.cd <= 0 then
			if vector_equals(vector_round(player:get_pos()), vector_round(data.topos)) then
				for _, v in pairs(ontpt) do v(player) end
				local control = data.topos.control
				if control == nil then
					control = data.topos.y >= 10000 and "frozen"
					or data.topos.y >= 128 and "zerograv"
					or false
				end
				klots.player_control_set(player, control)
				klots.flash_screen(player)
				data.cd = nil
				data.waitpos = nil
				data.topos = nil
			else
				klots.player_control_apply(player, function(obj)
						obj:set_pos(data.topos)
						obj:set_velocity({x = 0, y = 0, z = 0})
					end)
				setfocus(player, data.topos)
			end
		else
			data.cd = (data.cd or 3) - 1
			klots.player_control_set(player, "frozen")
			klots.player_control_apply(player, function(obj)
					obj:set_pos(data.waitpos)
					obj:set_velocity({x = 0, y = 0, z = 0})
				end)
			klots.flash_screen(player, true)
		end
	end)

function klots.player_teleport(player, waitpos, topos)
	local data = getdata(player)
	data.waitpos = waitpos
	data.topos = topos
end
