extends Node
class_name InputSettingsModel

## ===============================
## Dependencies
## ===============================
const InputSchema  = preload("res://__settings/input/InputSchema.gd")
const SettingsStorage = preload("res://__settings/SettingStorage.gd")

## ===============================
## Signals
## ===============================
signal binding_changed(action: String)
signal bindings_applied
signal bindings_reverted

## ===============================
## Internal State
## ===============================
var _bindings_committed : Dictionary = {}
var _bindings_working   : Dictionary = {}
var _storage := SettingsStorage.new()

const SECTION := "input"

## ===============================
## Lifecycle
## ===============================
func _ready() -> void:
	_load()

## ===============================
## Public API (Read)
## ===============================
func get_bindings(action: String) -> Array:
	return _bindings_working.get(action, []).duplicate(true)

func get_all_bindings() -> Dictionary:
	return _bindings_working.duplicate(true)

## ===============================
## Public API (Write)
## ===============================
func set_bindings(action: String, bindings: Array) -> void:
	_bindings_working[action] = bindings
	emit_signal("binding_changed", action)

func add_binding(action: String, binding: Dictionary) -> void:
	if not _bindings_working.has(action):
		_bindings_working[action] = []
	_bindings_working[action].append(binding)
	emit_signal("binding_changed", action)

func clear_bindings(action: String) -> void:
	_bindings_working[action] = []
	emit_signal("binding_changed", action)

## ===============================
## Transaction
## ===============================
func apply() -> void:
	_bindings_committed = _bindings_working.duplicate(true)
	_save_to_storage()
	_apply_to_input_map()
	emit_signal("bindings_applied")

func revert() -> void:
	_bindings_working = _bindings_committed.duplicate(true)
	emit_signal("bindings_reverted")

func has_unsaved_changes() -> bool:
	return _bindings_working != _bindings_committed

## ===============================
## Internal
## ===============================
func _load() -> void:
	_storage.load()

	# 1. defaults
	_bindings_committed = InputSchema.defaults().duplicate(true)

	# 2. override from storage
	for action in _bindings_committed.keys():
		var saved = _storage.get_value(SECTION, action, null)
		if saved != null:
			_bindings_committed[action] = saved

	_bindings_working = _bindings_committed.duplicate(true)

	# 3. apply to engine at startup
	_apply_to_input_map()

func _save_to_storage() -> void:
	for action in _bindings_committed.keys():
		_storage.set_value(SECTION, action, _bindings_committed[action])
	_storage.save()

func _apply_to_input_map() -> void:
	for action in _bindings_committed.keys():
		if not InputMap.has_action(action):
			InputMap.add_action(action)

		InputMap.action_erase_events(action)

		for binding in _bindings_committed[action]:
			var event = _binding_to_event(binding)
			if event:
				InputMap.action_add_event(action, event)

func _binding_to_event(binding: Dictionary) -> InputEvent:
	match binding.get("type"):
		"key":
			var e := InputEventKey.new()
			e.keycode = binding.get("keycode", 0)
			return e
		"mouse":
			var e := InputEventMouseButton.new()
			e.button_index = binding.get("button", 0)
			return e
	return null
