#ifndef CAR_PHYSICS_HPP
#define CAR_PHYSICS_HPP

#include "godot_cpp/classes/collision_shape3d.hpp"
#include "godot_cpp/templates/hash_set.hpp"
#include "godot_cpp/variant/rid.hpp"
#include <godot_cpp/classes/curve.hpp>
#include <godot_cpp/classes/physics_direct_body_state3d.hpp>
#include <godot_cpp/classes/rigid_body3d.hpp>

namespace godot {
class CarPhysics : public RigidBody3D {
    GDCLASS(CarPhysics, RigidBody3D);
    static void _bind_methods();
public:
    virtual void _enter_tree() override;

    virtual void _physics_process(double delta_time) override;
    void process_oversteer(double delta_time);
    void process_understeer(double delta_time);
    virtual void _integrate_forces(PhysicsDirectBodyState3D *state) override;
protected:
    // Integration functions, state can be gotten via the _integrate_forces method.
    // Only intended to be called from the _integrate_forces methdo
    void integrate_steering(PhysicsDirectBodyState3D *state);
    void integrate_engine_acceleration(PhysicsDirectBodyState3D *state);
    void integrate_oversteer(PhysicsDirectBodyState3D *state);
    void on_body_shape_entered(RID body_rid, Node *node, int body_shape_index, int local_shape_index);
    void on_body_shape_exited(RID body_rid, Node *node, int body_shape_index, int local_shape_index);
    // evaluate the oversteer curve with a speed, taking *_x_scale into account
    float evaluate_oversteer_curve(float speed) const;
    // evaluate the understeer curve with a speed, taking *_x_scale into account
    float evaluate_understeer_curve(float speed) const;

    // returns the maximum speed modified by under/oversteer
    float get_true_target_speed() const;
    bool is_grounded() const;
    float get_current_acceleration() const;
public:
    // convert the local_velocity to world coordinates
    Vector3 local_to_world_velocity() const;
    Vector3 world_to_local_velocity() const;
    void set_target_speed(float target);
    float get_target_speed() const;
    void set_current_steering(float steering);
    float get_current_steering() const;
    void set_brake(bool value);
    bool get_brake() const;
    void set_oversteer_curve(Ref<Curve> curve);
    Ref<Curve> get_oversteer_curve() const;
    void set_oversteer_curve_x_scale(float scale);
    float get_oversteer_curve_x_scale() const;
    void set_understeer_curve(Ref<Curve> curve);
    Ref<Curve> get_understeer_curve() const;
    void set_understeer_curve_x_scale(float scale);
    float get_understeer_curve_x_scale() const;
    void set_acceleration(float value);
    float get_acceleration() const;
    void set_engine_brake_force(float value);
    float get_engine_brake_force() const;
    void set_handbrake_force(float value);
    float get_handbrake_force() const;
    void set_handbrake_oversteer(float value);
    float get_handbrake_oversteer() const;
    void set_traction_recovery_speed(float value);
    float get_traction_recovery_speed() const;
    void set_max_slide_speed(float value);
    float get_max_slide_speed() const;
    void set_slide_speed_acceleration(float value);
    float get_slide_speed_acceleration() const;
    void set_oversteer_speed_penalty(float value);
    float get_oversteer_speed_penalty() const;
    void set_oversteer_brake_penalty(float value);
    float get_oversteer_brake_penalty() const;
    void set_oversteer_steering_speed(float value);
    float get_oversteer_steering_speed() const;
    void set_slide_resistance(float value);
    float get_slide_resistance() const;
    void set_steering_inward_speed(float value);
    float get_steering_inward_speed() const;
private:
    Vector3 last_velocity{0.f, 0.f, 0.f};
    Vector3 local_velocity{0.f, 0.f, 0.f};
    float target_speed{0.f};
    float current_steering{0.f};
    bool brake{false};

    Ref<Curve> oversteer_curve{};
    float oversteer_curve_x_scale{50.f};
    Ref<Curve> understeer_curve{};
    float understeer_curve_x_scale{50.f};
    float acceleration{20.f};
    float engine_brake_force{30.f};
    float handbrake_force{40.f};
    float handbrake_oversteer{1.f};
    float traction_recovery_speed{1.f};
    float max_slide_speed{20.f};
    float slide_speed_acceleration{0.05f};
    float oversteer_speed_penalty{5.f};
    float oversteer_brake_penalty{0.25f};
    float oversteer_steering_speed{0.7f};
    float understeer_speed_penalty{3.f};
    float slide_resistance{20.f};
    float steering_inward_speed{0.1f};

    HashSet<Node*> grounded_objects{};

    CollisionShape3D *front_wheels{nullptr};
    CollisionShape3D *back_wheels{nullptr};

    float current_understeer{0.f};
    float current_oversteer{0.f};
};
}

#endif // !CAR_PHYSICS_HPP
