#include "item.h"

#include <algorithm>  // Para clamp
#include <cstdlib>    // Para rand

#include "animated_sprite.h"  // Para AnimatedSprite
#include "param.h"            // Para Param, ParamGame, param

class Texture;  // lines 6-6

Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation)
    : sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
      type_(type),
      play_area_(play_area) {
    switch (type) {
        case ItemType::COFFEE_MACHINE: {
            width_ = COFFEE_MACHINE_WIDTH;
            height_ = COFFEE_MACHINE_HEIGHT;
            pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w);
            pos_y_ = y;
            vel_x_ = ((rand() % 3) - 1) * 0.5F;
            vel_y_ = -0.1F;
            accel_y_ = 0.1F;
            collider_.r = 10;
            break;
        }
        default: {
            width_ = param.game.item_size;
            height_ = param.game.item_size;
            pos_x_ = x;
            pos_y_ = y;
            vel_x_ = -1.0F + ((rand() % 5) * 0.5F);
            vel_y_ = -4.0F;
            accel_y_ = 0.2F;
            collider_.r = width_ / 2;
            break;
        }
    }

    // Actualiza el sprite
    shiftSprite();
    shiftColliders();
}

void Item::alignTo(int x) {
    const float MIN_X = param.game.play_area.rect.x + 1;
    const float MAX_X = play_area_.w - width_ - 1;

    pos_x_ = x - (width_ / 2);

    // Ajusta para que no quede fuera de la zona de juego
    pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);

    // Actualiza el sprite
    shiftSprite();
    shiftColliders();
}

void Item::render() {
    if (enabled_) {
        if (time_to_live_ > 200) {
            sprite_->render();
        } else if (time_to_live_ % 20 > 10) {
            sprite_->render();
        }
    }
}

void Item::move() {
    floor_collision_ = false;

    // Calcula la nueva posición
    pos_x_ += vel_x_;
    pos_y_ += vel_y_;

    // Aplica las aceleraciones a la velocidad
    vel_x_ += accel_x_;
    vel_y_ += accel_y_;

    // Comprueba los laterales de la zona de juego
    const float MIN_X = param.game.play_area.rect.x;
    const float MAX_X = play_area_.w - width_;
    pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);

    // Si toca el borde lateral, invierte la velocidad horizontal
    if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
        vel_x_ = -vel_x_;
    }

    // Si colisiona por arriba, rebota (excepto la máquina de café)
    if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) {
        // Corrige
        pos_y_ = param.game.play_area.rect.y;

        // Invierte la velocidad
        vel_y_ = -vel_y_;
    }

    // Si colisiona con la parte inferior
    if (pos_y_ > play_area_.h - height_) {
        // Corrige la posición
        pos_y_ = play_area_.h - height_;

        switch (type_) {
            case ItemType::COFFEE_MACHINE:
                // La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido
                floor_collision_ = true;
                if (vel_y_ < 1.0F) {
                    // Si la velocidad vertical es baja, detiene el objeto
                    vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
                } else {
                    // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
                    vel_y_ *= -0.20F;
                    vel_x_ *= 0.75F;
                }
                break;
            default:
                // Si no es una máquina de café
                if (vel_y_ < 1.0F) {
                    // Si la velocidad vertical es baja, detiene el objeto
                    vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
                } else {
                    // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
                    vel_y_ *= -0.5F;
                    vel_x_ *= 0.75F;
                }
                break;
        }
    }

    // Actualiza la posición del sprite
    shiftSprite();
    shiftColliders();
}

void Item::disable() { enabled_ = false; }

void Item::update() {
    move();
    sprite_->update();
    updateTimeToLive();
}

void Item::updateTimeToLive() {
    if (time_to_live_ > 0) {
        time_to_live_--;
    } else {
        disable();
    }
}

void Item::shiftColliders() {
    collider_.x = static_cast<int>(pos_x_ + (width_ / 2));
    collider_.y = static_cast<int>(pos_y_ + (height_ / 2));
}

void Item::shiftSprite() {
    sprite_->setPosX(pos_x_);
    sprite_->setPosY(pos_y_);
}

// Calcula la zona de aparición de la máquina de café
auto Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) -> int {
    // Distancia mínima del jugador (ajusta según necesites)
    const int MIN_DISTANCE_FROM_PLAYER = area_width / 2;

    const int LEFT_BOUND = margin;
    const int RIGHT_BOUND = area_width - item_width - margin;

    // Calcular zona de exclusión alrededor del jugador
    int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER;
    int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER;

    // Verificar si hay espacio suficiente a la izquierda
    bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width);

    // Verificar si hay espacio suficiente a la derecha
    bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width);

    if (can_spawn_left && can_spawn_right) {
        // Ambos lados disponibles, elegir aleatoriamente
        if (rand() % 2 == 0) {
            // Lado izquierdo
            return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
        }  // Lado derecho
        return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
    }
    if (can_spawn_left) {
        // Solo lado izquierdo disponible
        return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND;
    }
    if (can_spawn_right) {
        // Solo lado derecho disponible
        return rand() % (RIGHT_BOUND - exclude_right) + exclude_right;
    }  // No hay espacio suficiente lejos del jugador
    // Por ahora, intentar spawn en el extremo más lejano posible
    int distance_to_left = abs(player_x - LEFT_BOUND);
    int distance_to_right = abs(RIGHT_BOUND - player_x);

    if (distance_to_left > distance_to_right) {
        return LEFT_BOUND;
    }
    return RIGHT_BOUND - item_width;
}
