-- LUALOCALS < ---------------------------------------------------------
local klots, math, minetest, string, vector
    = klots, math, minetest, string, vector
local math_cos, math_pi, math_random, string_rep
    = math.cos, math.pi, math.random, string.rep
-- LUALOCALS > ---------------------------------------------------------

local modname = minetest.get_current_modname()

local nodename = modname .. ":teleporter"
local particlename = modname .. ":teleporter_particles"

minetest.register_node(particlename,
	{tiles = {modname .. "_teleparticles.png"},
		groups = {not_in_creative_inventory = 1}
	})

local function porticles(pos)
	local cpos = {x = pos.x, y = pos.y + 0.5, z = pos.z}
	return minetest.add_particlespawner({
			time = 1,
			amount = 25,
			exptime = 1,
			size = {
				min = 1,
				max = 3,
				bias = 1
			},
			collisiondetection = false,
			node = {name = particlename},
			pos = cpos,
			radius = {
				min = 1,
				max = 3,
				bias = 1
			},
			attract = {
				kind = "point",
				strength = {
					min = 1,
					max = 5,
					bias = 1
				},
				origin = cpos
			}
		})
end

local blackcube = "[inventorycube" .. string_rep("{[combine:1x1&[noalpha", 3)
minetest.register_node(nodename, {
		description = "Level End Portal",
		drawtype = "airlike",
		walkable = false,
		paramtype = "light",
		sunlight_propagates = true,
		pointable = klots.editmode or false,
		inventory_image = blackcube,
		wield_image = blackcube,
		groups = {
			ambiance = 100,
			auto_timer = 1
		},
		sounds = {
			ambiance = {
				name = modname .. "_portal",
				gain = 0.2,
				max_hear_distance = 12
			}
		},
		on_timer = function(pos)
			minetest.get_node_timer(pos):start(1)
			return porticles(pos)
		end,
		on_construct = function(pos)
			minetest.get_node_timer(pos):start(0.001)
		end
	})

if not klots.editmode then
	klots.register_playerstep(function(player, data, dtime)
			local pos = player:get_pos()

			local found = data.tpos
			if not (found and minetest.get_node(found).name == nodename) then
				found = minetest.find_node_near(pos, 3, nodename, true)
				if not found then return end
				found.y = found.y - 0.49
				data.tpos = found
			end

			if vector.distance(pos, found) > 5 then
				data.tpos = nil
				return
			end

			local _, lvdata = klots.level_from_pos(pos)
			local topos = klots.leveldata[lvdata.nextlv].pos
			if not topos then return end

			if vector.distance(pos, found) < 1 then
				data.tpos = nil
				return klots.player_teleport(player, found, topos)
			end

			klots.player_control_set(player, "zerognofly")
			klots.player_control_apply(player, function(obj)
					local dv = vector.subtract(found, pos)
					dv = vector.multiply(dv, dtime * 10)
					local vel = obj:get_velocity()
					dv = vector.add(dv, vector.multiply(vel,
							(0.5 ^ dtime) - 1))
					obj:add_velocity(dv)
				end)
		end)

	local tau = math_pi * 2
	klots.register_on_player_teleport(function(player, from)
			local pos = player:get_pos()
			if from.y < 128 and pos.y < 128 then return end

			minetest.sound_play(modname .. "_teleport", {
					pos = from,
					gain = 1
				}, true)

			minetest.sound_play(modname .. "_teleport", {
					pos = pos,
					gain = 1
				}, true)

			pos.y = pos.y + player:get_properties().eye_height
			local theta = math_random() * tau
			for i = 1, 3 do
				local p = {
					x = pos.x + math_cos(theta + i * tau / 3),
					y = pos.y,
					z = pos.z + math_cos(theta + i * tau / 3),
				}
				minetest.sound_play(modname .. "_teleport", {
						pos = p,
						gain = 0.5,
						pitch = 0.9 + math_random() * 0.2,
						to_player = player:get_player_name()
					}, true)
			end
		end)
end
