-- lx-bundle:true

local config = require("core.config")

local undo = {}

-- Pushes an undo command entry and advances stack index with pruning
function undo.push_undo(undo_stack, time, type, ...)
	undo_stack[undo_stack.idx] = { type = type, time = time, ... }
	undo_stack[undo_stack.idx - config.max_undos] = nil
	undo_stack.idx = undo_stack.idx + 1
end

--- Pop and apply one merged undo or redo command sequence
-- @param self table document instance
-- @param undo_stack table source history stack
-- @param redo_stack table inverse history stack
-- @return boolean|nil false when the document rejects an entry
function undo.pop_undo(self, undo_stack, redo_stack)
	while true do
		local cmd = undo_stack[undo_stack.idx - 1]
		if not cmd then
			return
		end
		undo_stack.idx = undo_stack.idx - 1

		-- Delegate replay so base undo stays generic
		if self:apply_undo_entry(cmd, redo_stack) == false then
			-- Rejected entries remain available and keep the same history identity
			undo_stack.idx = undo_stack.idx + 1
			return false
		end

		-- Keep adjacent entries inside one merge window in the same undo step
		local next = undo_stack[undo_stack.idx - 1]
		if not next or math.abs(cmd.time - next.time) >= config.undo_merge_timeout then
			return
		end
	end
end

-- Performs one undo step from the undo stack
function undo.undo(self)
	undo.pop_undo(self, self.undo_stack, self.redo_stack)
end

-- Performs one redo step from the redo stack
function undo.redo(self)
	undo.pop_undo(self, self.redo_stack, self.undo_stack)
end

return undo
