# Copyright (C) 2022-2023 John Pennycook
# SPDX-License-Identifier: MIT
@tool
class_name InputPromptManager
extends Node
## Singleton class for managing [InputPrompt]s.
##
## Singleton class for managing [InputPrompt]s.
## [br][br]
## [b]Note[/b]: An instance of [InputPromptManager] is autoloaded under the name
## PromptManager.

## Emitted when the preferred icons change. If the preferred icons are set to
## "Automatic", emitted whenever the input device changes.
signal icons_changed

## The icons currently used by [ActionPrompt] nodes.
var icons := InputPrompt.Icons.XBOX:
	get:
		# In the Editor, InputMap reflects Editor settings
		# Pick a default so there's something to render
		if Engine.is_editor_hint():
			return InputPrompt.Icons.XBOX
		return icons

## The icons currently used by [JoypadButtonPrompt] and [JoypadMotionPrompt] nodes.
var joy_icons = InputPrompt.Icons.XBOX:
	get:
		# In the Editor, InputMap reflects Editor settings
		# Pick a default so there's something to render
		if Engine.is_editor_hint():
			return InputPrompt.Icons.XBOX
		return joy_icons

## The preferred icons to be used by [ActionPrompt], [JoypadButtonPrompt] and [JoypadMotionPrompt]
## nodes. When set to a specific value, all nodes with "Automatic" icons will be overridden to use
## the specified value.
var preferred_icons := InputPrompt.Icons.AUTOMATIC:
	set(value):
		preferred_icons = value
		if preferred_icons == null or preferred_icons == InputPrompt.Icons.AUTOMATIC:
			icons = InputPrompt.Icons.XBOX
		else:
			icons = value
		emit_signal("icons_changed")

## The deadzone value used to detect joypad activity. The default value is determined by the
## "addons/input_prompts/joypad_detection_deadzone" setting in [ProjectSettings].
var joypad_detection_deadzone := ProjectSettings.get_setting(
	"addons/input_prompts/joypad_detection_deadzone", 0.5
)

var _keyboard_textures: KeyboardTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/keyboard", "res://addons/input_prompts/key_prompt/keys.tres"
	)
)
var _mouse_button_textures: MouseButtonTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/mouse_buttons",
		"res://addons/input_prompts/mouse_button_prompt/buttons.tres"
	)
)
var _nintendo_button_textures: JoypadButtonTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_buttons/nintendo",
		"res://addons/input_prompts/joypad_button_prompt/nintendo.tres"
	)
)
var _sony_button_textures: JoypadButtonTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_buttons/sony",
		"res://addons/input_prompts/joypad_button_prompt/sony.tres"
	)
)
var _xbox_button_textures: JoypadButtonTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_buttons/xbox",
		"res://addons/input_prompts/joypad_button_prompt/xbox.tres"
	)
)
var _nintendo_motion_textures: JoypadMotionTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_motion/nintendo",
		"res://addons/input_prompts/joypad_motion_prompt/nintendo.tres"
	)
)
var _sony_motion_textures: JoypadMotionTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_motion/sony",
		"res://addons/input_prompts/joypad_motion_prompt/sony.tres"
	)
)
var _xbox_motion_textures: JoypadMotionTextures = load(
	ProjectSettings.get_setting(
		"addons/input_prompts/icons/joypad_motion/xbox",
		"res://addons/input_prompts/joypad_motion_prompt/xbox.tres"
	)
)


## Force all [InputPrompt] nodes to refresh their icons and events.
## Must be called if the [InputMap] is changed.
func refresh() -> void:
	var prompts := get_tree().get_nodes_in_group("_input_prompts")
	for prompt in prompts:
		prompt.call_deferred("refresh")


## Return the [KeyboardTextures] used by [KeyPrompt] nodes.
func get_keyboard_textures() -> KeyboardTextures:
	return _keyboard_textures


## Return the [MouseButtonTextures] used by [MouseButtonPrompt] nodes.
func get_mouse_textures() -> MouseButtonTextures:
	return _mouse_button_textures


## Return the [JoypadButtonTextures] used by [JoypadButtonPrompt] nodes.
func get_joypad_button_textures(icons: int) -> JoypadButtonTextures:
	match icons:
		InputPrompt.Icons.AUTOMATIC:
			return get_joypad_button_textures(joy_icons)
		InputPrompt.Icons.XBOX:
			return _xbox_button_textures
		InputPrompt.Icons.SONY:
			return _sony_button_textures
		InputPrompt.Icons.NINTENDO:
			return _nintendo_button_textures
		InputPrompt.Icons.KEYBOARD:
			push_error("No JoypadButtonTextures for InputPrompt.Icons.KEYBOARD.")
	return null


## Return the [JoypadMotionTextures] used by [JoypadMotionPrompt] nodes.
func get_joypad_motion_textures(icons: int) -> JoypadMotionTextures:
	match icons:
		InputPrompt.Icons.AUTOMATIC:
			return get_joypad_motion_textures(joy_icons)
		InputPrompt.Icons.XBOX:
			return _xbox_motion_textures
		InputPrompt.Icons.SONY:
			return _sony_motion_textures
		InputPrompt.Icons.NINTENDO:
			return _nintendo_motion_textures
		InputPrompt.Icons.KEYBOARD:
			push_error("No JoypadMotionTextures for InputPrompt.Icons.KEYBOARD.")
	return null


# Monitor InputEvents and emit icons_changed if:
# 1) The user has not expressed an icon preference
# 2) The type of InputEvent is different to last time
func _input(event: InputEvent):
	if not (preferred_icons == null or preferred_icons == InputPrompt.Icons.AUTOMATIC):
		return
	if event is InputEventKey or event is InputEventMouse:
		if icons != InputPrompt.Icons.KEYBOARD:
			icons = InputPrompt.Icons.KEYBOARD
			emit_signal("icons_changed")
	if event is InputEventJoypadButton or event is InputEventJoypadMotion:
		# Do not detect Joypad unless value exceeds deadzone
		if event is InputEventJoypadMotion and absf(event.axis_value) < joypad_detection_deadzone:
			return

		var device = event.device
		var joy_name = Input.get_joy_name(device)
		if joy_name.find("Xbox") != -1:
			joy_icons = InputPrompt.Icons.XBOX
		elif joy_name.find("DualShock") != -1 or joy_name.find("PS") != -1:
			joy_icons = InputPrompt.Icons.SONY
		elif joy_name.find("Nintendo") != -1:
			joy_icons = InputPrompt.Icons.NINTENDO
		else:
			joy_icons = InputPrompt.Icons.XBOX
		if icons != joy_icons:
			icons = joy_icons
			emit_signal("icons_changed")