#include "game/entities/item.h"

#include <cstdlib>  // for rand

#include "core/rendering/animatedsprite.h"  // for AnimatedSprite
#include "game/defaults.hpp"                // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
class Texture;

// Constructor
Item::Item(Id id, float x, float y, Texture *texture, std::vector<std::string> *animation, SDL_Renderer *renderer) {
    sprite_ = new AnimatedSprite(texture, renderer, "", animation);

    this->id_ = id;
    enabled_ = true;
    time_to_live_ = 600;
    accel_x_ = 0.0F;
    floor_collision_ = false;

    if (id == Item::Id::COFFEE_MACHINE) {
        width_ = 23;
        height_ = 29;
        pos_x_ = (((int)x + (PLAY_AREA_WIDTH / 2)) % (PLAY_AREA_WIDTH - width_ - 5)) + 2;
        pos_y_ = PLAY_AREA_TOP - height_;
        vel_x_ = 0.0F;
        vel_y_ = -0.1F;
        accel_y_ = 0.1F;
        collider_.r = 10;
    } else {
        width_ = 16;
        height_ = 16;
        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;
    }

    sprite_->setPosX(pos_x_);
    sprite_->setPosY(pos_y_);
    shiftColliders();
}

// Destructor
Item::~Item() {
    delete sprite_;
}

// Centra el objeto en la posición X
void Item::allignTo(int x) {
    pos_x_ = float(x - (width_ / 2));

    if (pos_x_ < PLAY_AREA_LEFT) {
        pos_x_ = PLAY_AREA_LEFT + 1;
    } else if ((pos_x_ + width_) > PLAY_AREA_RIGHT) {
        pos_x_ = float(PLAY_AREA_RIGHT - width_ - 1);
    }

    // Posición X,Y del sprite
    sprite_->setPosX(int(pos_x_));
    sprite_->setPosY(int(pos_y_));

    // Alinea el circulo de colisión con el objeto
    shiftColliders();
}

// Pinta el objeto en la pantalla
void Item::render() {
    // Mentre quede temps de sobra (>200) es renderitza sempre; quan està a
    // punt d'expirar, parpalleja alternant 10 frames visibles i 10 invisibles.
    if (enabled_ && (time_to_live_ > 200 || time_to_live_ % 20 > 10)) {
        sprite_->render();
    }
}

// Actualiza la posición y estados del objeto
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_;

    // Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido
    if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) {
        // Corregir posición
        pos_x_ -= vel_x_;

        // Invertir sentido
        vel_x_ = -vel_x_;
    }

    // Si se sale por arriba rebota (excepto la maquina de café)
    if ((pos_y_ < PLAY_AREA_TOP) && !(id_ == Item::Id::COFFEE_MACHINE)) {
        // Corrige
        pos_y_ -= vel_y_;

        // Invierte el sentido
        vel_y_ = -vel_y_;
    }

    // Si el objeto se sale por la parte inferior
    if (pos_y_ + height_ > PLAY_AREA_BOTTOM) {
        // Detiene el objeto
        vel_y_ = 0;
        vel_x_ = 0;
        accel_x_ = 0;
        accel_y_ = 0;
        pos_y_ = PLAY_AREA_BOTTOM - height_;
        if (id_ == Item::Id::COFFEE_MACHINE) {
            floor_collision_ = true;
        }
    }

    // Actualiza la posición del sprite
    sprite_->setPosX(int(pos_x_));
    sprite_->setPosY(int(pos_y_));
    shiftColliders();
}

// Pone a cero todos los valores del objeto
void Item::disable() {
    enabled_ = false;
}

// Actualiza el objeto a su posicion, animación y controla los contadores
void Item::update() {
    move();
    sprite_->animate();
    updateTimeToLive();
    checkTimeToLive();
}

// Actualiza el contador
void Item::updateTimeToLive() {
    if (time_to_live_ > 0) {
        time_to_live_--;
    }
}

// Comprueba si el objeto sigue vivo
void Item::checkTimeToLive() {
    if (time_to_live_ == 0) {
        disable();
    }
}

// Obtiene del valor de la variable
auto Item::getPosX() const -> float {
    return pos_x_;
}

// Obtiene del valor de la variable
auto Item::getPosY() const -> float {
    return pos_y_;
}

// Obtiene del valor de la variable
auto Item::getWidth() const -> int {
    return width_;
}

// Obtiene del valor de la variable
auto Item::getHeight() const -> int {
    return height_;
}

// Obtiene del valor de la variable
auto Item::getId() const -> Id {
    return id_;
}

// Obtiene el valor de la variable
auto Item::isEnabled() const -> bool {
    return enabled_;
}

// Obtiene el circulo de colisión
auto Item::getCollider() -> Circle & {
    return collider_;
}

// Alinea el circulo de colisión con la posición del objeto
void Item::shiftColliders() {
    collider_.x = int(pos_x_ + (width_ / 2));
    collider_.y = int(pos_y_ + (height_ / 2));
}

// Informa si el objeto ha colisionado con el suelo
auto Item::isOnFloor() const -> bool {
    return floor_collision_;
}