eb_teleport = {}

local teleport_timer = 0
local teleport_start_block
local teleportee
local teleport_destination
local teleport_on_arrive
local teleportee_start_pos

local TELEPORT_TIMEOUT = 3

local TELEPORT_SPEED_UP = 0
local TELEPORT_SEND_GRAVITY = -0.4
local TELEPORT_MAX_SPEED_UP = 7

local TELEPORT_SEND_ABOVE = 15

local PHASE_INACTIVE = 0
local PHASE_SENDING = 1
local teleport_phase = PHASE_INACTIVE

core.register_entity("eb_teleport:attacher", {
	initial_properties = {
		is_visible = false,
		static_save = false,
		physical = false,
		collide_with_objects = false,
		pointable = false,
	},
	on_activate = function(self)
		self.object:set_armor_groups({immortal=1})
		core.log("action", "[eb_teleport] Attacher activates at "..core.pos_to_string(self.object:get_pos(), 1))
	end,
	on_deactivate = function(self)
		core.log("action", "[eb_teleport] Attacher deactivates at "..core.pos_to_string(self.object:get_pos(), 1))
	end,
})


eb_teleport.can_teleport = function(player)
	local vel = player:get_velocity()
	if math.abs(vel.x) < 0.05 and math.abs(vel.z) < 0.05 and vel.y < 0.1 then
		return true
	else
		return false
	end
end

eb_teleport.is_teleporting = function()
	if teleport_phase ~= PHASE_INACTIVE then
		return true
	end
	local player = core.get_player_by_name("singleplayer")
	if not player then
		return false
	end
	if player:get_attach() then
		return true
	else
		return false
	end
end

eb_teleport.teleport_send = function(player, on_arrive)
	teleportee_start_pos = player:get_pos()
	player:set_physics_override({gravity = TELEPORT_SEND_GRAVITY, speed = 0, jump = 0})
	-- TODO: Sound
	teleport_timer = 0
	teleportee = player
	teleport_on_arrive = on_arrive
	teleport_phase = PHASE_SENDING
	core.log("action", "[eb_teleport] Teleporting player (sending phase)!")
end

eb_teleport.set_pos = function(player, destination)
	core.log("action", "[eb_teleport] set_pos to "..core.pos_to_string(destination, 0))
	player:set_pos(destination)

	-- HACK: Attach player to entity to force speed to 0
	-- FIXME: Find a better method (when the engine allows it)
	core.log("action", "[eb_teleport] ADD attacher")
	local ent = core.add_entity(destination, "eb_teleport:attacher")
	if ent then
		core.log("action", "[eb_teleport] added attacher")
		player:set_attach(ent)
		core.after(1, function()
			if not player or not player:is_player() then
				return
			end
			player:set_detach()
			ent:remove()
			core.after(0, function()
				if not player or not player:is_player() then
					return
				end
				player:set_pos(destination)
			end)
			core.log("action", "[eb_teleport] detached attacher")
		end)
	else
		core.log("error", "[eb_teleport] Could not create attacher entity!")
	end
end

local teleport_finalize = function()
	if not teleportee or not teleportee:is_player() then
		teleport_phase = PHASE_INACTIVE
		core.log("error", "[eb_teleport] No teleportee!")
		return
	end

	teleportee:set_physics_override({gravity = 1, speed = 1, jump = 1})
	teleport_phase = PHASE_INACTIVE
	core.log("action", "[eb_teleport] Teleport finalized!")
end

eb_teleport.abort_teleport = function(player)
	player:set_physics_override({gravity = 1, speed = 1, jump = 1})
	teleport_phase = PHASE_INACTIVE
	player:set_detach()
end

core.register_globalstep(function(dtime)
	if teleport_phase == PHASE_INACTIVE then
		return
	end
	teleport_timer = teleport_timer + dtime
	if teleport_phase == PHASE_SENDING then
		if not teleportee or not teleportee:is_player() then
			return
		end
		if not teleportee_start_pos then
			return
		end
		local arriving = false
		if teleportee:get_pos().y >= teleportee_start_pos.y + TELEPORT_SEND_ABOVE then
			arriving = true
		end
		if arriving then
			teleport_destination = teleport_on_arrive(teleportee)
			teleport_phase = PHASE_INACTIVE
			return
		end
		if teleportee:get_velocity().y >= TELEPORT_MAX_SPEED_UP then
			teleportee:set_physics_override({gravity = 0, speed = 0, jump = 0})
		end
	end

	if teleport_timer < TELEPORT_TIMEOUT then
		return
	end
	teleport_timer = 0
	if teleport_phase == PHASE_SENDING then
		teleport_destination = teleport_on_arrive(teleportee)
		teleport_finalize()
		return
	end
end)
