local function vec_zero() return {x=0, y=0, z=0} end
local set_animation = defense_mob_api.library.set_animation
local reg_nodes = minetest.registered_nodes
local set_animation = defense_mob_api.library.set_animation

-- Movement implementations for the default movement types
defense_mob_api.library.move_methods = {}


local move_methods = defense_mob_api.library.move_methods
function move_methods.air(self, dtime, destination)
	local delta = vector.subtract(destination, self.object:get_pos())
	local dist = vector.length(delta)
	if dist ~= dist --nan check
	then
		return
	end
	
	-- Add random variation
	if dist > 3
	then
		local r_angle = (self.id/100000) * 2 * math.pi
		local r_radius = (self.id/100000) * (dist - 3)/3
		delta = vector.add(delta, {
			x = math.cos(r_angle) * r_radius,
			y = r_radius,
			z = math.sin(r_angle) * r_radius
		})
	end
	
	-- Compute speed and smoothing factor
	local speed = self.move_speed * math.max(0, math.min(1, 1.2 * dist))
	local t
	local v = self.object:getvelocity()
	if vector.length(v) < self.move_speed * 1.5
	then
		t = math.pow(0.1, dtime)
	else
		t = math.pow(0.4, dtime)
		speed = speed * 0.9
	end
	
	
	
	
	--[[Note:
		in lua
		x = a and b or c
		means
		if a then x = b else x = c
	]]
	-- Compute and set resulting velocity
	self.object:setvelocity(vector.add(
		vector.multiply(v, t),
		vector.multiply(dist > 0 and vector.normalize(delta) or vec_zero(), speed * (1-t))
	))
	
	if speed > self.move_speed * 0.04
	then
		set_animation(self, "move", {"attack", "move_attack"})
	else
		set_animation(self, "idle", {"attack", "move_attack"})
	end
end

function move_methods.ground(self, dtime, destination)
	local delta = vector.subtract(destination, self.object:get_pos())
	delta.y = 0
	local dist = vector.length(delta)
	
	if dist ~= dist --nan check
	then
		return
	end
	
	-- Add random variation
	if dist > 4
	then
		local r_angle = (self.id/100000) * 2 * math.pi
		local r_radius = (dist - 4)/4
		delta = vector.add(delta,
			{
				x=math.cos(r_angle) * r_radius,
				y=0,
				z=math.sin(r_angle) * r_radius
			})
	end

	-- Compute speed and smoothing factor
	local speed = self.move_speed * math.max(0, math.min(1, 1.2 * dist))
	local t
	local v = self.object:getvelocity()
	if self:is_standing() and vector.length(v) < self.move_speed * 4 then
		t = math.pow(0.001, dtime)
	else
		t = math.pow(0.4, dtime)
		speed = speed * 0.9
	end

	-- Compute and set resulting velocity
	local dir = dist > 0 and vector.normalize(delta) or vec_zero()
	local v2 = vector.add(
		vector.multiply(v, t),
		vector.multiply(dir, speed * (1-t))
	)
	v2.y = v.y
	self.object:setvelocity(v2)

	
	local jump = nil
	local pos = self.object:get_pos()
	
	local would_hit_head = not minetest.line_of_sight(pos,
		vector.add(pos, {x = 0, y = self.jump_height, z = 0}))
	
	-- Check for jump
	if dist > 1 or would_hit_head
	then
		--Jump over obstacles
		local fronts = {}
		
		local p = vector.new(pos)
		p.y = p.y + 0
		local collisionbox = self.object:get_properties().collisionbox
		
		local corners =
		{
			vector.new(collisionbox[1], collisionbox[2], collisionbox[3]),
			vector.new(collisionbox[4], collisionbox[2], collisionbox[6]),
			vector.new(collisionbox[1], collisionbox[2], collisionbox[6]),
			vector.new(collisionbox[4], collisionbox[2], collisionbox[3]),
		}
		
		local dir_corner
		do
			local furthest_corner
			local fav = 0
			for i, c in ipairs(corners)
			do
				local d = vector.distance(c, dir)
				if d > fav
				then
					corner = i
					fav = d
				end
			end
			local furthest = table.remove(corners, furthest_corner)
			for i, c in ipairs(corners)
			do
				if c.x ~= furthest.x and c.z ~= furthest.z
				then
					dir_corner = table.remove(corners, i)
					break
				end
			end
		end
		--dir_corner = vector.add(p, dir_corner)
		for _, c in ipairs(corners)
		do
			-- = vector.add(p, c)
			for iz = c.z, dir_corner.z, (c.z < dir_corner.z) and 1 or -1
			do
				for ix = c.x, dir_corner.x, (c.x < dir_corner.x) and 1 or -1
				do
					table.insert(fronts, vector.add(p,
						{
							x = ix,
							y = 0,
							z = iz,
						}))
				end
			end
		end
		
		
		for _,f in ipairs(fronts)
		do
			local node = minetest.get_node(f)
			
			-- or {walkable = true} snippet to avoid indexing nil values with unknown nodes
			if (reg_nodes[node.name] or {walkable = true}).walkable
			then
				jump = vector.aim(pos, destination)
				break
			end
		end
	elseif destination.y > pos.y + 1
	then
		jump = vector.aim(pos, destination)
	end


	if jump then
		self:jump(jump)
	elseif self.standing then
		if speed > self.move_speed * 0.06 then
			set_animation(self, "move", {"move_attack"})
		else
			set_animation(self, "idle", {"attack", "move_attack"})
		end
	end
end
