-- Modifies an array of path parts in place by interpreting "." and ".." segments

function Path:_normalizeArray(parts: Array<string>, isrelative: boolean)
	local skip = 0
	for i = #parts, 1, -1 do
		local part = parts[i]
		if part == "." then
			table.remove(parts, i)
		elseif part == ".." then
			table.remove(parts, i)
			skip = skip + 1
		elseif skip > 0 then
			table.remove(parts, i)
			skip = skip - 1
		end
	end
	if isrelative then
		while skip > 0 do
			table.insert(parts, 1, "..")
			skip = skip - 1
		end
	end
end

function Path:_splitBySeparators(filepath: string)
	local parts = {}
	for part in filepath:gmatch("[^" .. self.sep .. "]+") do
		parts[#parts + 1] = part
	end
	return parts
end

function Path:normalize(filepath: string)
	filepath = self:normalizeSeparators(filepath)
	local is_absolute = self:isAbsolute(filepath)
	local root = is_absolute and self:getRoot(filepath) or nil
	local trailing_slash = filepath:sub(#filepath) == self.sep

	if root then
		filepath = filepath:sub(root:len() + 1)
	end

	local parts = self:_splitBySeparators(filepath)
	self:_normalizeArray(parts, not is_absolute)
	filepath = table.concat(parts, self.sep)

	if #filepath == 0 then
		if is_absolute then
			return root
		end
		return "."
	end
	if trailing_slash then
		filepath = filepath .. self.sep
	end
	if is_absolute then
		filepath = root .. filepath
	end
	return filepath
end

function Path:_filterparts(parts: Array<string>)
	local filteredparts = {}
	-- filter out empty parts
	for i, part in ipairs(parts) do
		if part and part ~= "" then
			table.insert(filteredparts, part)
		end
	end
	for i, part in ipairs(filteredparts) do
		-- Strip leading slashes on all but first item
		if i > 1 then
			while part:sub(1, 1) == self.sep do
				part = part:sub(2)
			end
		end
		-- Strip trailing slashes on all but last item
		if i < #filteredparts then
			while part:sub(#part) == self.sep do
				part = part:sub(1, #part - 1)
			end
		end
		filteredparts[i] = part
	end
	return filteredparts
end

function Path:_rawjoin(parts: Array<string>)
	return table.concat(parts, self.sep)
end

function Path:_filteredjoin(...: string)
	local parts: Array<string> = { ... }
	for i, part in ipairs(parts) do
		parts[i] = self:normalizeSeparators(part)
	end
	local filteredparts = self:_filterparts(parts)
	local joined = self:_rawjoin(filteredparts)
	return joined, filteredparts
end

function Path:join(...: string)
	local joined = self:_filteredjoin(...)
	return self:normalize(joined)
end
