// bullet.cpp - Implementación de projectils de la ship
// © 1999 Visente i Sergi (versión Pascal)
// © 2025 Port a C++20 con SDL3

#include "game/entities/bullet.hpp"

#include <algorithm>
#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"

namespace {
// Velocidad escalar de las balas (px/s). Conserva el feel del Pascal original
// (7 px/frame × 20 FPS = 140 px/s).
constexpr float BULLET_SPEED = 140.0F;
}  // namespace

Bullet::Bullet(Rendering::Renderer* renderer)
    : Entity(renderer),
      esta_(false),
      owner_id_(0),
      grace_timer_(0.0F) {
    // Brightness específico para balas
    brightness_ = Defaults::Brightness::BALA;

    // Configuración del cuerpo físico.
    // Las balas son cinemáticas: no colisionan con otros bodies ni paredes.
    // El gameplay (GameScene) gestiona los hits con check_collision y la
    // salida del PLAYAREA. Por eso radius=0 en el world (no participa en
    // resolveBodyCollisions ni resolveBoundsCollisions).
    body_.setMass(0.5F);          // Ligera (no afecta a nadie, pero por consistencia)
    body_.radius = 0.0F;          // Sin colisión física (cinemática pura)
    body_.restitution = 0.0F;     // Irrelevante (no rebota)
    body_.linear_damping = 0.0F;  // Sin fricción (movimiento rectilíneo uniforme)
    body_.angular_damping = 0.0F;

    // Cargar shape compartida desde archivo
    shape_ = Graphics::ShapeLoader::load("bullet.shp");
    if (!shape_ || !shape_->isValid()) {
        std::cerr << "[Bullet] Error: no s'ha pogut load bullet.shp" << '\n';
    }
}

void Bullet::init() {
    // Inicialment inactiva
    esta_ = false;
    center_ = {.x = 0.0F, .y = 0.0F};
    angle_ = 0.0F;
    grace_timer_ = 0.0F;

    // Reset del cuerpo físico
    body_.position = Vec2{};
    body_.velocity = Vec2{};
    body_.angle = 0.0F;
    body_.angular_velocity = 0.0F;
    body_.clearAccumulators();
}

void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) {
    // Activar bullet
    esta_ = true;

    // Almacenar propietario (0=P1, 1=P2)
    owner_id_ = owner_id;

    // Activar grace period (prevents instant self-collision)
    grace_timer_ = Defaults::Game::BULLET_GRACE_PERIOD;

    // Posición y orientación iniciales = ship
    center_ = position;
    angle_ = angle;

    // Sincronizar el body físico: posición + velocidad cartesiana
    // angle - PI/2 porque angle=0 apunta hacia arriba (eje Y negativo 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();

    // Reproducir sonido de disparo láser
    Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
}

void Bullet::update(float delta_time) {
    if (!esta_) {
        return;
    }

    // Decrementar grace timer
    if (grace_timer_ > 0.0F) {
        grace_timer_ -= delta_time;
        grace_timer_ = std::max(grace_timer_, 0.0F);
    }

    // El movimiento real lo hace PhysicsWorld::update() (integración).
    // Aquí solo lógica de estado: detectar salida del PLAYAREA y desactivar.
    float min_x;
    float max_x;
    float min_y;
    float max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::BULLET_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    if (body_.position.x < min_x || body_.position.x > max_x ||
        body_.position.y < min_y || body_.position.y > max_y) {
        desactivar();
    }
}

void Bullet::postUpdate(float /*delta_time*/) {
    // Sincronizar mirror desde body_ tras la integración del world.
    center_ = body_.position;
    // angle_ no cambia (las balas no rotan visualmente).
}

void Bullet::desactivar() {
    esta_ = false;
    // Detener el cuerpo físico para que no acumule deriva mientras inactiva.
    body_.velocity = Vec2{};
    body_.angular_velocity = 0.0F;
}

void Bullet::draw() const {
    if (esta_ && shape_) {
        // Les bales roten segons l'angle de trayectòria (estático tras disparo)
        Rendering::render_shape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_,
                                /*rotation_3d=*/nullptr, Defaults::Palette::BULLET);
    }
}
