#include "game/gameplay/bullet_manager.hpp"

#include <algorithm>  // Para remove_if
#include <utility>

#include "game/entities/bullet.hpp"  // Para Bullet
#include "utils/param.hpp"           // Para Param, ParamGame, param
#include "utils/utils.hpp"           // Para Circle, Zone

// Constructor
BulletManager::BulletManager()
    : play_area_(param.game.play_area.rect) {
}

// Actualiza el estado de todas las balas
void BulletManager::update(float delta_time) {
    for (auto& bullet : bullets_) {
        if (bullet->isEnabled()) {
            processBulletUpdate(bullet, delta_time);
        }
    }
}

// Renderiza todas las balas activas
void BulletManager::render() {
    for (auto& bullet : bullets_) {
        if (bullet->isEnabled()) {
            bullet->render();
        }
    }
}

// Crea una nueva bala
void BulletManager::createBullet(int x, int y, Bullet::Type type, Bullet::Color color, int owner) {
    bullets_.emplace_back(std::make_shared<Bullet>(x, y, type, color, owner));
}

// Libera balas que ya no están habilitadas
void BulletManager::freeBullets() {
    std::erase_if(bullets_, [](const std::shared_ptr<Bullet>& bullet) -> bool {
        return !bullet->isEnabled();
    });
}

// Elimina todas las balas
void BulletManager::clearAllBullets() {
    bullets_.clear();
}

// Verifica colisiones de todas las balas
void BulletManager::checkCollisions() {
    for (auto& bullet : bullets_) {
        if (!bullet->isEnabled()) {
            continue;
        }

        // Verifica colisión con Tabe
        if (tabe_collision_callback_ && tabe_collision_callback_(bullet)) {
            break;  // Sale del bucle si hubo colisión
        }

        // Verifica colisión con globos
        if (balloon_collision_callback_ && balloon_collision_callback_(bullet)) {
            break;  // Sale del bucle si hubo colisión
        }
    }
}

// Establece el callback para colisión con Tabe
void BulletManager::setTabeCollisionCallback(CollisionCallback callback) {
    tabe_collision_callback_ = std::move(callback);
}

// Establece el callback para colisión con globos
void BulletManager::setBalloonCollisionCallback(CollisionCallback callback) {
    balloon_collision_callback_ = std::move(callback);
}

// Establece el callback para balas fuera de límites
void BulletManager::setOutOfBoundsCallback(OutOfBoundsCallback callback) {
    out_of_bounds_callback_ = std::move(callback);
}

// --- Métodos privados ---

// Procesa la actualización individual de una bala
void BulletManager::processBulletUpdate(const std::shared_ptr<Bullet>& bullet, float delta_time) {
    auto status = bullet->update(delta_time);

    // Si la bala salió de los límites, llama al callback
    if (status == Bullet::MoveStatus::OUT && out_of_bounds_callback_) {
        out_of_bounds_callback_(bullet);
    }
}

// Verifica si la bala está fuera de los límites del área de juego
auto BulletManager::isBulletOutOfBounds(const std::shared_ptr<Bullet>& bullet) const -> bool {
    auto collider = bullet->getCollider();

    return (collider.x < play_area_.x ||
        collider.x > play_area_.x + play_area_.w ||
        collider.y < play_area_.y ||
        collider.y > play_area_.y + play_area_.h);
}