extends Node

signal mode_changed(mode: Mode)

enum Mode { KB_MOUSE, CONTROLLER }
enum ControllerType { XBOX, SONY, NINTENDO, STEAM, KNOCKOFF }

const LEFT_TRIGGER_SPRITE_INDEX: int = 20
const RIGHT_TRIGGER_SPRITE_INDEX: int = 21
const LEFT_STICK_SPRITE_INDEX: int = 23
const RIGHT_STICK_SPRITE_INDEX: int = 24

@export var mode: Mode = Mode.KB_MOUSE

@export var _controller: ControllerType = ControllerType.XBOX:
	set(value):
		_set_current_prompts(value)
		_controller = value

@export var _prompt_grid_size: int = 5
@export var _prompt_icon_size: int = 60

var _prompts_xbox := preload("res://assets/textures/ui/prompts_xbox.png")
var _prompts_sony := preload("res://assets/textures/ui/prompts_sony.png")
var _prompts_nintendo := preload("res://assets/textures/ui/prompts_nintendo.png")
var _prompts_steam := preload("res://assets/textures/ui/prompts_steam.png")
var _prompts_knockoff := preload("res://assets/textures/ui/prompts_knockoff.png")

var _current_prompts: CompressedTexture2D = _prompts_xbox


func _ready() -> void:
	Debugger.add_event("mode_changed")
	mode_changed.connect(
		func(new_mode: Mode) -> void:
			Debugger.event_emitted("mode_changed", [Mode.keys()[new_mode]])
	)
	_get_controller_type()


func _input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		return

	var event_mode := get_event_mode(event)
	if mode == Mode.KB_MOUSE and event_mode == Mode.CONTROLLER:
		mode = Mode.CONTROLLER
		mode_changed.emit(mode)

	if mode == Mode.CONTROLLER and event_mode == Mode.KB_MOUSE:
		mode = Mode.KB_MOUSE
		mode_changed.emit(mode)

	if Debugger.show_debug() and event.is_pressed():
		Debugger.text("input", _get_event_prompt_current_mode(event))


func get_action_prompt(action: StringName) -> String:
	var events := InputMap.action_get_events(action)
	var bbcode: PackedStringArray = []
	for event in events:
		var event_bbcode := _get_event_prompt_current_mode(event)
		if event_bbcode:
			bbcode.append(event_bbcode)

	if bbcode.size() != 0:
		return " / ".join(bbcode)

	return _get_event_prompt(events[0]) if events.size() != 0 else "???"


func get_event_mode(event: InputEvent) -> Mode:
	if event is InputEventJoypadButton or event is InputEventJoypadMotion:
		return Mode.CONTROLLER

	return Mode.KB_MOUSE


func _get_event_prompt_current_mode(event: InputEvent) -> String:
	var event_mode := get_event_mode(event)
	if mode == Mode.CONTROLLER and event_mode == Mode.CONTROLLER:
		return _get_controller_prompt(event)
	if mode == Mode.KB_MOUSE and event_mode == Mode.KB_MOUSE:
		return _get_kb_mouse_prompt(event)
	return ""


func _get_event_prompt(event: InputEvent) -> String:
	var event_mode := get_event_mode(event)
	if event_mode == Mode.CONTROLLER:
		return _get_controller_prompt(event)
	if event_mode == Mode.KB_MOUSE:
		return _get_kb_mouse_prompt(event)
	return event.as_text()


func _get_kb_mouse_prompt(event: InputEvent) -> String:
	if event is InputEventKey:
		var button_event := event as InputEventKey
		return button_event.as_text_physical_keycode()

	if event is InputEventMouseButton:
		return event.as_text()

	return event.as_text()


func _get_controller_prompt(event: InputEvent) -> String:
	if event is InputEventJoypadButton:
		var button_event := event as InputEventJoypadButton
		return _get_prompt_sprite(button_event.button_index)

	if event is InputEventJoypadMotion:
		var motion_event := event as InputEventJoypadMotion
		if motion_event.axis == JOY_AXIS_LEFT_X or motion_event.axis == JOY_AXIS_LEFT_Y:
			return _get_prompt_sprite(LEFT_STICK_SPRITE_INDEX)
		if (
			motion_event.axis == JOY_AXIS_RIGHT_X
			or motion_event.axis == JOY_AXIS_RIGHT_Y
		):
			return _get_prompt_sprite(RIGHT_STICK_SPRITE_INDEX)
		if motion_event.axis == JOY_AXIS_TRIGGER_LEFT:
			return _get_prompt_sprite(LEFT_TRIGGER_SPRITE_INDEX)
		if motion_event.axis == JOY_AXIS_TRIGGER_RIGHT:
			return _get_prompt_sprite(RIGHT_TRIGGER_SPRITE_INDEX)

	return event.as_text()


func _get_prompt_sprite(index: int) -> String:
	var region := _get_prompt_sprite_region(index)
	return (
		"[img region=%d,%d,%d,%d height=%d width=%d]%s[/img]"
		% [
			region.position.x,
			region.position.y,
			region.size.x,
			region.size.y,
			_prompt_icon_size,
			_prompt_icon_size,
			_current_prompts.resource_path
		]
	)


func _get_prompt_sprite_region(index: int) -> Rect2i:
	var corner_x: int = index % _prompt_grid_size
	var corner_y: int = index / _prompt_grid_size
	var sprite_size: int = _current_prompts.get_width() / _prompt_grid_size
	return Rect2i(
		corner_x * sprite_size,
		corner_y * sprite_size,
		sprite_size,
		sprite_size,
	)


func _get_controller_type() -> void:
	var controller_name := Input.get_joy_name(0).to_lower()
	if (
		controller_name.begins_with("ps")
		or controller_name.contains("sony")
		or controller_name.contains("playstation")
	):
		_controller = ControllerType.SONY
	elif controller_name.contains("steam"):
		_controller = ControllerType.STEAM
	elif controller_name.contains("nintendo"):
		_controller = ControllerType.NINTENDO
	else:
		_controller = ControllerType.XBOX


func _set_current_prompts(type: ControllerType) -> void:
	match type:
		ControllerType.XBOX:
			_current_prompts = _prompts_xbox
		ControllerType.SONY:
			_current_prompts = _prompts_sony
		ControllerType.NINTENDO:
			_current_prompts = _prompts_nintendo
		ControllerType.STEAM:
			_current_prompts = _prompts_steam
		ControllerType.KNOCKOFF:
			_current_prompts = _prompts_knockoff
