class_name SurfaceDrawingToolbar
extends Control

const UtilityPageStyleType = preload("res://ui/utility_page_style.gd")
const TOOLBAR_WIDTH: float = 580.0
const TOOLBAR_HEIGHT: float = 373.0
const COLOR_RAIL_WIDTH: float = 46.0

@onready var _mode_button: Button = %ModeButton
@onready var _brush_option: OptionButton = %BrushOption
@onready var _grid_option: OptionButton = %GridOption
@onready var _color_list: VBoxContainer = %ColorList
@onready var _top_panel: PanelContainer = %TopPanel
@onready var _color_panel: PanelContainer = %ColorPanel
@onready var _eraser_button: Button = %EraserButton
@onready var _undo_button: Button = %UndoButton
@onready var _hide_guide_button: Button = %HideGuideButton
@onready var _restore_guide_button: Button = %RestoreGuideButton
@onready var _finalize_guide_button: Button = %FinalizeGuideButton
@onready var _close_button: Button = %CloseButton

var _service: NetworkSurfaceDrawingService
var _unlocks: PlayerArtUnlocks
var _color_buttons: Dictionary[StringName, Button] = {}
var _dock_right: bool = true


func _ready() -> void:
	UtilityPageStyleType.apply_page(self)
	_apply_toolbar_panel(_top_panel)
	_apply_toolbar_panel(_color_panel)
	UtilityPageStyleType.apply_ocean_button(_mode_button)
	UtilityPageStyleType.apply_ocean_button(_brush_option)
	UtilityPageStyleType.apply_ocean_button(_grid_option)
	for button: Button in _action_buttons():
		UtilityPageStyleType.apply_ocean_button(button)
		button.custom_minimum_size = Vector2(48, 44)
		_make_button_round(button, 22)
	_mode_button.custom_minimum_size = Vector2(48, 48)
	_make_button_round(_mode_button, 24)
	_mode_button.pressed.connect(_toggle_mode)
	_eraser_button.pressed.connect(_toggle_eraser)
	_undo_button.pressed.connect(_undo_last_stroke)
	_hide_guide_button.pressed.connect(
		_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.HIDE)
	)
	_restore_guide_button.pressed.connect(
		_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.RESTORE)
	)
	_finalize_guide_button.pressed.connect(
		_arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.FINALIZE)
	)
	_close_button.pressed.connect(_close_toolbar)
	_brush_option.item_selected.connect(_select_brush)
	_grid_option.item_selected.connect(_select_grid)
	_build_options()
	_apply_dock_side()
	hide()


func _apply_toolbar_panel(panel: PanelContainer) -> void:
	var style: StyleBoxFlat = UtilityPageStyleType.panel_style()
	style.content_margin_left = 6.0
	style.content_margin_top = 6.0
	style.content_margin_right = 6.0
	style.content_margin_bottom = 6.0
	style.set_corner_radius_all(10)
	panel.add_theme_stylebox_override("panel", style)


func _action_buttons() -> Array[Button]:
	return [
		_eraser_button,
		_undo_button,
		_hide_guide_button,
		_restore_guide_button,
		_finalize_guide_button,
		_close_button,
	]


func _make_button_round(button: Button, radius: int) -> void:
	for style_name: StringName in [
		&"normal", &"hover", &"pressed", &"focus", &"disabled",
	]:
		var existing: StyleBox = button.get_theme_stylebox(style_name)
		var style := existing.duplicate() as StyleBoxFlat
		if style == null:
			continue
		style.set_corner_radius_all(radius)
		button.add_theme_stylebox_override(style_name, style)


func setup(
	service: NetworkSurfaceDrawingService,
	unlocks: PlayerArtUnlocks,
) -> void:
	_service = service
	_unlocks = unlocks
	if _service != null and not _service.hud_state_changed.is_connected(
		_on_service_state_changed
	):
		_service.hud_state_changed.connect(_on_service_state_changed)
	if _unlocks != null and not _unlocks.unlocks_changed.is_connected(
		_on_unlocks_changed
	):
		_unlocks.unlocks_changed.connect(_on_unlocks_changed)
	_refresh_unlocks()


func set_dock_right(should_dock_right: bool) -> void:
	_dock_right = should_dock_right
	if is_node_ready():
		_apply_dock_side()


func is_docked_right() -> bool:
	return _dock_right


func _apply_dock_side() -> void:
	anchor_left = 1.0 if _dock_right else 0.0
	anchor_right = anchor_left
	offset_left = -TOOLBAR_WIDTH if _dock_right else 0.0
	offset_right = 0.0 if _dock_right else TOOLBAR_WIDTH
	offset_top = 0.0
	offset_bottom = TOOLBAR_HEIGHT
	_color_panel.offset_left = (
		TOOLBAR_WIDTH - COLOR_RAIL_WIDTH if _dock_right else 0.0
	)
	_color_panel.offset_right = (
		TOOLBAR_WIDTH if _dock_right else COLOR_RAIL_WIDTH
	)


func owns_pointer_event(event: InputEvent) -> bool:
	if not visible:
		return false
	if _brush_option.get_popup().visible or _grid_option.get_popup().visible:
		return true
	if event is InputEventMouse:
		var pointer_position: Vector2 = (event as InputEventMouse).position
		return (
			_top_panel.get_global_rect().has_point(pointer_position)
			or _color_panel.get_global_rect().has_point(pointer_position)
		)
	return false


func _build_options() -> void:
	_brush_option.clear()
	for brush_size: int in PlayerArtUnlocks.BRUSH_SIZES:
		_brush_option.add_item("%d×" % brush_size, brush_size)
	_grid_option.clear()
	for grid_size: int in PlayerArtUnlocks.GRID_SIZES:
		_grid_option.add_item("%d×" % grid_size, grid_size)
	for child: Node in _color_list.get_children():
		child.queue_free()
	_color_buttons.clear()
	for color_id: StringName in SurfaceDrawingPalette.get_color_ids():
		var button := Button.new()
		button.custom_minimum_size = Vector2(34, 34)
		button.focus_mode = Control.FOCUS_ALL
		button.tooltip_text = SurfaceDrawingPalette.get_display_name(color_id)
		button.pressed.connect(_select_color.bind(color_id))
		_color_list.add_child(button)
		_color_buttons[color_id] = button
		_apply_color_button_style(button, color_id, false)


func _refresh_unlocks() -> void:
	if not is_node_ready() or _unlocks == null:
		return
	var brush_popup: PopupMenu = _brush_option.get_popup()
	for index: int in range(_brush_option.item_count):
		var brush_size: int = _brush_option.get_item_id(index)
		brush_popup.set_item_disabled(
			index, not _unlocks.is_brush_size_unlocked(brush_size)
		)
	var grid_popup: PopupMenu = _grid_option.get_popup()
	for index: int in range(_grid_option.item_count):
		var grid_size: int = _grid_option.get_item_id(index)
		grid_popup.set_item_disabled(
			index, not _unlocks.is_grid_size_unlocked(grid_size)
		)
	for color_id: StringName in _color_buttons:
		var button: Button = _color_buttons[color_id]
		button.disabled = not _unlocks.is_color_unlocked(color_id)
		_apply_color_button_style(
			button,
			color_id,
			(
				_service != null
				and not _service.is_eraser_mode()
				and _service.get_color_id() == color_id
			),
		)


func _apply_color_button_style(
	button: Button,
	color_id: StringName,
	selected: bool,
) -> void:
	var color: Color = (
		SurfaceDrawingPalette.get_color(color_id)
		if not button.disabled
		else UtilityPageStyleType.OCEAN_DISABLED
	)
	for style_name: StringName in [&"normal", &"hover", &"pressed", &"focus"]:
		var style := StyleBoxFlat.new()
		style.bg_color = color.lightened(0.12) if style_name == &"hover" else color
		style.set_border_width_all(3 if selected else 0)
		style.border_color = UtilityPageStyleType.OCEAN_TEXT_PRIMARY
		style.set_corner_radius_all(17)
		button.add_theme_stylebox_override(style_name, style)
	var disabled_style := StyleBoxFlat.new()
	disabled_style.bg_color = UtilityPageStyleType.OCEAN_DISABLED
	disabled_style.set_corner_radius_all(17)
	button.add_theme_stylebox_override("disabled", disabled_style)


func _toggle_mode() -> void:
	if _service != null:
		_service.set_placement_mode(not _service.is_placement_mode())


func _select_brush(index: int) -> void:
	if _service != null:
		_service.set_brush_size(_brush_option.get_item_id(index))


func _select_grid(index: int) -> void:
	if _service != null:
		_service.set_grid_size(_grid_option.get_item_id(index))


func _select_color(color_id: StringName) -> void:
	if _service != null:
		_service.set_color_id(color_id)


func _toggle_eraser() -> void:
	if _service != null:
		_service.set_eraser_mode(not _service.is_eraser_mode())


func _undo_last_stroke() -> void:
	if _service != null:
		_service.request_undo_last_stroke()


func _arm_guide_action(action: int) -> void:
	if _service != null:
		_service.arm_guide_action(action)


func _close_toolbar() -> void:
	if _service != null:
		_service.deactivate()


func _on_unlocks_changed(_unlock_mask: int) -> void:
	_refresh_unlocks()


func _on_service_state_changed(
	is_active: bool,
	mode_name: String,
	_color_name: String,
	_color_value: Color,
	brush_size: int,
	grid_size: int,
	_status: String,
) -> void:
	visible = is_active
	if not is_active:
		return
	_mode_button.text = "▦" if mode_name == "place grid" else "●"
	_mode_button.tooltip_text = (
		"Switch to marker mode"
		if mode_name == "place grid"
		else "Switch to grid mode"
	)
	_select_option_by_id(_brush_option, brush_size)
	_select_option_by_id(_grid_option, grid_size)
	_refresh_action_state()
	_refresh_unlocks()


func _refresh_action_state() -> void:
	if _service == null:
		return
	_eraser_button.button_pressed = _service.is_eraser_mode()
	var guide_action: int = _service.get_armed_guide_action()
	_hide_guide_button.button_pressed = (
		guide_action == NetworkSurfaceDrawingService.GuideAction.HIDE
	)
	_restore_guide_button.button_pressed = (
		guide_action == NetworkSurfaceDrawingService.GuideAction.RESTORE
	)
	_finalize_guide_button.button_pressed = (
		guide_action == NetworkSurfaceDrawingService.GuideAction.FINALIZE
	)


func _select_option_by_id(option: OptionButton, item_id: int) -> void:
	for index: int in range(option.item_count):
		if option.get_item_id(index) == item_id:
			option.select(index)
			return
