#include "core/rendering/sprite/card_sprite.hpp"

#include <algorithm>   // Para std::clamp
#include <functional>  // Para function
#include <utility>     // Para move

#include "core/rendering/texture.hpp"  // Para Texture
#include "utils/utils.hpp"             // Para easeOutBounce, easeOutCubic

// Constructor
CardSprite::CardSprite(std::shared_ptr<Texture> texture)
    : MovingSprite(std::move(texture)),
      entry_easing_(easeOutBounce) {}

// Inicia la animación de entrada (solo si está en IDLE)
auto CardSprite::enable() -> bool {
    if (state_ != CardState::IDLE) {
        return false;
    }

    state_ = CardState::ENTERING;
    entry_elapsed_ = 0.0F;
    first_touch_ = false;

    // Posición inicial (borde de pantalla)
    setPos(entry_start_x_, entry_start_y_);

    // Zoom inicial grande (como si estuviera cerca de la cámara)
    horizontal_zoom_ = start_zoom_;
    vertical_zoom_ = start_zoom_;

    // Ángulo inicial
    rotate_.angle = start_angle_;
    rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};

    shadow_visible_ = true;
    return true;
}

// Inicia la animación de salida (solo si está en LANDED)
void CardSprite::startExit() {
    if (state_ != CardState::LANDED) {
        return;
    }

    state_ = CardState::EXITING;
    shadow_visible_ = true;

    // Velocidad y aceleración de salida
    vx_ = exit_vx_;
    vy_ = exit_vy_;
    ax_ = exit_ax_;
    ay_ = exit_ay_;

    // Rotación continua
    rotate_.enabled = true;
    rotate_.amount = exit_rotate_amount_;
    rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F};
}

// Actualiza según el estado
void CardSprite::update(float delta_time) {
    switch (state_) {
        case CardState::ENTERING:
            updateEntering(delta_time);
            break;
        case CardState::EXITING:
            updateExiting(delta_time);
            break;
        default:
            break;
    }
}

// Animación de entrada: interpola posición, zoom y ángulo
void CardSprite::updateEntering(float delta_time) {
    entry_elapsed_ += delta_time;

    float progress = std::clamp(entry_elapsed_ / entry_duration_s_, 0.0F, 1.0F);
    double eased = entry_easing_(static_cast<double>(progress));

    // Zoom: de start_zoom_ a 1.0 con rebote
    auto current_zoom = static_cast<float>(start_zoom_ + (1.0 - start_zoom_) * eased);
    horizontal_zoom_ = current_zoom;
    vertical_zoom_ = current_zoom;

    // Ángulo: de start_angle_ a 0 con rebote
    rotate_.angle = start_angle_ * (1.0 - eased);

    // Posición: de entry_start a landing con easing suave (sin rebote)
    // Usamos easeOutCubic para que el desplazamiento sea fluido
    double pos_eased = easeOutCubic(static_cast<double>(progress));
    auto current_x = static_cast<float>(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased);
    auto current_y = static_cast<float>(entry_start_y_ + (landing_y_ - entry_start_y_) * pos_eased);
    setPos(current_x, current_y);

    // Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez)
    if (!first_touch_ && eased >= FIRST_TOUCH_THRESHOLD) {
        first_touch_ = true;
    }

    // Transición a LANDED cuando termina la animación completa
    if (progress >= 1.0F) {
        horizontal_zoom_ = 1.0F;
        vertical_zoom_ = 1.0F;
        rotate_.angle = 0.0;
        setPos(landing_x_, landing_y_);
        state_ = CardState::LANDED;
        first_touch_ = true;
    }
}

// Animación de salida: movimiento + rotación continua + zoom opcional
void CardSprite::updateExiting(float delta_time) {
    move(delta_time);
    rotate(delta_time);

    // Ganar altura gradualmente (zoom hacia el objetivo)
    if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) {
        float new_zoom = horizontal_zoom_ + exit_zoom_speed_ * delta_time;
        if (new_zoom > exit_target_zoom_) {
            new_zoom = exit_target_zoom_;
        }
        horizontal_zoom_ = new_zoom;
        vertical_zoom_ = new_zoom;
    }

    if (isOffScreen()) {
        state_ = CardState::FINISHED;
    }
}

// Renderiza el sprite y su sombra
void CardSprite::render() {
    if (state_ == CardState::IDLE || state_ == CardState::FINISHED) {
        return;
    }

    // Sombra primero (debajo de la tarjeta)
    if (shadow_visible_ && shadow_texture_) {
        renderShadow();
    }

    // Tarjeta
    MovingSprite::render();
}

// Renderiza la sombra con efecto de perspectiva 2D→3D (efecto helicóptero)
//
// Fuente de luz en la esquina superior izquierda (0,0).
// La sombra se mueve con la tarjeta pero desplazada en dirección opuesta a la luz
// (abajo-derecha a 45°). Cuanto más alta la tarjeta (zoom > 1.0):
//   - Más separada de la tarjeta (offset grande)
//   - Más pequeña (proyección lejana)
// Cuando la tarjeta está en la mesa (zoom=1.0):
//   - Sombra pegada con offset base
//   - Tamaño real
void CardSprite::renderShadow() {
    // Altura sobre la mesa: 0.0 = en la mesa, 0.8 = alta (zoom 1.8)
    float height = horizontal_zoom_ - 1.0F;

    // Escala: más pequeña cuanto más alta
    float shadow_zoom = 1.0F / horizontal_zoom_;

    // Offset respecto a la tarjeta: base + extra proporcional a la altura
    // La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0)
    float offset_x = shadow_offset_x_ + height * SHADOW_HEIGHT_MULTIPLIER;
    float offset_y = shadow_offset_y_ + height * SHADOW_HEIGHT_MULTIPLIER;

    shadow_texture_->render(
        pos_.x + offset_x,
        pos_.y + offset_y,
        &sprite_clip_,
        shadow_zoom,
        shadow_zoom,
        rotate_.angle,
        &rotate_.center,
        flip_);
}

// Comprueba si el sprite está fuera de pantalla
auto CardSprite::isOffScreen() const -> bool {
    float effective_width = pos_.w * horizontal_zoom_;
    float effective_height = pos_.h * vertical_zoom_;
    return (pos_.x + effective_width < -OFF_SCREEN_MARGIN ||
        pos_.x > screen_width_ + OFF_SCREEN_MARGIN ||
        pos_.y + effective_height < -OFF_SCREEN_MARGIN ||
        pos_.y > screen_height_ + OFF_SCREEN_MARGIN);
}

// --- Consultas de estado ---
auto CardSprite::hasLanded() const -> bool {
    return state_ == CardState::LANDED || state_ == CardState::EXITING || state_ == CardState::FINISHED;
}

auto CardSprite::hasFirstTouch() const -> bool {
    return first_touch_;
}

auto CardSprite::hasFinished() const -> bool {
    return state_ == CardState::FINISHED;
}

auto CardSprite::isExiting() const -> bool {
    return state_ == CardState::EXITING;
}

auto CardSprite::getState() const -> CardState {
    return state_;
}

// --- Configuración ---
void CardSprite::setEntryParams(float start_zoom, double start_angle, float duration_s, std::function<double(double)> easing) {
    start_zoom_ = start_zoom;
    start_angle_ = start_angle;
    entry_duration_s_ = duration_s;
    entry_easing_ = std::move(easing);
}

void CardSprite::setEntryPosition(float start_x, float start_y) {
    entry_start_x_ = start_x;
    entry_start_y_ = start_y;
}

void CardSprite::setLandingPosition(float x, float y) {
    landing_x_ = x;
    landing_y_ = y;
}

void CardSprite::setExitParams(float vx, float vy, float ax, float ay, double rotate_amount) {
    exit_vx_ = vx;
    exit_vy_ = vy;
    exit_ax_ = ax;
    exit_ay_ = ay;
    exit_rotate_amount_ = rotate_amount;
}

void CardSprite::setExitLift(float target_zoom, float zoom_speed) {
    exit_target_zoom_ = target_zoom;
    exit_zoom_speed_ = zoom_speed;
}

void CardSprite::setShadowTexture(std::shared_ptr<Texture> texture) {
    shadow_texture_ = std::move(texture);
}

void CardSprite::setShadowOffset(float offset_x, float offset_y) {
    shadow_offset_x_ = offset_x;
    shadow_offset_y_ = offset_y;
}

void CardSprite::setScreenBounds(float width, float height) {
    screen_width_ = width;
    screen_height_ = height;
}
