-- Encapsulate common and change lines

local CommonBuffer = {}
CommonBuffer.__index = CommonBuffer
function CommonBuffer.new(deleteBuffer, insertBuffer)
	local self = {}
	self.deleteBuffer = deleteBuffer
	self.insertBuffer = insertBuffer
	self.lines = {}
	setmetatable(self, CommonBuffer)
	return self
end

function CommonBuffer:pushDiffCommonLine(diff: Diff): ()
	table.insert(self.lines, diff)
end

function CommonBuffer:pushDiffChangeLines(diff: Diff): ()
	local isDiffEmpty = #diff[2] == 0

	-- An empty diff string is redundant, unless a change line is empty.
	if not isDiffEmpty or self.deleteBuffer:isLineEmpty() then
		self.deleteBuffer:pushDiff(diff)
	end
	if not isDiffEmpty or self.insertBuffer:isLineEmpty() then
		self.insertBuffer:pushDiff(diff)
	end
end

function CommonBuffer:flushChangeLines(): ()
	self.deleteBuffer:moveLinesTo(self.lines)
	self.insertBuffer:moveLinesTo(self.lines)
end

function CommonBuffer:align(diff: Diff): ()
	local op = diff[1]
	local s = diff[2]

	if s:match("\n") then
		local substrings = s:split("\n")
		-- ROBLOX deviation: 1-indexing
		local iLast = #substrings
		for i, substring in ipairs(substrings) do
			if i == 1 then
				local subdiff = Diff.new(op, substring)
				if self.deleteBuffer:isLineEmpty() and self.insertBuffer:isLineEmpty() then
					-- If both current change lines are empty,
					-- then the first substring is a common line.
					self:flushChangeLines()
					self:pushDiffCommonLine(subdiff)
				else
					-- If either current change line is non-empty,
					-- then the first substring completes the change lines.
					self:pushDiffChangeLines(subdiff)
					self:flushChangeLines()
				end
			elseif i < iLast then
				-- A middle substring is a common line.
				self:pushDiffCommonLine(Diff.new(op, substring))
			elseif #substring ~= 0 then
				-- The last substring starts a change line, if it is not empty.
				-- Important: This non-empty condition also automatically omits
				-- the newline appended to the end of expected and received strings.
				self:pushDiffChangeLines(Diff.new(op, substring))
			end
		end
	else
		-- Append non-multiline string to current change lines.
		-- Important: It cannot be at the end following empty change lines,
		-- because newline appended to the end of expected and received strings.
		self:pushDiffChangeLines(diff)
	end
end

function CommonBuffer:getLines(): Array<Diff>
	self:flushChangeLines()
	return self.lines
end
