-- Based on: https://github.com/minetest/minetest_game/blob/master/mods/boats/init.lua

local function get_velocity(v, yaw, y)
	local x = -math.sin(yaw) * v
	local z =  math.cos(yaw) * v
	return {x = x, y = y, z = z}
end

local function get_v(v, speed)
	return math.sqrt(v.x ^ 2 + v.z ^ 2) * speed
end

local class = {
	driver = nil,
	v = 0,
	last_v = 0,
	removed = false,
	auto = false,
	speed = 1
}

function class:on_rightclick(clicker)
	if not clicker or not clicker:is_player() then
		return
	end

	local name = clicker:get_player_name()
	if self.driver and name == self.driver then
		clicker:set_detach()

		local pos = clicker:get_pos()
		pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
		core.after(0.1, function()
			clicker:set_pos(pos)
		end)
	elseif not self.driver then
		clicker:set_attach(self.object, "",
		{x = 0.5, y = 1, z = -3}, {x = 0, y = 0, z = 0})

		self.driver = name

		clicker:set_look_horizontal(self.object:get_yaw())
	end
end

function class:on_detach_child(child)
	if child and child:get_player_name() == self.driver then
		self.driver = nil
		self.auto = false
	end
end

function class:on_activate(staticdata, dtime_s)
	self.object:set_armor_groups({immortal = 1})
	if staticdata then
		self.v = tonumber(staticdata)
	end
	self.last_v = self.v
end

function class:get_staticdata()
	return tostring(self.v)
end

function class:on_punch(puncher)
	if not puncher or not puncher:is_player() or self.removed then
		return
	end

	local name = puncher:get_player_name()
	if self.driver and name == self.driver then
		self.driver = nil
		puncher:set_detach()
	end
	if not self.driver then
		self.removed = true
		local inv = puncher:get_inventory()
		if not core.is_creative_enabled(name)
			or not inv:contains_item("main", self.name) then

			local leftover = inv:add_item("main", self.name)
			if not leftover:is_empty() then
				core.add_item(self.object:get_pos(), leftover)
			end
		end

		core.after(0.1, function()
			self.object:remove()
		end)
	end
end

function class:on_step(dtime)
	self.v = get_v(self.object:get_velocity(), self.speed) * math.sign(self.v)
	if self.driver then
		local driver_objref = core.get_player_by_name(self.driver)
		if driver_objref then
			local ctrl = driver_objref:get_player_control()
			if ctrl.up and ctrl.down then
				if not self.auto then
					self.auto = true
					core.chat_send_player(self.driver, Translate("Auto mode on"))
				end
			elseif ctrl.down then
				self.v = self.v - dtime * 2.0
				if self.auto then
					self.auto = false
					core.chat_send_player(self.driver, Translate("Auto mode off"))
				end
			elseif ctrl.up or self.auto then
				self.v = self.v + dtime * 2.0
			end
			if ctrl.left then
				if self.v < -0.001 then
					self.object:set_yaw(self.object:get_yaw() - dtime * 0.9)
				else
					self.object:set_yaw(self.object:get_yaw() + dtime * 0.9)
				end
			elseif ctrl.right then
				if self.v < -0.001 then
					self.object:set_yaw(self.object:get_yaw() + dtime * 0.9)
				else
					self.object:set_yaw(self.object:get_yaw() - dtime * 0.9)
				end
			end
		end
	end
	local velo = self.object:get_velocity()
	if not self.driver and
		self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
		self.object:set_pos(self.object:get_pos())
		return
	end
	-- We need to preserve velocity sign to properly apply drag force
	-- while moving backward
	local drag = dtime * math.sign(self.v) * (0.01 + 0.0796 * self.v * self.v)
	-- If drag is larger than velocity, then stop horizontal movement
	if math.abs(self.v) <= math.abs(drag) then
		self.v = 0
	else
		self.v = self.v - drag
	end

	local p = self.object:get_pos()
	p.y = p.y - 0.5
	local new_velo
	local new_acce = {x = 0, y = 0, z = 0}
	p.y = p.y + 1
	local y = self.object:get_velocity().y
	if y >= 5 then
		y = 5
	end
	new_velo = get_velocity(self.v, self.object:get_yaw(), y, self.speed)
	self.object:set_pos(self.object:get_pos())
	self.object:set_velocity(new_velo)
	self.object:set_acceleration(new_acce)
end

PyuTest.create_vehicle = function(name, desc, speed, properties, image, craft)
	local options = setmetatable(properties, { __index = class})
	options.name = name
	options.speed = speed

	core.register_entity(name, properties)

	PyuTest.make_item(name, desc, {
		vehicle = 1
	}, image, {
		liquids_pointable = true,
		on_place = function (itemstack, placer, pointed_thing)
			local under = pointed_thing.under
			local node = core.get_node(under)
			local udef = core.registered_nodes[node.name]
			if udef and udef.on_rightclick and
				not (placer and placer:is_player() and
				placer:get_player_control().sneak) then
				return udef.on_rightclick(under, node, placer, itemstack,
				pointed_thing) or itemstack
			end

			if pointed_thing.type ~= "node" then
				return itemstack
			end

			pointed_thing.under.y = pointed_thing.under.y + 0.5
			local vehicle = core.add_entity(pointed_thing.under, name)
			if vehicle then
				if placer then
					vehicle:set_yaw(placer:get_look_horizontal())
				end
				local player_name = placer and placer:get_player_name() or ""
				if not core.is_creative_enabled(player_name) then
					itemstack:take_item()
				end
			end
			return itemstack
		end
	})

	if craft then
		core.register_craft({
			output = name,
			recipe = craft
		})
	end
end
