#include "pellet_projectile.hpp"
#include "health.hpp"
#include "utils/godot_macros.h"
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
#include <godot_cpp/classes/physics_server3d.hpp>
#include <godot_cpp/classes/world3d.hpp>

namespace godot {
void PelletProjectile::_bind_methods() {
#define CLASSNAME PelletProjectile
}

void PelletProjectile::_enter_tree() {
    this->distance_traveled = 0;
}

void PelletProjectile::_physics_process(double delta_time) { GDGAMEONLY();
    float const speed = this->data->get_projectile_speed()->sample(distance_traveled / this->data->get_range());
    this->distance_traveled += speed;
    Vector3 const next_position{this->get_global_position() + this->get_global_transform().basis.get_column(2) * speed};
    if(this->check_hit(next_position) || this->distance_traveled > this->data->get_range())
        this->return_to_pool();
    else
        this->set_global_position(next_position);
}

bool PelletProjectile::check_hit(Vector3 next_position) {
    Ref<PhysicsRayQueryParameters3D> query{PhysicsRayQueryParameters3D::create(this->get_global_position(), next_position, 0b101)};
    Dictionary hit = this->get_world_3d()->get_direct_space_state()->intersect_ray(query);
    if(hit.is_empty())
        return false;
    Node *collider = Object::cast_to<Node>(hit.get("collider", nullptr));
    if(collider == nullptr)
        return false;
    IHealthEntity *health_entity = dynamic_cast<IHealthEntity*>(collider);
    if(health_entity == nullptr)
        return true;
    health_entity->get_health()->damage(this->data->get_damage());
    return true;
}
}
