class_name NotepadInkChoice
extends Control

signal item_selected(index: int)

@export var ink_color := Color(0.19, 0.16, 0.12, 1.0)
@export_range(0.0, 16.0, 0.25) var outline_padding: float = 3.0:
	set(value):
		outline_padding = value
		if is_node_ready():
			_ink_outline.padding = outline_padding
			_ink_outline.queue_redraw()
@export var outline_outsets := Vector4.ZERO:
	set(value):
		outline_outsets = value
		if is_node_ready():
			_apply_outline_geometry()

@onready var _ink_outline: NotepadInkOutline = %InkOutline
@onready var _displayed_value: Label = %DisplayedValue
@onready var _choice_panel: Control = %ChoicePanel
@onready var _choice_buttons: Array[Button] = [
	%CatchOrderChoice,
	%NameChoice,
	%RarityChoice,
]

var disabled: bool = false:
	set(value):
		disabled = value
		if is_node_ready():
			_apply_interaction_state()
var _items: Array[String] = []
var _item_ids: Array[int] = []
var _selected_index: int = 0
var _hovered: bool = false
var _focused: bool = false
var _opened_with_mouse: bool = false


func _ready() -> void:
	_ink_outline.padding = outline_padding
	_apply_outline_geometry()
	mouse_entered.connect(_set_hovered.bind(true))
	mouse_exited.connect(_set_hovered.bind(false))
	focus_entered.connect(_set_focused.bind(true))
	focus_exited.connect(_set_focused.bind(false))
	gui_input.connect(_on_display_gui_input)
	for index: int in _choice_buttons.size():
		var button: Button = _choice_buttons[index]
		button.pressed.connect(_select_index.bind(index))
		button.gui_input.connect(_on_choice_gui_input.bind(index))
	_choice_panel.hide()
	_apply_paper_style()
	_apply_interaction_state()


func add_item(item_text: String, item_id: int = -1) -> void:
	if _items.size() >= _choice_buttons.size():
		return
	var index: int = _items.size()
	_items.append(item_text)
	_item_ids.append(item_id if item_id >= 0 else index)
	_choice_buttons[index].text = item_text
	_choice_buttons[index].show()
	if index == _selected_index:
		_displayed_value.text = item_text


func select(index: int) -> void:
	if index < 0 or index >= _items.size():
		return
	_selected_index = index
	_displayed_value.text = _items[index]


func get_selected_id() -> int:
	if _selected_index < 0 or _selected_index >= _item_ids.size():
		return -1
	return _item_ids[_selected_index]


func refresh_ink_state() -> void:
	_refresh_ink()


func set_choice_font_size(font_size: int) -> void:
	_displayed_value.add_theme_font_size_override("font_size", font_size)
	for button: Button in _choice_buttons:
		button.add_theme_font_size_override("font_size", font_size)


func close_choices() -> void:
	if not _choice_panel.visible:
		return
	_choice_panel.hide()
	if _opened_with_mouse:
		_opened_with_mouse = false
		if has_focus():
			release_focus()
	_refresh_ink()


func _apply_outline_geometry() -> void:
	_ink_outline.offset_left = -outline_outsets.x
	_ink_outline.offset_top = -outline_outsets.y
	_ink_outline.offset_right = outline_outsets.z
	_ink_outline.offset_bottom = outline_outsets.w
	_ink_outline.queue_redraw()


func _draw() -> void:
	var underline_y: float = size.y - 5.0
	var underline := PackedVector2Array([
		Vector2(7.0, underline_y),
		Vector2(size.x * 0.34, underline_y + 0.7),
		Vector2(size.x * 0.68, underline_y - 0.5),
		Vector2(size.x - 8.0, underline_y + 0.2),
	])
	var underline_color: Color = ink_color
	underline_color.a = 0.48
	draw_polyline(underline, underline_color, 1.1, true)


func _gui_input(event: InputEvent) -> void:
	if disabled:
		return
	if event.is_action_pressed("ui_accept"):
		_open_choices(false)
		accept_event()
	elif event.is_action_pressed("ui_cancel") and _choice_panel.visible:
		close_choices()
		accept_event()


func _input(event: InputEvent) -> void:
	if not _choice_panel.visible:
		return
	if event.is_action_pressed("ui_cancel"):
		close_choices()
		get_viewport().set_input_as_handled()
		return
	if (
		event is InputEventMouseButton
		and event.button_index == MOUSE_BUTTON_LEFT
		and event.pressed
	):
		var mouse_position: Vector2 = event.position
		if (
			not get_global_rect().has_point(mouse_position)
			and not _choice_panel.get_global_rect().has_point(mouse_position)
		):
			close_choices()


func _on_display_gui_input(event: InputEvent) -> void:
	if (
		not disabled
		and event is InputEventMouseButton
		and event.button_index == MOUSE_BUTTON_LEFT
		and event.pressed
	):
		_open_choices(true)
		accept_event()


func _on_choice_gui_input(event: InputEvent, index: int) -> void:
	if event.is_action_pressed("ui_cancel"):
		close_choices()
		get_viewport().set_input_as_handled()
	elif event.is_action_pressed("ui_up"):
		_choice_buttons[posmod(index - 1, _choice_buttons.size())].grab_focus()
		get_viewport().set_input_as_handled()
	elif event.is_action_pressed("ui_down"):
		_choice_buttons[(index + 1) % _choice_buttons.size()].grab_focus()
		get_viewport().set_input_as_handled()


func _open_choices(from_mouse: bool) -> void:
	if disabled or _choice_panel.visible:
		return
	_opened_with_mouse = from_mouse
	_choice_panel.show()
	_choice_panel.move_to_front()
	if not from_mouse:
		_choice_buttons[_selected_index].grab_focus()
	_refresh_ink()


func _select_index(index: int) -> void:
	if disabled or index < 0 or index >= _items.size():
		return
	select(index)
	close_choices()
	item_selected.emit(get_selected_id())


func _set_hovered(value: bool) -> void:
	_hovered = value
	_refresh_ink()


func _set_focused(value: bool) -> void:
	_focused = value
	_refresh_ink()


func _refresh_ink() -> void:
	if is_node_ready():
		_ink_outline.set_mark_strength(
			0.88 if (_hovered or _focused) and not disabled else 0.0
		)


func _apply_interaction_state() -> void:
	_displayed_value.modulate.a = 0.42 if disabled else 1.0
	focus_mode = Control.FOCUS_NONE if disabled else Control.FOCUS_ALL
	mouse_filter = (
		Control.MOUSE_FILTER_IGNORE
		if disabled
		else Control.MOUSE_FILTER_STOP
	)
	for button: Button in _choice_buttons:
		button.disabled = disabled
	if disabled:
		close_choices()
	_refresh_ink()


func _apply_paper_style() -> void:
	var empty := StyleBoxEmpty.new()
	var hover := StyleBoxFlat.new()
	hover.bg_color = Color(0.82, 0.775, 0.63, 1.0)
	hover.set_border_width_all(0)
	hover.set_corner_radius_all(0)
	hover.anti_aliasing = false
	_displayed_value.add_theme_color_override("font_color", ink_color)
	for button: Button in _choice_buttons:
		for state: StringName in [
			&"normal",
			&"disabled",
		]:
			button.add_theme_stylebox_override(state, empty)
		for state: StringName in [
			&"hover",
			&"pressed",
			&"focus",
		]:
			button.add_theme_stylebox_override(state, hover)
		button.add_theme_color_override("font_color", ink_color)
		button.add_theme_color_override(
			"font_hover_color",
			ink_color.darkened(0.08),
		)
		button.add_theme_color_override(
			"font_focus_color",
			ink_color.darkened(0.08),
		)
		button.add_theme_color_override(
			"font_pressed_color",
			ink_color.darkened(0.14),
		)
