local modname = minetest.get_current_modname()

local on_ground = function(self)
	local collisionbox = self.object:get_properties().collisionbox
	local pos = self.object:get_pos()
	
	for ix = collisionbox[1], collisionbox[4]
	do
		for iz = collisionbox[3], collisionbox[6]
		do
			local node = minetest.get_node({x = pos.x + ix, y = pos.y + collisionbox[2] - 0.1, z = pos.z + iz})
			local def = minetest.registered_nodes[node.name]
			if not def or def.walkable
			then
				return true
			end
		end
	end
	return false
end

local vector_rotate_a = vector.rotate_around_axis
local vector_rotate = vector.rotate
local function rot_to_up(rot)
	return vector_rotate({x = 0, y = 1, z = 0}, rot)
end
local function rot_to_forward(rot)
	return vector_rotate({x = 0, y = 0, z = 1}, rot)
end

local dir_to_rot = vector.dir_to_rotation

local turn_speed = math.pi / 256
local function handle_control(self, dtime)
	local pilot = self.pilot
	if not pilot
	then
		return
	end
	
	local ctrl = pilot:get_player_control()
	if ctrl.jump
	then
		if self.time - self.time_last_switch > 0.5
		then
			self.time_last_switch = self.time
			self.on = not self.on
		end
	elseif ctrl.sneak
	then
		self.pilot:set_detach()
		--self.object:setvelocity({x = 0, y = -9, z = 0})
		self.pilot = nil
	end
	if self.on_ground
	then
		local yaw_change = 0
		if ctrl.LMB or ctrl.left
		then
			yaw_change = math.pi / 128
		end
		if ctrl.RMB or ctrl.right
		then
			yaw_change = -math.pi / 128
		end
		self.object:set_yaw(self.object:get_yaw() + yaw_change)
		return
	end
	
	
	local rot = self.object:get_rotation()
	local forward = rot_to_forward(rot)
	local up = rot_to_up(rot)
	
	local speed = vector.length(self.object:getvelocity()) * dtime
	
	--pitch
	if ctrl.up
	then
		local sideways = vector.normalize(vector.cross(up, forward))
		forward = vector_rotate_a(forward, sideways, -turn_speed * speed)
		up = vector_rotate_a(up, sideways, -turn_speed * speed)
	end
	if ctrl.down
	then
		local sideways = vector.normalize(vector.cross(up, forward))
		forward = vector_rotate_a(forward, sideways, turn_speed * speed)
		up = vector_rotate_a(up, sideways, turn_speed * speed)
	end
	
	--yaw
	if ctrl.LMB
	then
		forward = vector_rotate_a(forward, up, turn_speed * speed)
	end
	if ctrl.RMB
	then
		forward = vector_rotate_a(forward, up, -turn_speed * speed)
	end
	
	--roll
	if ctrl.left
	then
		up = vector_rotate_a(up, forward, -turn_speed * speed)
	end
	if ctrl.right
	then
		up = vector_rotate_a(up, forward, turn_speed * speed)
	end
	
	rot = dir_to_rot(forward, up)
	self.object:set_rotation(rot)
end

local mass = 1

local function handle_physics(self, dtime)
	local rot = self.object:get_rotation()
	local up = rot_to_up(rot)
	local vel = self.object:getvelocity()
	local forward = rot_to_forward(rot)
	
	local vel_length = vector.length(vel)
	vel = vector.normalize(vel)
	vel = vector.add(vel, forward)
	
	if self.on_ground
	then
		if self.on
		then
			vel_length = vel_length == 0 and 0.5 or vel_length * 1.01
		else
			vel_length = vel_length * 0.9
		end
		if vel_length > 5
		then
			vel = vector.add(vel, {x = 0, y = 0.3, z = 0})
		elseif vel_length < 0.1
		then
			vel_length = 0
		end
		vel = vector.add(vel, forward)
		vel = vector.multiply(vector.normalize(vel), vel_length)
		self.object:setvelocity(vel)
		return
	end
	
	local propellation = vector.new(0, 0, 0)
	if self.on and vel_length < 7
	then
		if vector.length(vel) == 0
		then
			vel = forward
		end
		propellation = vector.multiply(forward, 0.1)
	end
	if not self.on
	then
		propellation = {x = 0, y = -0.5, z = 0}
		vel_length = vel_length * 0.95
	end
	vel = vector.add(vel, propellation)
	local gravity = vector.multiply({x = 0, y = -5, z = 0}, vector.angle(up, {x = 0, y = 1, z = 0}) * 0.05)
	vel = vector.add(vel, gravity)
	vel = vector.normalize(vel)
	vel = vector.multiply(vel, math.min(30, vel_length - forward.y * (forward.y < 0 and 0.1 or (self.on and 0.05 or 0.5)) + vector.length(propellation)))
	self.object:setvelocity(vel)
end

minetest.register_entity(modname .. ":airplane",
	{
		initial_properties =
		{
			visual = "mesh",
			mesh = "eg_airplanes_airplane.b3d",
			textures = {"eg_airplanes_airplane.png"},
			backface_culling = false,
			collisionbox = {-1, -1, -1, 1, 0.5, 1},
			physical = true,
			armor_groups = {punch_operable = 1},
		},
		
		time = 0,
		time_last_switch = -1,
		on_step = function(self, dtime)
			self.on_ground = on_ground(self)
			self.time = self.time + dtime
			handle_control(self, dtime)
			handle_physics(self, dtime)
			
			if self.on
			then
				local pos = vector.add(self.object:get_pos(), rot_to_forward(self.object:get_rotation()))
				minetest.add_particlespawner(
					{
						amount = 5,
						time = dtime,
						minpos = pos,
						maxpos = pos,
						minvel = {x = -1, y = -1, z = -1},
						maxvel = {x = 1, y = 1, z = 1},
						texture = "eg_airplanes_smoke.png",
					})
			end
		end,
		
		on_rightclick = function(self, clicker)
			if not self.pilot
			then
				clicker:set_attach(self.object, "", {x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
				self.pilot = clicker
			end
		end,
		
		on_punch = function(self, puncher)
			if not self.pilot
			then
				self.object:remove()
			end
		end,
	})
