-- lx-bundle:true

-- This file is part of Lx, created by Liqube <www.liqube.com>
-- Copyright Liqube Audio. All rights reserved.
-- Lx Interim License. See LICENSE for details.

--- XML formatter backed by Lx's native XML API
-- @module plugins.tidyxml

local core = require("core")
local command = require("core.command")
local config = require("core.config")
local xml = require("xml")

config.tidyxml_indent_size = config.tidyxml_indent_size or 2

-- Preserve explicit false values from user configuration
if config.tidyxml_use_tabs == nil then
	config.tidyxml_use_tabs = true
end

--- Return configured indentation for readable XML
-- @return string indent one indentation level
local function get_indent()
	if config.tidyxml_use_tabs then
		return "\t"
	end

	local width = math.max(0, math.floor(tonumber(config.tidyxml_indent_size) or 2))
	return string.rep(" ", width)
end

--- Parse and serialize XML without losing non-element nodes
-- @param text string source XML
-- @param compact boolean omit formatting whitespace
-- @return string result formatted XML
local function format_xml(text, compact)
	local document, err = xml.parse(text, { full = true })
	if not document then
		error(err, 0)
	end

	return document:serialize({
		indent = compact and "" or get_indent(),
		compact = compact,
	})
end

--- Replace active document through native XML formatting
-- @param compact boolean omit formatting whitespace
local function run_formatter(compact)
	local doc = core.active_view.doc
	local ok, err = pcall(function()
		doc:replace(function(text)
			return format_xml(text, compact)
		end)
	end)

	if not ok then
		core.status_view:show_error("Error in XML: " .. tostring(err))
	elseif compact then
		core.status_view:show_success("XML minimized")
	else
		core.status_view:show_success("XML tidied")
	end
end

command.register({
	["tidyxml:format"] = {
		requires = { "doc", "edit" },
		perform = function()
			run_formatter(false)
		end,
	},

	["tidyxml:minimize"] = {
		requires = { "doc", "edit" },
		perform = function()
			run_formatter(true)
		end,
	},
})

return true
