#ifndef CAR_PLAYER_HPP
#define CAR_PLAYER_HPP

#include "car_physics.hpp"
#include "godot_cpp/classes/camera3d.hpp"
#include "godot_cpp/classes/packed_scene.hpp"
#include "utils/player.hpp"
#include <godot_cpp/classes/input_event.hpp>
#include <godot_cpp/classes/rigid_body3d.hpp>
#include <godot_cpp/classes/collision_shape3d.hpp>

namespace godot {
/*!
 * Sandboxed subclass of CarPhysics.
 * Uses PlayerInput callbacks to drive the car. And updates the camera's position to show the action better. Inherits from IPlayer to be spawnable by the GameRoot3D and to be assigned a PlayerInput instance.
 */
class CarPlayer : public CarPhysics,
                  public IPlayer {
    GDCLASS(CarPlayer, CarPhysics);
    //! Register editor properties and damage function.
    static void _bind_methods();
public:
    //! Get the required child nodes.
    virtual void _ready() override;
    //! Update powerup and grace period timers. As well as the camera position.
    virtual void _process(double delta_time) override;
    /*!
     * Initialize player input callbacks.
     * \param input Reference to a PlayerInput node managed by the GameRoot3D from the godot-cpp-utils library.
     */
    virtual void setup_player_input(PlayerInput *input) override;
    /*!
     * Place player at initial position
     * Called from the GameRoot3D of the godot-cpp-utils library.
     * \param transform The location and orientation of the spawn point.
     */
    virtual void spawn_at_position(Transform3D const &transform) override;
    //! Convert to node
    virtual Node *to_node() override;

    //! Input callback for the steering axis.
    void on_steer(Ref<InputEvent> event, float value);
    //! Input callback for the brake key.
    void on_brake(Ref<InputEvent> event, float value);
    //! Input callback for the accelerate key.
    void on_accelerate(Ref<InputEvent> event, float value);

    //! Take 1 damage.
    void damage();

    //! Destroy all children of roof_slot.
    void destroy_all_powerups();
    //! Destroy all children and activate a new powerup.
    void activate_powerup(Ref<PackedScene> scene);
    //! Destroy all powerups and spawn a turret.
    void activate_turret();
    //! Destroy all powerups and spawn a beacon.
    void activate_beacon();
    //! Destroy all powerups and spawn a shield.
    void activate_shield();

    //! The object representing the turret pickup.
    void set_turret_scene(Ref<PackedScene> scene);
    //! The object representing the turret pickup.
    Ref<PackedScene> get_turret_scene() const;
    //! The object representing the beacon pickup.
    void set_beacon_scene(Ref<PackedScene> scene);
    //! The object representing the beacon pickup.
    Ref<PackedScene> get_beacon_scene() const;
    //! The object representing the shield pickup.
    void set_shield_scene(Ref<PackedScene> scene);
    //! The object representing the shield pickup.
    Ref<PackedScene> get_shield_scene() const;
private:
    //! Amount of hits the player's car can still take.
    int health{5};
    //! The time at which the current powerup will be deactivated.
    double end_of_powerup{0.0};
    //! If false, the car will be invincible.
    bool takes_damage{true};
    //! The time at which the current grace period will end.
    float grace_timer_end{0.f};

    //! The parent of any pickup models.
    Node3D *roof_slot{nullptr};
    //! The camera
    Camera3D *camera{nullptr};

    //! The roof turret powerup scene.
    Ref<PackedScene> turret;
    //! The hacking beacon powerup scene.
    Ref<PackedScene> beacon;
    //! The ramming shield powerup scene.
    Ref<PackedScene> shield;

    //! Target speed when accelerate is pressed.
    float const max_speed{40.f};
    //! Amount of steering to apply based on input.
    float const steering_factor{0.7f};
    //! Duration of a powerup after it is picked up.
    float const powerup_duration{10.f};
    //! Distance from the pivot the camera should be at.
    float const camera_distance{4.f};
    //! Height the camera should be at relative to the pivot.
    float const camera_height{2.f};
    //! The FOV of the camera when the car is traveling at max_speed.
    float const camera_fullspeed_fov{90.f};
    //! FOV of the camera when the car is not moving.
    float const camera_stopped_fov{70.f};
    //! Speed in degrees-per-second that the camera FOV can change at.
    float const fov_lerp_delta{40.f};
    //! Amount of time the player should be invincible after being hit.
    float const grace_time{1.f};
    //! Interval at which the car's model should flash while invincible after being hit.
    float const grace_time_flash{0.1f};
};
};

#endif // !CAR_PLAYER_HPP
