-- lx-bundle:true

--- Build complete Forms from declarative Rows, Cols, Panes, and Lua Controls
-- The raw APIs remain available for runtime changes and specialized construction
-- @module qui

local form_api = assert(form, "the Form API is not loaded")
local layout_api = assert(layout, "the Layout API is not loaded")
local pane_api = assert(pane, "the Pane API is not loaded")
local control_api = assert(control, "the Control API is not loaded")
local window_api = assert(window, "the Window API is not loaded")
local colors = require("qui.colors")
local palette = require("qui.palette")
local Command = require("qui.command")
local Shortcut = require("qui.shortcut")
local theme = require("qui.theme")
local Font = require("qui.font")
require("qui.defaults")			-- Loads the one normal metric baseline before any named Theme
require("themes.liqube_dark")	-- Registers the stock dark appearance without current-era tables
local lua_type = type			-- Public color handlers use the concise name 'type' without hiding Lua's checker here

local default_applied, default_apply_error = theme.apply("Liqube Dark")
assert(default_applied, "could not apply the default new-UI Theme: " .. tostring(default_apply_error))

local qui = {}

qui.font = Font
qui.palette = palette

--- Create one reusable action for Controls inside a single Form
-- Direct Control callbacks remain the simpler choice for local-only behavior
-- @param options table command name, presentation, state, and execute callback
-- @return table command
function qui.command(options)
	return Command.new(options)
end

--- Create one stable lowercase shortcut value for Controls and Menu items
-- Display text is prepared separately so matching never depends on capitalization
-- @param source string authored shortcut
-- @return string shortcut_value
function qui.shortcut(source)
	return Shortcut.parse(source)
end

-- These familiar primitive values support custom drawing without becoming mutable catalog storage
qui.black = colors.black
qui.white = colors.white
qui.gray = colors.gray
qui.red = colors.red
qui.transparent = colors.transparent

-- Declaration identity and checked bindings stay outside user tables so custom metatables remain untouched
local declaration_kinds = setmetatable({}, { __mode = "k" })
local control_bindings = setmetatable({}, { __mode = "k" })	-- Live handles retained only while the Control table exists
local built_form_handles = setmetatable({}, { __mode = "k" })	-- Confirms that a Pane receives a real live Form built by qui
local themed_forms = setmetatable({}, { __mode = "k" })	-- Forms without an override follow the active stock Theme

--- Refresh every live Form that uses the active Theme background
-- @return boolean updated
-- @return string|nil error_text
local function apply_form_backgrounds()
	local background = theme.resolve(colors.form_bg)
	for built_form in pairs(themed_forms) do
		if form_api.is_valid(built_form.handle) then
			local updated, update_error = form_api.set_background(built_form.handle, background)
			if not updated then
				return nil, update_error
			end
		end
	end

	return true
end

theme.on_apply(apply_form_backgrounds)

--- Change whether a Control receives ordinary input after construction
-- Before construction this only changes the initial declaration value
-- @param enabled boolean requested enabled state
-- @return boolean|nil changed
-- @return string|nil error_text
local function set_control_enabled(self, enabled)
	assert(type(enabled) == "boolean", "Control enabled state must be a boolean")
	local binding = control_bindings[self]
	if binding then
		local changed, change_error = control_api.set_enabled(binding.form, binding.control, enabled)
		if not changed then
			return nil, change_error
		end
	end

	rawset(self, "enabled", enabled)
	return true
end

--- Change whether a Control participates in layout and drawing after construction
-- Before construction this only changes the initial declaration value
-- @param visible boolean requested visible state
-- @return boolean|nil changed
-- @return string|nil error_text
local function set_control_visible(self, visible)
	assert(type(visible) == "boolean", "Control visible state must be a boolean")
	local binding = control_bindings[self]
	if binding then
		local changed, change_error = control_api.set_visible(binding.form, binding.control, visible)
		if not changed then
			return nil, change_error
		end
	end

	rawset(self, "visible", visible)
	return true
end

--- Change whether a focusable Control participates in Tab traversal
-- Pointer clicks and direct focus remain available when this is false
-- @param tab_stop boolean requested Tab participation
-- @return boolean|nil changed
-- @return string|nil error_text
local function set_control_tab_stop(self, tab_stop)
	assert(type(tab_stop) == "boolean", "Control tab_stop state must be a boolean")
	local binding = control_bindings[self]
	if binding then
		local changed, change_error = control_api.set_tab_stop(binding.form, binding.control, tab_stop)
		if not changed then
			return nil, change_error
		end
	end

	rawset(self, "tab_stop", tab_stop)
	return true
end

--- Request drawing after custom visual state changes outside an event callback
-- @return boolean|nil invalidated
-- @return string|nil error_text
local function invalidate_control(self)
	local binding = control_bindings[self]
	if not binding then
		return nil, "Control is not part of a built Form"
	end

	return control_api.invalidate(binding.form, binding.control)
end

--- Request drawing while retaining valid renderer and shaped-text caches
-- Use this for ordinary visual state changes that do not invalidate the complete Window
-- @return boolean|nil redrawn
-- @return string|nil error_text
local function redraw_control(self)
	local binding = control_bindings[self]
	if not binding then
		return nil, "Control is not part of a built Form"
	end

	return control_api.redraw(binding.form, binding.control)
end

--- Request fresh measurement, arrangement, and drawing after natural-size changes
-- An unbuilt declaration needs no request because its first layout will use the latest fields
-- @return boolean|nil relaid_out
-- @return string|nil error_text
local function relayout_control(self)
	local binding = control_bindings[self]
	if not binding then
		return true
	end

	return control_api.relayout(binding.form, binding.control)
end

--- Move logical keyboard focus to this live Control
-- @return boolean|nil focused
-- @return string|nil error_text
local function focus_control(self)
	local binding = control_bindings[self]
	if not binding then
		return nil, "Control is not part of a built Form"
	end

	return form_api.focus(binding.form, binding.control)
end

--- Open one companion overlay and keep it attached to this Control after later layout changes
-- @param overlay table companion Control declared in this Control's overlays list
-- @param options table optional size, gap, placement, and local anchor row; omitted sizes use natural size
-- @return boolean|nil shown
-- @return string|nil error_text
local function show_control_overlay(self, overlay, options)
	assert(type(options) == "table", "Control.show_overlay expects an options table")
	local binding = control_bindings[self]
	local overlay_binding = control_bindings[overlay]
	if not binding or not overlay_binding or overlay_binding.form ~= binding.form then
		return nil, "Overlay and anchor must belong to the same built Form"
	end

	return form_api.set_anchored_overlay(
		binding.form,
		overlay_binding.control,
		binding.control,
		options
	)
end

--- Close one companion overlay while retaining it for later reuse
-- @param overlay table companion Control declared in this Control's overlays list
-- @return boolean|nil hidden
-- @return string|nil error_text
local function hide_control_overlay(self, overlay)
	local binding = control_bindings[self]
	local overlay_binding = control_bindings[overlay]
	if not binding or not overlay_binding or overlay_binding.form ~= binding.form then
		return nil, "Overlay and anchor must belong to the same built Form"
	end

	return form_api.set_overlay(binding.form, overlay_binding.control, nil)
end

--- Report whether one companion overlay is currently presented in a native Window
-- @param overlay table companion Control declared in this Control's overlays list
-- @return boolean visible
local function control_overlay_visible(self, overlay)
	local binding = control_bindings[self]
	local overlay_binding = control_bindings[overlay]
	if not binding or not overlay_binding or overlay_binding.form ~= binding.form then
		return false
	end

	return form_api.is_overlay_visible(binding.form, overlay_binding.control)
end

--- Place native IME helpers beside the caret of a focused text Control
-- Coordinates are local to the Control and the cursor is measured from the area's left edge
-- @param x integer local left edge
-- @param y integer local top edge
-- @param width integer editing area width
-- @param height integer editing area height
-- @param cursor integer horizontal caret offset
-- @return boolean|nil updated
-- @return string|nil error_text
local function set_text_input_area(self, x, y, width, height, cursor)
	local binding = control_bindings[self]
	if not binding then
		return nil, "Control is not part of a built Form"
	end

	return control_api.set_text_input_area(binding.form, binding.control, x, y, width, height, cursor)
end

--- Resolve one Control color through its dynamic callback, shared default, and the central theme
-- @param type string requested visual type such as 'background', 'frame', or 'text'
-- @param flags table independent current conditions supplied by the Control
-- @return table resolved renderer-neutral color
local function get_control_color(self, type, flags)
	assert(lua_type(type) == "string" and type ~= "", "Control color type must be a non-empty string")
	assert(lua_type(flags) == "table", "Control color flags must be a table")

	local selected
	local on_color = rawget(self, "on_color")
	if on_color ~= nil then
		assert(lua_type(on_color) == "function", "Control on_color must be a function")
		selected = on_color(self, type, flags)
	end
	if selected == nil then
		local default_color = rawget(self, "default_color")
		assert(lua_type(default_color) == "function", "Control default_color must be a function")
		selected = default_color(self, type, flags)
		assert(selected ~= nil, "Control default_color did not handle " .. type)
	end

	return theme.resolve(selected)
end

local form_fields = {
	name = true,
	type = true,
	background = true,
	layout = true,
	overlays = true,
	commands = true,
	initial_focus = true,
	event = true,
	on_close_request = true,
	on_complete = true,
}

local layout_fields = {
	name = true,
	gap = true,
	padding_left = true,
	padding_top = true,
	padding_right = true,
	padding_bottom = true,
}

local pane_fields = {
	name = true,
	width = true,
	height = true,
	length = true,
	weight = true,
	minimum_width = true,
	minimum_height = true,
	maximum_width = true,
	maximum_height = true,
	collapsed = true,
	layout = true,
	controls = true,
	form = true,
}

-- Form-owned opening accepts ordinary native settings while keeping visibility private
local window_fields = {
	title = true,
	width = true,
	height = true,
	owner_id = true,
	modality = true,
	resizable = true,
	high_pixel_density = true,
}

--- Report one declaration error with its readable location
-- @param path string declaration location
-- @param message string failure explanation
local function declaration_error(path, message)
	error(path .. ": " .. message, 0)
end

--- Mark one user table without changing its contents or metatable
-- @param kind string declaration kind
-- @param options table declaration table
-- @return table options same table supplied by the caller
local function declare(kind, options)
	if type(options) ~= "table" then
		error("qui." .. kind .. " expects a table", 3)
	end

	local previous_kind = declaration_kinds[options]
	if previous_kind and previous_kind ~= kind then
		error("one UI declaration table cannot be both " .. previous_kind .. " and " .. kind, 3)
	end

	declaration_kinds[options] = kind

	return options
end

--- Declare a top-to-bottom Layout
-- @param options table Layout settings and Pane declarations
-- @return table declaration
function qui.rows(options)
	return declare("rows", options)
end

--- Declare a left-to-right Layout
-- @param options table Layout settings and Pane declarations
-- @return table declaration
function qui.cols(options)
	return declare("cols", options)
end

--- Declare one non-visual Pane
-- @param options table Pane settings, optional nested Layout, and Controls
-- @return table declaration
function qui.pane(options)
	return declare("pane", options)
end

--- Build one titled form section from ordinary Layouts, Panes, Labels, and Separators
-- Section fills the HTML/CSS fieldset role and broadly resembles a classic GroupBox
-- It is a construction-time meta control, not a live Control or a new ownership layer
-- @param options table title, content Layout, sizing, and optional section spacing
-- @return table declaration ordinary Pane declaration
function qui.section(options)
	local Section = require("qui.section")

	return Section.new(options)
end

--- Declare one unrestricted Lua Control definition
-- @param definition table Control state, callbacks, and optional placement
-- @return table declaration
function qui.control(definition)
	if type(definition) == "table" then
		if rawget(definition, "set_enabled") ~= nil
			or rawget(definition, "set_visible") ~= nil
			or rawget(definition, "set_tab_stop") ~= nil
			or rawget(definition, "invalidate") ~= nil
			or rawget(definition, "redraw") ~= nil
			or rawget(definition, "relayout") ~= nil
			or rawget(definition, "focus") ~= nil
			or rawget(definition, "show_overlay") ~= nil
			or rawget(definition, "hide_overlay") ~= nil
			or rawget(definition, "overlay_visible") ~= nil
			or rawget(definition, "set_text_input_area") ~= nil
			or rawget(definition, "get_color") ~= nil
		then
			error("Control live-method fields are reserved", 2)
		end

		-- Direct fields preserve custom metatables while giving every declaration the same live methods
		rawset(definition, "set_enabled", set_control_enabled)
		rawset(definition, "set_visible", set_control_visible)
		rawset(definition, "set_tab_stop", set_control_tab_stop)
		rawset(definition, "invalidate", invalidate_control)
		rawset(definition, "redraw", redraw_control)
		rawset(definition, "relayout", relayout_control)
		rawset(definition, "focus", focus_control)
		rawset(definition, "show_overlay", show_control_overlay)
		rawset(definition, "hide_overlay", hide_control_overlay)
		rawset(definition, "overlay_visible", control_overlay_visible)
		rawset(definition, "set_text_input_area", set_text_input_area)
		rawset(definition, "get_color", get_control_color)
	end

	return declare("control", definition)
end

--- Run one function after the current UI callback or drawing walk has fully returned
-- Outside active UI work the function runs immediately instead of waiting for another frame
-- @param callback function work that may safely construct or restructure Forms
-- @return boolean|nil accepted
-- @return string|nil error_text
function qui.defer(callback)
	assert(type(callback) == "function", "qui.defer expects a function")

	return form_api.defer(callback)
end

--- Reject unknown named fields and return the length of a gapless numeric sequence
-- @param value table declaration or child list
-- @param allowed_fields table allowed named keys
-- @param path string declaration location
-- @return integer length
local function validate_table_shape(value, allowed_fields, path)
	local numeric_count = 0
	local highest_index = 0

	for key in next, value do
		if type(key) == "number" then
			if key < 1 or key % 1 ~= 0 then
				declaration_error(path, "numeric children must use positive whole-number positions")
			end

			numeric_count = numeric_count + 1
			highest_index = math.max(highest_index, key)
		elseif type(key) ~= "string" or not allowed_fields[key] then
			declaration_error(path, "unknown field " .. tostring(key))
		end
	end

	if highest_index ~= numeric_count then
		declaration_error(path, "numeric children must not contain gaps")
	end

	return numeric_count
end

--- Record one declaration once so cycles and repeated structural objects fail before construction
-- @param declaration table declaration being visited
-- @param path string current location
-- @param seen table earlier declaration locations
local function remember_declaration(declaration, path, seen)
	local previous_path = seen[declaration]
	if previous_path then
		declaration_error(path, "reuses the declaration already placed at " .. previous_path)
	end

	seen[declaration] = path
end

--- Validate one Control declaration while leaving all custom fields unrestricted
-- @param declaration table Control declaration
-- @param path string current location
-- @param seen table earlier declaration locations
local function validate_control(declaration, path, seen)
	if declaration_kinds[declaration] ~= "control" then
		declaration_error(path, "expected qui.control { ... }")
	end

	remember_declaration(declaration, path, seen)
	local binding = control_bindings[declaration]
	if binding and control_api.is_valid(binding.form, binding.control) then
		declaration_error(path, "is already part of a live Form")
	end
	if binding then
		-- A declaration may be reused only after its previous Form has actually gone away
		control_bindings[declaration] = nil
	end

	local placement = rawget(declaration, "placement")
	if placement ~= nil and type(placement) ~= "table" then
		declaration_error(path .. ".placement", "expected a table")
	end

	local companion_overlays = rawget(declaration, "overlays")
	if companion_overlays == nil then
		return
	end
	if type(companion_overlays) ~= "table" then
		declaration_error(path .. ".overlays", "expected a list of qui.control declarations")
	end

	local overlay_count = validate_table_shape(companion_overlays, {}, path .. ".overlays")
	for index = 1, overlay_count do
		local companion = rawget(companion_overlays, index)
		local companion_path = path .. ".overlays[" .. index .. "]"
		validate_control(companion, companion_path, seen)
		if rawget(companion, "placement") ~= nil then
			declaration_error(companion_path .. ".placement", "overlay position is supplied when it is shown")
		end
	end
end

local validate_layout

--- Report whether one Lua number is safe to treat as a DIP declaration
-- @param value any value to inspect
-- @return boolean valid
local function is_dip_number(value)
	return type(value) == "number" and value >= 0 and value == value and value < math.huge
end

--- Validate Pane dimensions against the direction that gives them structural meaning
-- @param declaration table Pane declaration
-- @param path string current Pane location
-- @param parent_kind string 'rows' or 'cols'
local function validate_pane_dimensions(declaration, path, parent_kind)
	local main_field = parent_kind == "rows" and "height" or "width"
	local cross_field = parent_kind == "rows" and "width" or "height"
	local main_value = rawget(declaration, main_field)
	local cross_value = rawget(declaration, cross_field)
	local length = rawget(declaration, "length")

	if main_value ~= nil and length ~= nil then
		declaration_error(path, "cannot combine '" .. main_field .. "' with 'length'")
	end
	if length ~= nil and length ~= "content" and length ~= "weighted" then
		declaration_error(path .. ".length", "expected 'content' or 'weighted'; use '" .. main_field .. "' for a DIP or percentage length")
	end

	if type(main_value) == "string" then
		-- The final byte is enough to distinguish a percentage before C++ parses its number once
		if main_value:byte(-1) ~= 37 then
			declaration_error(path .. "." .. main_field, "expected a DIP number or percentage ending in '%'")
		end
	elseif main_value ~= nil and not is_dip_number(main_value) then
		declaration_error(path .. "." .. main_field, "expected a non-negative finite DIP number or percentage")
	end

	if type(cross_value) == "string" then
		declaration_error(path .. "." .. cross_field, "percentages are only valid for '" .. main_field .. "' inside qui." .. parent_kind)
	elseif cross_value ~= nil and not is_dip_number(cross_value) then
		declaration_error(path .. "." .. cross_field, "expected a non-negative finite DIP number")
	end
end

--- Validate one detached live Form selected for construction-time Pane hosting
-- @param built_form table live Form returned by qui.form
-- @param path string current Form field location
-- @param seen_forms table earlier embedded Form locations
local function validate_embedded_form(built_form, path, seen_forms)
	local form_handle
	if type(built_form) == "table" then
		form_handle = built_form_handles[built_form]
	end
	if not form_handle or rawget(built_form, "handle") ~= form_handle or not form_api.is_valid(form_handle) then
		declaration_error(path, "expected a live Form returned by qui.form")
	end

	local previous_path = seen_forms[built_form]
	if previous_path then
		declaration_error(path, "reuses the Form already placed at " .. previous_path)
	end
	seen_forms[built_form] = path

	local host, host_error = form_api.get_host(form_handle)
	if host_error then
		declaration_error(path, host_error)
	end
	if host then
		declaration_error(path, "Form is already attached to a " .. host.kind)
	end
end

--- Validate one Pane and recursively validate its nested Layout and Controls
-- @param declaration table Pane declaration
-- @param path string current location
-- @param seen table earlier declaration locations
-- @param seen_forms table earlier embedded Form locations
local function validate_pane(declaration, path, seen, seen_forms, parent_kind)
	if declaration_kinds[declaration] ~= "pane" then
		declaration_error(path, "expected qui.pane { ... }")
	end

	remember_declaration(declaration, path, seen)
	if validate_table_shape(declaration, pane_fields, path) ~= 0 then
		declaration_error(path, "Panes use named fields rather than numeric children")
	end
	validate_pane_dimensions(declaration, path, parent_kind)

	local nested_layout = rawget(declaration, "layout")
	if nested_layout ~= nil then
		validate_layout(nested_layout, path .. ".layout", seen, seen_forms, false)
	end

	local embedded_form = rawget(declaration, "form")
	if embedded_form ~= nil then
		validate_embedded_form(embedded_form, path .. ".form", seen_forms)
	end

	local controls = rawget(declaration, "controls")
	if controls == nil then
		return
	end
	if type(controls) ~= "table" then
		declaration_error(path .. ".controls", "expected a list of qui.control declarations")
	end

	local control_count = validate_table_shape(controls, {}, path .. ".controls")
	for index = 1, control_count do
		validate_control(rawget(controls, index), path .. ".controls[" .. index .. "]", seen)
	end
end

--- Validate one Rows or Cols declaration and every Pane it creates
-- @param declaration table Layout declaration
-- @param path string current location
-- @param seen table earlier declaration locations
-- @param seen_forms table earlier embedded Form locations
-- @param root boolean whether this is the Form root
validate_layout = function(declaration, path, seen, seen_forms, root)
	local kind = declaration_kinds[declaration]
	if kind ~= "rows" and kind ~= "cols" then
		declaration_error(path, "expected qui.rows { ... } or qui.cols { ... }")
	end

	remember_declaration(declaration, path, seen)
	if root and rawget(declaration, "name") ~= nil then
		declaration_error(path .. ".name", "the root Layout already has the fixed name 'root'")
	end

	local pane_count = validate_table_shape(declaration, layout_fields, path)
	for index = 1, pane_count do
		validate_pane(rawget(declaration, index), path .. "[" .. index .. "]", seen, seen_forms, kind)
	end
end

--- Validate the complete Form tree before allocating its durable C++ Form
-- @param declaration table Form declaration
local function validate_form(declaration)
	if type(declaration) ~= "table" then
		declaration_error("Form", "qui.form expects a table")
	end
	if validate_table_shape(declaration, form_fields, "Form") ~= 0 then
		declaration_error("Form", "use the 'layout' field for the root Rows or Cols")
	end
	local form_type = rawget(declaration, "type")
	if form_type ~= nil and form_type ~= "dialog" then
		declaration_error("Form.type", "expected 'dialog'")
	end
	local root_layout = rawget(declaration, "layout")
	if root_layout == nil then
		declaration_error("Form.layout", "a root qui.rows or qui.cols declaration is required")
	end
	if rawget(declaration, "on_close_request") ~= nil
		and type(rawget(declaration, "on_close_request")) ~= "function"
	then
		declaration_error("Form.on_close_request", "expected a function")
	end
	if rawget(declaration, "on_complete") ~= nil
		and type(rawget(declaration, "on_complete")) ~= "function"
	then
		declaration_error("Form.on_complete", "expected a function")
	end

	if rawget(declaration, "event") ~= nil and type(rawget(declaration, "event")) ~= "function" then
		declaration_error("Form.event", "expected a function")
	end

	local commands = rawget(declaration, "commands")
	if commands ~= nil then
		if type(commands) ~= "table" then
			declaration_error("Form.commands", "expected a list of qui.command values")
		end

		local command_count = validate_table_shape(commands, {}, "Form.commands")
		for index = 1, command_count do
			if not Command.is(rawget(commands, index)) then
				declaration_error("Form.commands[" .. index .. "]", "expected a qui.command")
			end
		end
	end

	local seen = {}
	local seen_forms = {}
	validate_layout(root_layout, "Form.layout", seen, seen_forms, true)
	local overlays = rawget(declaration, "overlays")
	if overlays ~= nil then
		if type(overlays) ~= "table" then
			declaration_error("Form.overlays", "expected a list of qui.control declarations")
		end

		local overlay_count = validate_table_shape(overlays, {}, "Form.overlays")
		for index = 1, overlay_count do
			local overlay = rawget(overlays, index)
			validate_control(overlay, "Form.overlays[" .. index .. "]", seen)
			if rawget(overlay, "placement") ~= nil then
				declaration_error("Form.overlays[" .. index .. "].placement", "overlay position is supplied when it is shown")
			end
		end
	end

	local initial_focus = rawget(declaration, "initial_focus")
	if initial_focus ~= nil then
		if declaration_kinds[initial_focus] ~= "control" then
			declaration_error("Form.initial_focus", "expected a qui.control in this Form")
		end
		if seen[initial_focus] == nil then
			declaration_error("Form.initial_focus", "Control is not placed in this Form")
		end
	end
end

--- Call one raw API and turn both Lua errors and 'nil, error' results into one build failure
-- @param action string readable operation
-- @param callback function raw API function
-- @param ... any raw API arguments
-- @return any result first successful result
local function call_api(action, callback, ...)
	local ok, result, result_error = pcall(callback, ...)
	if not ok then
		error(action .. ": " .. tostring(result), 0)
	end
	if not result then
		error(action .. ": " .. tostring(result_error or "operation failed"), 0)
	end

	return result
end

--- Store every handle numerically and also by a non-empty declaration name
-- @param collection table returned handle collection
-- @param name string|nil optional declaration name
-- @param handle userdata checked handle
local function remember_handle(collection, name, handle)
	collection[#collection + 1] = handle
	if type(name) == "string" and name ~= "" then
		collection[name] = handle
	end
end

--- Connect the original unrestricted Lua table to its durable checked identities
-- @param state table construction state and returned handles
-- @param declaration table original Control declaration
-- @param control_handle userdata checked Control handle
local function bind_control(state, declaration, control_handle)
	control_bindings[declaration] = {
		form = state.handle,
		control = control_handle,
	}
	state.bound_controls[#state.bound_controls + 1] = declaration
end

--- Report whether one Form action Control can currently be used
-- @param control table original Control declaration
-- @return boolean available
local function form_control_available(control)
	return rawget(control, "enabled") ~= false and rawget(control, "visible") ~= false
end

--- Focus and activate one Control through its normal public behavior
-- @param control table original Control declaration
-- @return boolean handled
local function activate_form_control(control)
	local focused, focus_error = control:focus()
	assert(focused, "Form action could not focus its Control: " .. tostring(focus_error))
	local activated, activate_error = control:activate()
	assert(activated ~= nil, "Form action failed: " .. tostring(activate_error))

	return true
end

--- Activate one shortcut target without moving the current keyboard focus
-- @param target table registered Control or Menu item target
-- @return boolean handled
local function activate_form_shortcut(target)
	if target.available and not target.available() then
		return false
	end

	local activated, activate_error = target.activate()
	assert(activated ~= nil, "Form shortcut failed: " .. tostring(activate_error))

	return activated == true
end

--- Register one shortcut target and reject ambiguous Form-wide matches
-- @param state table Form construction state
-- @param target table shortcut, activate, and optional available callback
-- @param path string readable declaration location
local function register_form_shortcut(state, target, path)
	assert(type(target) == "table", path .. " shortcut target must be a table")
	assert(type(target.shortcut) == "string" and target.shortcut ~= "",
		path .. " shortcut must be a non-empty string")
	assert(type(target.activate) == "function", path .. " shortcut requires an activate function")
	assert(target.available == nil or type(target.available) == "function",
		path .. " shortcut available must be a function")
	local value = Shortcut.parse(target.shortcut)
	assert(state.shortcuts[value] == nil, "Form has more than one shortcut " .. Shortcut.format(value))
	target.shortcut = value
	state.shortcuts[value] = target
end

--- Register one named Command and its optional Form-wide shortcut
-- @param state table Form construction state
-- @param command table Command created by 'qui.command'
-- @param path string readable declaration location
local function register_form_command(state, command, path)
	assert(Command.is(command), path .. " must be a qui.command")
	assert(state.commands[command.name] == nil, "Form has more than one command named " .. command.name)
	state.commands[command.name] = command
	state.command_set[command] = true

	if command.shortcut then
		register_form_shortcut(state, {
			shortcut = command.shortcut,
			available = function()
				return command.enabled
			end,
			activate = function()
				return command:run()
			end,
		}, path)
	end
end

--- Record one placed Control's optional accelerator, shortcut, and dialog role
-- @param state table Form construction state
-- @param control table original Control declaration
-- @param path string readable declaration location
local function register_form_control(state, control, path)
	local command = rawget(control, "command")
	if command ~= nil then
		assert(state.command_set[command], path .. " uses a Command not listed in Form.commands")
	end

	local command_refs = rawget(control, "command_refs")
	if command_refs ~= nil then
		assert(type(command_refs) == "table", path .. " command_refs must be a table")
		for index, referenced_command in ipairs(command_refs) do
			assert(state.command_set[referenced_command],
				path .. ".command_refs[" .. index .. "] is not listed in Form.commands")
		end
	end

	local shortcut = rawget(control, "shortcut")
	if shortcut ~= nil then
		assert(type(rawget(control, "activate")) == "function",
			path .. " shortcut requires an activate function")
		register_form_shortcut(state, {
			shortcut = shortcut,
			activate = function()
				if not form_control_available(control) then
					return false
				end

				return control:activate()
			end,
		}, path)
	end

	local shortcut_targets = rawget(control, "shortcut_targets")
	if shortcut_targets ~= nil then
		assert(type(shortcut_targets) == "table", path .. " shortcut_targets must be a table")
		for index, target in ipairs(shortcut_targets) do
			register_form_shortcut(state, target, path .. ".shortcut_targets[" .. index .. "]")
		end
	end

	local accelerator = rawget(control, "accelerator")
	if accelerator ~= nil then
		assert(type(accelerator) == "string" and accelerator ~= "",
			path .. " accelerator must be a non-empty string")
		assert(type(rawget(control, "activate")) == "function",
			path .. " accelerator requires an activate function")
		assert(rawget(control, "focusable") ~= false,
			path .. " accelerator requires a focusable Control")
		local matches = state.accelerators[accelerator]
		if matches == nil then
			matches = {}
			state.accelerators[accelerator] = matches
		end
		matches[#matches + 1] = control
	end

	local dialog_action = rawget(control, "dialog_action")
	if dialog_action == nil then
		return
	end
	assert(state.type == "dialog", path .. " dialog_action requires Form.type = 'dialog'")
	assert(dialog_action == "default" or dialog_action == "cancel",
		path .. " dialog_action must be default or cancel")
	assert(type(rawget(control, "activate")) == "function",
		path .. " dialog_action requires an activate function")
	assert(rawget(control, "focusable") ~= false,
		path .. " dialog_action requires a focusable Control")
	assert(state.dialog_actions[dialog_action] == nil,
		"Form has more than one " .. dialog_action .. " dialog action")
	state.dialog_actions[dialog_action] = control
end

--- Handle QUI-wide Form keys before leaving other keys to the authored callback
-- Controls receive the event first, so this sees only keys they deliberately leave available
-- @param self table built Form
-- @param event table normalized UI event
-- @return boolean handled
local function handle_form_event(self, event)
	if event.kind == "key_down" and not event["repeat"] then
		local shortcut_value = Shortcut.from_event(event)
		local shortcut_target = shortcut_value and self.shortcuts[shortcut_value] or nil
		if shortcut_target and activate_form_shortcut(shortcut_target) then
			return true
		elseif event.alt and not event.control and not event.system then
			local matches = self.accelerators[event.key:lower()]
			if matches then
				for _, control in ipairs(matches) do
					if form_control_available(control) then
						return activate_form_control(control)
					end
				end
			end
		elseif not event.control and not event.alt and not event.system then
			local dialog_action
			if event.key == "return" then
				dialog_action = self.dialog_actions.default
			elseif event.key == "escape" then
				dialog_action = self.dialog_actions.cancel
			end
			if dialog_action and form_control_available(dialog_action) then
				return activate_form_control(dialog_action)
			end
		end
	end

	if self.user_event ~= nil then
		return self.user_event(self, event)
	end

	return false
end

local build_control_overlays	-- Recursive companion overlays are built as Form-owned siblings

--- Create one reusable Form-owned overlay Control without placing it in a Pane
-- @param state table construction state and returned handles
-- @param declaration table overlay Control declaration
-- @param path string readable declaration location
local function build_overlay_control(state, declaration, path)
	local control_handle = call_api(
		"cannot create " .. path,
		control_api.create,
		state.handle,
		declaration
	)
	remember_handle(state.controls, rawget(declaration, "name"), control_handle)
	bind_control(state, declaration, control_handle)
	register_form_control(state, declaration, path)
	call_api("cannot attach " .. path, form_api.attach_overlay, state.handle, control_handle)
	-- Nested declarations remain sibling Form overlays while preserving their readable ownership tree
	build_control_overlays(state, declaration, path)
end

--- Build overlays kept private by one reusable Control such as a Combobox
-- @param state table construction state and returned handles
-- @param declaration table ordinary Control declaration
-- @param path string readable declaration location
build_control_overlays = function(state, declaration, path)
	local overlays = rawget(declaration, "overlays")
	if overlays == nil then
		return
	end

	for index, overlay in ipairs(overlays) do
		build_overlay_control(state, overlay, path .. ".overlays[" .. index .. "]")
	end
end

--- Remove bindings created by a Form construction that did not finish
-- @param declarations table Control declarations bound during the failed build
local function unbind_controls(declarations)
	for _, declaration in ipairs(declarations) do
		control_bindings[declaration] = nil
	end
end

--- Create one nested Layout through the matching raw constructor
-- @param form_handle userdata owning Form handle
-- @param declaration table Rows or Cols declaration
-- @return userdata layout_handle
local function create_nested_layout(form_handle, declaration)
	local constructor = declaration_kinds[declaration] == "rows" and layout_api.rows or layout_api.cols

	return call_api("cannot create nested Layout", constructor, form_handle, declaration)
end

local build_layout

--- Translate the declarative parent-axis dimension into the durable Layout length
-- @param declaration table original Pane declaration
-- @param parent_kind string 'rows' or 'cols'
-- @return table options detached copy passed to the raw Pane API
local function pane_api_options(declaration, parent_kind)
	local options = {}
	for key, value in next, declaration do
		options[key] = value
	end

	local main_field = parent_kind == "rows" and "height" or "width"
	local main_value = rawget(declaration, main_field)
	if main_value ~= nil then
		options.length = main_value
		if type(main_value) == "string" then
			-- Percentage text describes parent sizing and is not a natural numeric Pane dimension
			options[main_field] = nil
		end
	end

	return options
end

--- Create one Pane, its nested Layout, Controls, and optional embedded Form
-- @param state table construction state and returned handles
-- @param parent_layout userdata parent Layout handle
-- @param declaration table Pane declaration
-- @param path string readable location
local function build_pane(state, parent_layout, declaration, path, parent_kind)
	local pane_handle = call_api(
		"cannot create " .. path,
		pane_api.create,
		state.handle,
		pane_api_options(declaration, parent_kind)
	)
	remember_handle(state.panes, rawget(declaration, "name"), pane_handle)
	call_api("cannot append " .. path, layout_api.append, state.handle, parent_layout, pane_handle)

	local nested_layout = rawget(declaration, "layout")
	if nested_layout ~= nil then
		local layout_handle = create_nested_layout(state.handle, nested_layout)
		remember_handle(state.layouts, rawget(nested_layout, "name"), layout_handle)
		call_api("cannot attach " .. path .. ".layout", pane_api.set_layout, state.handle, pane_handle, layout_handle)
		build_layout(state, layout_handle, nested_layout, path .. ".layout")
	end

	local controls = rawget(declaration, "controls")
	if controls ~= nil then
		for index, control_declaration in ipairs(controls) do
			local control_path = path .. ".controls[" .. index .. "]"
			local control_handle = call_api(
				"cannot create " .. control_path,
				control_api.create,
				state.handle,
				control_declaration
			)
			remember_handle(state.controls, rawget(control_declaration, "name"), control_handle)
			bind_control(state, control_declaration, control_handle)
			register_form_control(state, control_declaration, control_path)
			call_api(
				"cannot attach " .. control_path,
				pane_api.attach,
				state.handle,
				pane_handle,
				control_handle,
				rawget(control_declaration, "placement")
			)
			build_control_overlays(state, control_declaration, control_path)
		end
	end

	local embedded_form = rawget(declaration, "form")
	if embedded_form ~= nil then
		call_api(
			"cannot attach " .. path .. ".form",
			form_api.attach_to_pane,
			built_form_handles[embedded_form],
			pane_handle
		)
	end
end

--- Fill one already-created Layout from its ordered Pane declarations
-- @param state table construction state and returned handles
-- @param layout_handle userdata current Layout handle
-- @param declaration table Rows or Cols declaration
-- @param path string readable location
build_layout = function(state, layout_handle, declaration, path)
	local parent_kind = declaration_kinds[declaration]
	for index, pane_declaration in ipairs(declaration) do
		build_pane(state, layout_handle, pane_declaration, path .. "[" .. index .. "]", parent_kind)
	end
end

--- Create Form-owned Controls that a Window may present above its complete Form tree
-- @param state table construction state and returned handles
-- @param declarations table overlay Control declarations
local function build_overlays(state, declarations)
	if declarations == nil then
		return
	end

	for index, declaration in ipairs(declarations) do
		build_overlay_control(state, declaration, "Form.overlays[" .. index .. "]")
	end
end

--- Copy root Layout settings into the flat raw Form creation request
-- @param declaration table Form declaration
-- @return table options raw Form creation options
local function form_options(declaration)
	local root = rawget(declaration, "layout")
	local background = rawget(declaration, "background")
	if background == nil then
		background = theme.resolve(colors.form_bg)
	end

	return {
		name = rawget(declaration, "name"),
		type = rawget(declaration, "type"),
		background = background,
		root_layout = declaration_kinds[root],
		gap = rawget(root, "gap"),
		padding_left = rawget(root, "padding_left"),
		padding_top = rawget(root, "padding_top"),
		padding_right = rawget(root, "padding_right"),
		padding_bottom = rawget(root, "padding_bottom"),
	}
end

--- Destroy a detached partial Form and return a rollback error only if cleanup failed
-- @param form_handle userdata partial Form handle
-- @return string|nil rollback_error
local function rollback_form(form_handle)
	local ok, destroyed, destroy_error = pcall(form_api.destroy, form_handle)
	if not ok then
		return tostring(destroyed)
	end
	if not destroyed then
		return tostring(destroy_error or "unknown rollback failure")
	end

	return nil
end

--- Attach this built Form to a native Window or a Pane inside another Form
-- @param host integer|userdata native Window identity or checked Pane handle
-- @return boolean|nil attached
-- @return string|nil error_text
local function attach_form(self, host)
	if type(host) == "number" then
		return window_api.attach_form(host, self.handle)
	end

	return form_api.attach_to_pane(self.handle, host)
end

--- Detach this built Form from its current Window or Pane without destroying it
-- @return boolean|nil detached
-- @return string|nil error_text
local function detach_form(self)
	return form_api.detach(self.handle)
end

--- Add one rollback failure to the original opening error
-- @param error_text string original operation failure
-- @param action string rollback action written for a human reader
-- @param called boolean whether the raw function returned normally
-- @param result any raw success result or raised error
-- @param detail string|nil raw returned error
-- @return string combined_error
local function add_rollback_error(error_text, action, called, result, detail)
	if not called then
		return error_text .. "; " .. action .. " raised: " .. tostring(result)
	end
	if not result then
		return error_text .. "; " .. action .. " failed: " .. tostring(detail or "unknown error")
	end

	return error_text
end

--- Copy supported native Window settings and derive the frame from the Form type
-- Visibility and native furniture belong to the Form lifecycle
-- @param self table built Form
-- @param options table requested native Window settings
-- @return table create_options hidden native Window settings
local function form_window_options(self, options)
	assert(type(options) == "table", "Form.open_window expects a table")
	local copied = {}
	for key, value in next, options do
		if key == "visible" then
			error("Form.open_window manages visibility; do not provide 'visible'", 3)
		end
		if type(key) ~= "string" or not window_fields[key] then
			error("Form.open_window received unknown field " .. tostring(key), 3)
		end

		copied[key] = value
	end

	copied.visible = false
	local form_type = call_api("cannot read Form type", form_api.get_type, self.handle)
	if form_type == "dialog" then
		if copied.resizable == true then
			error("a dialog Form cannot open a resizable Window", 3)
		end

		copied.frame = "dialog"
		copied.resizable = false
	else
		copied.frame = "standard"
	end

	return copied
end

--- Open this detached Form in one hidden-before-ready native Window
-- Every failed stage unwinds the native Window and preserves the Form when possible
-- @param options table title, size, owner, modality, and native Window behavior
-- @return integer|nil window_id
-- @return string|nil error_text
local function open_form_window(self, options)
	if not form_api.is_valid(self.handle) then
		return nil, "Form no longer exists"
	end
	if form_api.is_ui_work_active() then
		return nil, "Form.open_window cannot run during active UI work; use qui.defer"
	end

	local host, host_error = form_api.get_host(self.handle)
	if host_error then
		return nil, host_error
	end
	if host then
		return nil, "Form is already attached to a " .. host.kind
	end

	local create_options = form_window_options(self, options)
	local created, window_id, create_error = pcall(window_api.create, create_options)
	if not created then
		return nil, tostring(window_id)
	end
	if not window_id then
		return nil, tostring(create_error or "could not create Window")
	end

	local attached, attach_result, attach_error = pcall(window_api.attach_form, window_id, self.handle)
	if not attached or not attach_result then
		local error_text = attached and tostring(attach_error or "could not attach Form") or tostring(attach_result)
		local closed, close_result, close_error = pcall(window_api.close, window_id)
		error_text = add_rollback_error(error_text, "hidden Window cleanup", closed, close_result, close_error)

		return nil, error_text
	end

	local shown, show_result, show_error = pcall(window_api.set_visible, window_id, true)
	if not shown or not show_result then
		local error_text = shown and tostring(show_error or "could not show Window") or tostring(show_result)
		local detached, detach_result, detach_error = pcall(window_api.detach_form, window_id)
		error_text = add_rollback_error(error_text, "Form detachment", detached, detach_result, detach_error)
		local closed, close_result, close_error = pcall(window_api.close, window_id)
		error_text = add_rollback_error(error_text, "hidden Window cleanup", closed, close_result, close_error)

		return nil, error_text
	end

	return window_id
end

--- Close this Form's native window and finish its Lua lifecycle once
-- The first result requested before deferred teardown is the result delivered
-- @param result string|nil readable result such as 'accept' or 'cancel'
-- @return boolean|nil accepted
-- @return string|nil error_text
local function close_form(self, result)
	if result == nil then
		result = "closed"
	end
	assert(type(result) == "string", "Form close result must be a string")

	return form_api.close(self.handle, result)
end

--- Destroy this Form without closing a window or delivering completion
-- @return boolean|nil destroyed
-- @return string|nil error_text
local function destroy_form(self)
	return form_api.destroy(self.handle)
end

--- Check whether this Form still refers to a live C++ Form
-- @return boolean valid
local function form_is_valid(self)
	return form_api.is_valid(self.handle)
end

--- Move logical keyboard focus to one live Control belonging to this Form
-- @param control table original live Control object
-- @return boolean|nil focused
-- @return string|nil error_text
local function focus_form_control(self, control)
	local binding = control_bindings[control]
	if not binding or binding.form ~= self.handle then
		return nil, "Control does not belong to this built Form"
	end

	return form_api.focus(self.handle, binding.control)
end

--- Open or move one Form-owned Control in the Window overlay layer
-- The rectangle uses Form-local coordinates but is clipped only by the native Window
-- @param control table overlay Control declared in Form.overlays
-- @param bounds table x, y, width, and height
-- @return boolean|nil shown
-- @return string|nil error_text
local function show_form_overlay(self, control, bounds)
	local binding = control_bindings[control]
	if not binding or binding.form ~= self.handle then
		return nil, "Control does not belong to this built Form"
	end
	assert(type(bounds) == "table", "Overlay bounds must be a table")

	return form_api.set_overlay(self.handle, binding.control, bounds)
end

--- Close one Control in this Form's overlay layer while retaining its Lua state
-- @param control table overlay Control declared in Form.overlays
-- @return boolean|nil hidden
-- @return string|nil error_text
local function hide_form_overlay(self, control)
	local binding = control_bindings[control]
	if not binding or binding.form ~= self.handle then
		return nil, "Control does not belong to this built Form"
	end

	return form_api.set_overlay(self.handle, binding.control, nil)
end

--- Build one detached durable Form from a complete declarative Lua tree
-- @param declaration table Form identity, appearance, and root Layout
-- @return table|nil built_form handle and named object collections
-- @return string|nil error_text construction failure
function qui.form(declaration)
	local validated, validation_error = pcall(validate_form, declaration)
	if not validated then
		return nil, tostring(validation_error)
	end

	local form_handle
	local bound_controls = {}
	local built, result = pcall(function()
		form_handle = call_api("cannot create Form", form_api.create, form_options(declaration))
		local root_layout = call_api("cannot read root Layout", form_api.root_layout, form_handle)
		local initial_focus = rawget(declaration, "initial_focus")
		local form_type = rawget(declaration, "type") or "standard"
		local state = {
			handle = form_handle,
			type = form_type,
			layouts = { root_layout },
			panes = {},
			controls = {},
			initial_focus = initial_focus,
			event = nil,
			user_event = rawget(declaration, "event"),
			accelerators = {},
			shortcuts = {},
			commands = {},
			command_set = {},
			dialog_actions = {},
			on_close_request = rawget(declaration, "on_close_request"),
			on_complete = rawget(declaration, "on_complete"),
			attach = attach_form,
			detach = detach_form,
			open_window = open_form_window,
			close = close_form,
			destroy = destroy_form,
			is_valid = form_is_valid,
			focus = focus_form_control,
			show_overlay = show_form_overlay,
			hide_overlay = hide_form_overlay,
			bound_controls = bound_controls,	-- Rollback list kept private after construction
		}
		state.layouts.root = root_layout
		for index, command in ipairs(rawget(declaration, "commands") or {}) do
			register_form_command(state, command, "Form.commands[" .. index .. "]")
		end
		build_layout(state, root_layout, rawget(declaration, "layout"), "Form.layout")
		build_overlays(state, rawget(declaration, "overlays"))
		if state.user_event ~= nil or next(state.accelerators) ~= nil
			or next(state.shortcuts) ~= nil
			or next(state.dialog_actions) ~= nil
		then
			state.event = handle_form_event
		end
		if initial_focus ~= nil then
			local binding = control_bindings[initial_focus]
			call_api("cannot set initial Form focus", form_api.set_initial_focus, state.handle, binding.control)
		end
		call_api("cannot bind Form lifecycle", form_api.bind_lifecycle, state.handle, state)

		return state
	end)

	if built then
		result.bound_controls = nil
		result.command_set = nil
		built_form_handles[result] = result.handle
		if rawget(declaration, "background") == nil then
			themed_forms[result] = true
		end
		return result
	end

	unbind_controls(bound_controls)
	local rollback_error = form_handle and rollback_form(form_handle) or nil
	if rollback_error then
		return nil, tostring(result) .. "; could not roll back partial Form: " .. rollback_error
	end

	return nil, tostring(result)
end

return qui
