-- lx-bundle:true

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

--- Provide a reusable Lua-owned text Label Control
-- @module qui.label

local qui = require("qui")
local colors = require("qui.colors")
local Font = require("qui.font")
local theme = require("qui.theme")

local Label = {}
local color_flags = setmetatable({}, { __mode = "k" })	-- One reusable disabled flag follows each Label

--- Select the complete Label background or its shared input text color
-- @param self table Label control
-- @param type string requested visual type
-- @param flags table current Label conditions
-- @return string color_identifier
function Label.default_color(self, type, flags)
	if type == "background" then
		return flags.grayed and colors.label_disabled_bg or colors.label_bg
	elseif type == "text" then
		return flags.grayed and colors.label_disabled_text or colors.label_text
	end

	return nil
end

--- Create one text Label with an optional complete background for use inside 'qui.pane.controls'
-- @param options table text, font, color, background, placement, and ordinary Control settings
-- @return table control unrestricted Lua Control declaration
function Label.new(options)
	assert(type(options) == "table", "Label expects a table")
	assert(options.text == nil or type(options.text) == "string", "Label text must be a string")
	assert(options.on_color == nil or type(options.on_color) == "function", "Label on_color must be a function")
	assert(options.default_color == nil, "Label default_color is fixed; use on_color for dynamic changes")

	local control = options
	control.text = control.text or ""
	control.font = control.font or Font.default
	assert(control.font, "Label requires a font")
	control.default_color = Label.default_color
	control.width = control.width
	control.height = control.height
	control.enabled = control.enabled ~= false
	control.focusable = control.focusable == true
	control.accepts_pointer = control.accepts_pointer == true
	color_flags[control] = {
		grayed = not control.enabled,
	}

	--- Measure the current text without allocating drawing resources
	function control:measure(control_canvas, minimum_width, minimum_height, maximum_width, maximum_height)
		local text_width, text_height = control_canvas:measure_text(self.font, self.text)
		local natural_width = self.width or text_width
		local natural_height = self.height or text_height
		local width = math.max(minimum_width, math.min(natural_width, maximum_width))
		local height = math.max(minimum_height, math.min(natural_height, maximum_height))

		return width, height
	end

	--- Draw the complete Label background before its vertically centered text
	function control:draw(control_canvas)
		local width, height = control_canvas:size()
		local flags = assert(color_flags[self], "Label color flags are unavailable")
		flags.grayed = not self.enabled
		local _, text_height = control_canvas:measure_text(self.font, self.text)
		local text_y = math.floor((height - text_height) / 2)
		local background = self.background and theme.resolve(self.background) or
			self:get_color("background", flags)
		local color = self.color and theme.resolve(self.color) or
			self:get_color("text", flags)

		control_canvas:fill_rect(0, 0, width, height, background)
		control_canvas:draw_text(self.font, self.text, 0, text_y, color)
	end

	--- Change visible text and remeasure the owning Form when already built
	-- @param text string new Label text
	-- @return boolean|nil changed
	-- @return string|nil error_text
	function control:set_text(text)
		assert(type(text) == "string", "Label text must be a string")
		if self.text == text then
			return true
		end

		local previous_text = self.text
		self.text = text
		local changed, change_error = self:relayout()
		if not changed then
			self.text = previous_text
			return nil, change_error
		end

		return true
	end

	return qui.control(control)
end

-- Callable module syntax keeps declarations readable as 'Label { ... }'
setmetatable(Label, {
	__call = function(_, options)
		return Label.new(options)
	end,
})

return Label
