#include "state_machine.h"
#include "state.h"

void StateMachine::_bind_methods() {}

void StateMachine::switch_to_state(State *state) {
	if (this->current_state != nullptr) {
		this->current_state_is_active = false;
		this->current_state->exit_state();
	}
	this->current_state = state;
	if (this->current_state != nullptr) {
		this->current_state_is_active = true;
		this->current_state->enter_state();
	} else {
		print_error("StateMachine::switch_to_state: New state is nullptr, StateMachine will now stop working");
	}
}

void StateMachine::process(double delta) {
	if (this->current_state) {
		this->current_state->process(delta);
		String new_state{ this->current_state->get_next_state() };
		if (new_state != this->current_state->get_class()) {
			this->switch_to_state(this->states.has(new_state) ? this->states[new_state] : nullptr);
		}
	}
}

void StateMachine::_notification(int what) {
	if (Engine::get_singleton()->is_editor_hint()) {
		return;
	}
	switch (what) {
		default:
			return;
		case NOTIFICATION_READY:
			set_process(true);
			return;
		case NOTIFICATION_PROCESS:
			process(get_process_delta_time());
			return;
		case NOTIFICATION_DISABLED:
		case NOTIFICATION_EXIT_TREE:
			if (this->current_state_is_active && this->current_state != nullptr) {
				this->current_state_is_active = false;
				this->current_state->exit_state();
			}
			return;
		case NOTIFICATION_ENABLED:
		case NOTIFICATION_ENTER_TREE:
			if (!this->current_state_is_active && this->current_state != nullptr) {
				this->current_state_is_active = true;
				this->current_state->enter_state();
			}
			return;
	}
}

StateMachine::~StateMachine() {
	for (KeyValue<StringName, State *> kvp : this->states) {
		if (kvp.value != nullptr) {
			memdelete(kvp.value);
		}
	}
}

void StateMachine::add_state(State *state) {
	state->set_state_machine(this);
	state->set_target(this->get_parent());
	this->states.insert(state->get_class(), state);
	if (this->current_state == nullptr) {
		this->switch_to_state(state);
	}
}
