## Virtual base class for all nodes that deal directly with states.
## Extend this class and override its methods to implement a state machine.
class_name StateMachine extends Node


## The initial state of the state machine. If not set, the first child node is used.
@export var initial_state: State

## The state machine's current loaded state
@onready var state: State = _get_initial_state()


func _ready() -> void:
	assert(state != null, "initial state is null")
	for state_node: State in find_children("*", "State"):
		# fixes duplicate connections (not sure why)
		if not state_node.finished.is_connected(_transition_to_next_state):
			state_node.finished.connect(_transition_to_next_state)
	await owner.ready
	state.enter("")


func _unhandled_input(event: InputEvent) -> void:
	state.handle_input(event)


func _process(delta: float) -> void:
	state.state_update(delta)


func _physics_process(delta: float) -> void:
	state.state_physics_update(delta)


## Called when initial state is not specified.
## Returns the first child node by default.
## Override if necessary, but don't call it directly.
func _get_initial_state() -> State:
	return initial_state if initial_state != null else get_child(0)


## Transitions the active state out after receiving a finished signal.
func _transition_to_next_state(target_state_path: String, data: Dictionary[StringName, Variant] = {}) -> void:
	print(owner.name+"+++ TRANSITION CALLED: ", target_state_path)
	print(owner.name+"+++ has node? ", has_node(target_state_path))
	print(owner.name+"+++ all children: ", get_children().map(func(c: Node) -> StringName: return c.name))
	
	assert(has_node(target_state_path), owner.name + ": Trying to transition to state " + target_state_path + " but it does not exist.")
	
	if not has_node(target_state_path):
		printerr(owner.name + ": Trying to transition to state " + target_state_path + " but it does not exist.")
		return
	
	var previous_state_path: StringName = state.name
	state.exit()
	state = get_node(target_state_path)
	state.enter(previous_state_path, data)
