// bullet.cpp - Implementación de projectils de la ship
// © 2026 JailDesigner

#include "game/entities/bullet.hpp"

#include <cmath>
#include <cstdint>
#include <iostream>

#include "core/audio/audio.hpp"
#include "core/defaults.hpp"
#include "core/entities/entity.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "core/types.hpp"
#include "game/constants.hpp"
#include "game/entities/bullet_config.hpp"
#include "game/entities/bullet_registry.hpp"

Bullet::Bullet(Rendering::Renderer* renderer)
    : Entity(renderer),
      config_(&BulletRegistry::get()) {
    brightness_ = Defaults::Brightness::BALA;

    // Cinemàtiques pures: no col·lisionen al PhysicsWorld (body_.radius = 0).
    // El gameplay (GameScene) gestiona els hits via checkCollisionSwept i la
    // sortida del PLAYAREA.
    body_.setMass(config_->physics.mass);
    body_.radius = 0.0F;
    body_.restitution = config_->physics.restitution;
    body_.linear_damping = config_->physics.linear_damping;
    body_.angular_damping = config_->physics.angular_damping;

    shape_ = Graphics::ShapeLoader::load(config_->shape.path);
    if (!shape_ || !shape_->isValid()) {
        std::cerr << "[Bullet] Error: no s'ha pogut carregar " << config_->shape.path << '\n';
    }

    // Radi de col·lisió derivat del cercle circumscrit de la shape.
    const float BOUNDING = (shape_ != nullptr) ? shape_->getBoundingRadius() : 0.0F;
    collision_radius_ = BOUNDING * config_->shape.scale * config_->shape.collision_factor;
}

void Bullet::init() {
    is_active_ = false;
    center_ = {.x = 0.0F, .y = 0.0F};
    prev_position_ = {.x = 0.0F, .y = 0.0F};
    angle_ = 0.0F;

    body_.position = Vec2{};
    body_.velocity = Vec2{};
    body_.angle = 0.0F;
    body_.angular_velocity = 0.0F;
    body_.clearAccumulators();
}

void Bullet::fire(const Vec2& position, float angle, uint8_t owner_id, float bullet_speed, const BulletConfig* cfg) {
    is_active_ = true;
    owner_id_ = owner_id;

    // Si no es passa cfg, restaurem al config per defecte (BulletRegistry::get):
    // els slots són reutilitzables i una bala que abans ha estat disparada amb
    // una variant (p.ex. bullet_long d'enemic) ha de tornar al bullet.shp del
    // player quan aquest la reutilitza.
    const BulletConfig* effective = (cfg != nullptr) ? cfg : &BulletRegistry::get();
    if (effective != config_) {
        config_ = effective;
        shape_ = Graphics::ShapeLoader::load(config_->shape.path);
        if (!shape_ || !shape_->isValid()) {
            std::cerr << "[Bullet] Error: no s'ha pogut carregar " << config_->shape.path << '\n';
        }
        const float BOUNDING = (shape_ != nullptr) ? shape_->getBoundingRadius() : 0.0F;
        collision_radius_ = BOUNDING * config_->shape.scale * config_->shape.collision_factor;
        body_.setMass(config_->physics.mass);
        body_.restitution = config_->physics.restitution;
        body_.linear_damping = config_->physics.linear_damping;
        body_.angular_damping = config_->physics.angular_damping;
    }

    center_ = position;
    prev_position_ = position;  // spawn: swept degenera a punt-cercle
    angle_ = angle;

    // Sincronizar el body físic: posició + velocitat cartesiana.
    // angle - PI/2 perquè angle=0 apunta cap amunt (eje Y negatiu SDL).
    body_.position = position;
    body_.angle = angle;
    const float DIR_X = std::cos(angle - (Constants::PI / 2.0F));
    const float DIR_Y = std::sin(angle - (Constants::PI / 2.0F));
    body_.velocity = Vec2{.x = DIR_X * bullet_speed, .y = DIR_Y * bullet_speed};
    body_.angular_velocity = 0.0F;
    body_.clearAccumulators();

    Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
}

void Bullet::update(float /*delta_time*/) {
    // No-op: la desactivació per fora-de-zone viu a
    // Systems::Collision::desactivateOutOfBoundsBullets() perquè així té accés
    // al DebrisManager i pot generar el "trencament" visual de la bala alhora.
    // El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_.
}

void Bullet::postUpdate(float /*delta_time*/) {
    prev_position_ = center_;
    center_ = body_.position;
}

void Bullet::desactivar() {
    is_active_ = false;
    body_.velocity = Vec2{};
    body_.angular_velocity = 0.0F;
}

void Bullet::draw() const {
    if (is_active_ && shape_) {
        Rendering::renderShape(renderer_, shape_, center_, angle_, config_->shape.scale, 1.0F, brightness_, config_->colors.normal);
    }
}
