#include "car_player.hpp"
#include "godot_cpp/classes/global_constants.hpp"
#include "godot_cpp/classes/time.hpp"
#include "rally_rush_game_mode.hpp"
#include "utils/game_root.hpp"
#include "utils/godot_macros.h"
#include "utils/player_input.hpp"
#include <godot_cpp/variant/transform3d.hpp>
#include <godot_cpp/classes/physics_direct_body_state3d.hpp>

namespace godot {
void CarPlayer::_bind_methods() {
#define CLASSNAME CarPlayer
    GDFUNCTION(damage);
    GDPROPERTY_HINTED(turret_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
    GDPROPERTY_HINTED(beacon_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
    GDPROPERTY_HINTED(shield_scene, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene");
}

void CarPlayer::_ready() { GDGAMEONLY();
    this->roof_slot = this->get_node<Node3D>("RoofSlot");
}

void CarPlayer::_process(double delta_time) { GDGAMEONLY();
    if(this->roof_slot->get_child_count() > 0 && Time::get_singleton()->get_ticks_msec() * 0.001 > this->end_of_powerup) {
        this->destroy_all_powerups();
    }
}

void CarPlayer::setup_player_input(PlayerInput *input) {
    input->listen_to(PlayerInput::Listener("steer_left", "steer_right", callable_mp(this, &CarPlayer::on_steer)));
    input->listen_to(PlayerInput::Listener("accelerate", callable_mp(this, &CarPlayer::on_accelerate)));
    input->listen_to(PlayerInput::Listener("brake", callable_mp(this, &CarPlayer::on_brake)));
}

void CarPlayer::spawn_at_position(Transform3D const &transform) {
    this->set_transform(transform);
    this->set_scale({1,1,1});
}

Node *CarPlayer::to_node() { return this; }

void CarPlayer::on_steer(Ref<InputEvent> event, float value) {
    this->set_current_steering(value * this->steering_factor);
}

void CarPlayer::on_brake(Ref<InputEvent> input, float value) {
    this->set_brake(value != 0);
}

void CarPlayer::on_accelerate(Ref<InputEvent> input, float value) {
    this->set_target_speed(max_speed * value);
}

void CarPlayer::damage() {
    --this->health;
    if(this->health <= 0) {
        RallyRushGameMode *game_mode = Ref<RallyRushGameMode>(GameRoot3D::get_singleton()->get_game_mode()).ptr();
        game_mode->notify_player_death();
    }
}

void CarPlayer::destroy_all_powerups() {
    for(int i = 0; i < this->roof_slot->get_child_count(); ++i) {
        if(this->roof_slot->get_child(0)->is_class("Turret"))
            Ref<RallyRushGameMode>(GameRoot3D::get_singleton()->get_game_mode())->notify_end_turret_pickup();
        this->roof_slot->get_child(0)->queue_free();
    }
}

void CarPlayer::activate_powerup(Ref<PackedScene> scene) {
    this->destroy_all_powerups();
    Node3D *powerup = Object::cast_to<Node3D>(scene->instantiate());
    this->roof_slot->add_child(powerup);
    powerup->set_global_transform(this->roof_slot->get_global_transform());
    this->end_of_powerup = Time::get_singleton()->get_ticks_msec() * 0.001 + this->powerup_duration;
}

void CarPlayer::activate_turret() {
    Ref<RallyRushGameMode>(GameRoot3D::get_singleton()->get_game_mode())->notify_turret_pickup();
    this->activate_powerup(this->turret);
}
void CarPlayer::activate_beacon() { this->activate_powerup(this->beacon); }
void CarPlayer::activate_shield() { this->activate_powerup(this->turret); }

void CarPlayer::set_turret_scene(Ref<PackedScene> scene) {
    this->turret = scene;
}

Ref<PackedScene> CarPlayer::get_turret_scene() const {
    return this->turret;
}

void CarPlayer::set_beacon_scene(Ref<PackedScene> scene) {
    this->beacon = scene;
}

Ref<PackedScene> CarPlayer::get_beacon_scene() const {
    return this->beacon;
}

void CarPlayer::set_shield_scene(Ref<PackedScene> scene) {
    this->shield = scene;
}

Ref<PackedScene> CarPlayer::get_shield_scene() const {
    return this->shield;
}
}
