-- lx-bundle:true

--- Text document model with syntax and highlighter ownership
-- @module core.text.document

local Doc = require("core.doc")
local Highlighter = require("core.text.highlighter")
local file = require("core.text.file")
local edit = require("core.text.edit")
local grid = require("core.text.grid")
local sel = require("core.text.select")
local syntax = require("core.syntax")

local TextDocument = Doc:extend()

-- Keep text-model method ownership explicit as text-only behavior moves out of core.doc
for name, fn in pairs({
	set_selection = sel.set_selection,
	get_selection = sel.get_selection,
	has_selection = sel.has_selection,
	get_selected_line_range = sel.get_selected_line_range,
	sanitize_selection = sel.sanitize_selection,
	sanitize_position = sel.sanitize_position,
	position_offset = sel.position_offset,
	get_text = sel.get_text,
	get_logical_line = sel.get_logical_line,
	line_has_lf = sel.line_has_lf,
	get_char = sel.get_char,
	get_indent_size = grid.get_indent_size,
	get_indent_end_col = grid.get_indent_end_col,
	is_in_indentation = grid.is_in_indentation,
	get_indent_visual_column = grid.get_indent_visual_column,
	get_indent_visual_width = grid.get_indent_visual_width,
	get_indent_col_for_visual_column = grid.get_indent_col_for_visual_column,
	get_previous_indent_caret_col = grid.get_previous_indent_caret_col,
	get_next_indent_caret_col = grid.get_next_indent_caret_col,
	get_indent_delete_range = grid.get_indent_delete_range,
	open = file.open,
	reopen = file.reopen,
	save = file.save,
	save_current = file.save_current,
	rename = file.rename,
	raw_insert = edit.raw_insert,
	raw_remove = edit.raw_remove,
	raw_replace = edit.raw_replace,
	insert = edit.insert,
	remove = edit.remove,
	text_input = edit.text_input,
	replace = edit.replace,
	replace_range = edit.replace_range,
	delete_to = edit.delete_to,
	move_to = edit.move_to,
	flash_line = edit.flash_line,
	select_to = edit.select_to,
}) do
	TextDocument[name] = fn
end

--- Reset text document state and rebuild text-specific services
function TextDocument:reset()
	TextDocument.super.reset(self)
	self.highlighter = Highlighter.create(self)
	self:reset_syntax()
end

--- Re-detect syntax based on filename and header text
function TextDocument:reset_syntax()
	local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
	local syntax_definition, detect_meta = syntax.get(self.filename or "", header, self.is_binary == true)
	self.syntax_detect_meta = detect_meta

	if self.syntax ~= syntax_definition then
		self.syntax = syntax_definition
		self.highlighter:reset()
	end
end

--- Replay one text undo entry using raw edit primitives
-- @param command table undo entry to replay
-- @param redo_stack table target stack for inverse entries
-- @return boolean true when applied, false when unsupported
function TextDocument:apply_undo_entry(cmd, redo_stack)
	if cmd.type == "insert" then
		local line, col, text = table.unpack(cmd)
		self:raw_insert(line, col, text, redo_stack, cmd.time)
	elseif cmd.type == "remove" then
		local line1, col1, line2, col2 = table.unpack(cmd)
		self:raw_remove(line1, col1, line2, col2, redo_stack, cmd.time)
	elseif cmd.type == "replace" then
		local line1, col1, line2, col2, text = table.unpack(cmd)
		self:raw_replace(line1, col1, line2, col2, text, redo_stack, cmd.time)
	elseif cmd.type == "selection" then
		self:set_selection(cmd[1], cmd[2], cmd[3], cmd[4])
	else
		return false
	end

	return true
end

return TextDocument
