#ifndef PLAYER_CHARACTER_HPP
#define PLAYER_CHARACTER_HPP

#include "character_data.hpp"
#include "health.hpp"
#include "projectile_pool.hpp"
#include "state.hpp"
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/curve.hpp>

namespace godot {
class NavigationAgent3D;
class TunnelsPlayer;
class AnimationPlayer;

namespace goap {
    class Planner;
};

class CharacterActor : public CharacterBody3D,
                       public IHealthEntity {
    GDCLASS(CharacterActor, CharacterBody3D);
    static void _bind_methods();
public:
    virtual void _enter_tree() override;
    virtual void _process(double delta_time) override;
    virtual void _physics_process(double delta_time) override;
    // manually set target_velocity
    void move(Vector3 world_vector);
    // manually aim at a target position
    // calls aim_direction with the flattened direction to 'at'
    void aim(Vector3 at);
    // manually set the forward vector of target_rotation
    void aim_direction(Vector3 direction);
    // set a movement target to navigate towards
    void move_to(Vector3 to, float target_distance = 0.5f);
    // fire weapon at a target position
    // calls aim(at) and set_firing(true)
    void shoot_at(Vector3 at);
    // refresh the current action, ending the previous one
    void force_update_action();
    // getter-setters
    void set_firing(bool firing);
    void set_manual_mode(bool value);

    void set_rotation_speed_curve(Ref<Curve> curve);
    Ref<Curve> get_rotation_speed_curve() const;

    virtual Health *get_health() override;
    virtual Health const *get_health() const override;

    void set_character_data(Ref<CharacterData> data);
    void set_weapon_muzzle(Node3D *node);
    void set_velocity_target(Vector3 value);
    Vector3 get_velocity_target() const;
    bool get_is_near_player() const;
    bool get_is_near_target() const;
    goap::Planner *get_planner() const;
    void set_target(Node *target);
    Node *get_target() const;
    void set_state(goap::State state);
protected:
    void process_behaviour(double delta_time);
    void process_navigation(double delta_time);
    void process_rotation(double delta_time);
    void try_fire_weapon();
private:
    // desired velocity, accelerated towards each frame
    Vector3 velocity_target{0.f,0.f,0.f};
    // target rotation, slerped towards each frame
    Basis target_rotation{};
    // ignore any ai planning or navigation
    bool mode_manual{false};
    // fire weapon at whatever we're aiming at
    bool firing{false};
    // the next timestamp at which a weapon can be fired
    float fire_timer{0.f};
    // the origin point for projectiles
    Node3D *weapon_muzzle{nullptr};
    // something that the AI wants to target
    Node *target{nullptr};
    // the current state of the actor
    goap::State current_state{goap::State::new_invalid()};
    AnimationPlayer *anim_player{nullptr};

    Health *health{nullptr};
    ProjectilePool *primary_weapon_pool{nullptr};
    NavigationAgent3D *nav_agent{nullptr};
    goap::Planner *planner{nullptr};

    Ref<Curve> rotation_speed_curve{};
    // character data assigned when spawned
    Ref<CharacterData> data;
    float fire_interval{0.f}; // derived from 1 / the current weapon's rps

    static float const ACCELERATION;
    static float const WALK_SPEED;
    static float const SPRINT_SPEED;
    static float const ROTATION_SPEED;
};
}

#endif // !PLAYER_CHARACTER_HPP
