local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3

---
-- @module worldeditadditions

-- ███████ ██████  ██      ██ ███    ██ ███████ 
-- ██      ██   ██ ██      ██ ████   ██ ██      
-- ███████ ██████  ██      ██ ██ ██  ██ █████   
--      ██ ██      ██      ██ ██  ██ ██ ██      
-- ███████ ██      ███████ ██ ██   ████ ███████ 

--- Creates a spline that follows the given list of points.
-- @param	pos_list		Vector3[]	The list of points that define the path the spline should follow. Minimum: 2
-- @param	width_start		number	The width of the spline at the start.
-- @param	width_end		number	The width of the spline at the end. If nil, then width_start is used.
-- @param	steps			number		The number of smoothing passes to apply to the list of points.
-- @param	target_node		Vector3		The *normalised* name of the node to use to build the square spiral with.
-- @returns	bool,number|string			A success boolean value, followed by either the number of the nodes set or an error message string.
function worldeditadditions.spline(pos_list, width_start, width_end, steps, target_node)
	if #pos_list < 2 then return false, "Error: At least 2 positions are required to make a spline." end
	if not width_end then width_end = width_start end
	
	---
	-- 0: Find bounding box
	---
	-- We can't use wea_c.pos.get_bounds 'cause that requires a player name
	local pos1, pos2 = pos_list[1], pos_list[2]
	for _, pos in pairs(pos_list) do
		pos1, pos2 = Vector3.expand_region(pos, pos1, pos2)
	end
	local max_width = math.max(width_start, width_end)
	pos1 = pos1 - max_width
	pos2 = pos2 + max_width
	
	
	---
	-- 1: Interpolate points & widths
	---
	local pos_list_chaikin = wea_c.chaikin(pos_list, steps)
	
	local widths_lerped = {}
	for i = 1, #pos_list_chaikin do
		table.insert(
			widths_lerped,
			wea_c.lerp(width_start, width_end, (1/#pos_list_chaikin)*(i-1))
		)
	end
	
	---
	-- 2: Fetch VoxelManipulator, prepare for replacement
	---
	local manip, area = worldedit.manip_helpers.init(pos1, pos2)
	local data = manip:get_data()
	
	local node_id = minetest.get_content_id(target_node)
	
	local count = 0 -- The number of nodes replaced
	
	---
	-- 3: Replace nodes
	---
	
	local pos_prev = pos_list_chaikin[1]:floor()
	local width_prev = widths_lerped[1]
	for i = 2, #pos_list_chaikin do
		local pos_next = pos_list_chaikin[i]:floor()
		local width_next = widths_lerped[i]
		
		-- For now, just plot a point at each node
		data[area:indexp(pos_next)] = node_id
		data[area:indexp(pos_prev)] = node_id
		
		local subline_length = (pos_next - pos_prev):length()
		print("DEBUG:spline DRAW pos", pos_next, "width", width_next, "length", subline_length)
		if subline_length > 0 then
			
			-- Iterate a box around the subline and draw it
			local width_max = math.ceil(math.max(width_prev, width_next))
			local subpos1, subpos2 = Vector3.sort(pos_next, pos_prev)
			subpos1 = subpos1 - width_max
			subpos2 = subpos2 + width_max
			
			print("subpos1", subpos1, "subpos2", subpos2, "width_prev", width_prev, "width_next", width_next)
			
			for z = subpos2.z, subpos1.z, -1 do
				for y = subpos2.y, subpos1.y, -1 do
					for x = subpos2.x, subpos1.x, -1 do
						local here = Vector3.new(x, y, z)
						local D = (pos_next - pos_prev):normalise()
						local d = Vector3.dot(here - pos_prev, D)
						local closest_on_line = pos_prev + (D * d)
						local distance = (here - closest_on_line):length()
						
						local distance_to_prev = (closest_on_line - pos_prev):length()
						
						local percentage_along_line = distance_to_prev / subline_length
						local width_here = wea_c.lerp(width_prev, width_next, percentage_along_line)
						if distance < width_here then
							-- print("POINT ", here, "width_here", width_here, "distance from line", distance)
							data[area:indexp(here)] = node_id
							-- May hit some nodes multiplle times, but there's nothing doing about that without a big performance (and/or memory) hit
							count = count + 1
						end
					end
				end
			end
			pos_prev = pos_next:clone()
			width_prev = width_next
		end
	end
	
	---
	-- 4: Save changes and return
	---
	
	-- Save the modified nodes back to disk & return
	worldedit.manip_helpers.finish(manip, data)
	
	return true, count
end