#ifndef PLAYER_STATES_H
#define PLAYER_STATES_H

#include "core/object/object.h"
#include "core/templates/hash_map.h"
#include "scene/main/node.h"
class PlayerBody;

class PlayerState : public Object {
	GDCLASS(PlayerState, Object);
	friend class PlayerStateMachine;
public:
	typedef StringName StateID;
public:
	virtual StateID get_next_state() const {
		return this->get_class();
	}
	virtual void state_entered() {}
	virtual void process(double delta) {}
	virtual void physics_process(double delta) {}
	virtual void state_exited() {}

	PlayerBody *get_body() const;
private:
	PlayerBody *body{nullptr};
};

class StandingState : public PlayerState {
	GDCLASS(StandingState, PlayerState);
public:
	virtual StateID get_next_state() const override;
	virtual void state_entered() override;
	virtual void process(double delta) override;
	virtual void physics_process(double delta) override;
	virtual void state_exited() override;
private:
	double game_over_timer{0.0};
	StringName game_over_param{"game_over_percentage"};
};

class RunningState : public PlayerState {
	GDCLASS(RunningState, PlayerState);
public:
	virtual StateID get_next_state() const override;
	virtual void state_entered() override;
	void process_lean(double delta);
	virtual void physics_process(double delta) override;
	virtual void state_exited() override;
private:
	float lean_modifier{0.f};
};

class SplitStepState : public PlayerState {
	GDCLASS(SplitStepState, PlayerState);
public:
	virtual StateID get_next_state() const override;
	virtual void state_entered() override;
	virtual void process(double delta) override;
	virtual void state_exited() override;
private:
	Vector3 last_velocity{0.f, 0.f, 0.f};
	double timer{0.0};
	bool jump{false};
};

class FallingState : public PlayerState {
	GDCLASS(FallingState, PlayerState);
public:
	virtual StateID get_next_state() const override;
	virtual void state_entered() override;
	virtual void process(double delta) override;
	virtual void state_exited() override;
private:
	bool can_game_over_falling{false};
	double game_over_timer{0.0};
	StringName game_over_param{"game_over_percentage"};
};

class JumpingState : public PlayerState {
	GDCLASS(JumpingState, PlayerState);
public:
	virtual StateID get_next_state() const override;
	virtual void state_entered() override;
	virtual void process(double delta) override;
};

class PlayerStateMachine : public Node {
	GDCLASS(PlayerStateMachine, Node);
	static void _bind_methods();
	void _notification(int what);
	void ready();
	void try_transition();
	template <class TState>
	void add_state();

	PlayerBody *body{nullptr};
	PlayerState *current_state{nullptr};
	HashMap<PlayerState::StateID, PlayerState*> states;
};

template <class TState>
void PlayerStateMachine::add_state() {
	PlayerState *state{new TState()};
	state->body = this->body;
	this->states.insert(TState::get_class_static(), state);
	if(this->current_state == nullptr) {
		this->current_state = state;
		state->state_entered();
	}
}

#endif // !PLAYER_STATES_H
