#include "game/gameplay/game_logo.hpp"

#include <SDL3/SDL.h>  // Para SDL_SetTextureScaleMode, SDL_FlipMode, SDL_ScaleMode

#include <algorithm>  // Para max
#include <string>     // Para basic_string

#include "core/audio/audio.hpp"                       // Para Audio
#include "core/rendering/screen.hpp"                  // Para Screen
#include "core/rendering/sprite/animated_sprite.hpp"  // Para AnimatedSprite
#include "core/rendering/sprite/smart_sprite.hpp"     // Para SmartSprite
#include "core/rendering/sprite/sprite.hpp"           // Para Sprite
#include "core/rendering/texture.hpp"                 // Para Texture
#include "core/resources/resource.hpp"                // Para Resource
#include "utils/color.hpp"                            // Para Color
#include "utils/param.hpp"                            // Para Param, param, ParamGame, ParamTitle

constexpr int ZOOM_FACTOR = 5;
constexpr float FLASH_DELAY_S = 0.05F;                  // 3 frames → 0.05s
constexpr float FLASH_DURATION_S = 0.1F;                // 6 frames → 0.1s (3 + 3)
constexpr Color FLASH_COLOR = Color(0xFF, 0xFF, 0xFF);  // Color blanco para el flash

// Constructor
GameLogo::GameLogo(int x, int y)
    : dust_texture_(Resource::get()->getTexture("title_dust.png")),
      coffee_texture_(Resource::get()->getTexture("title_coffee.png")),
      crisis_texture_(Resource::get()->getTexture("title_crisis.png")),
      arcade_edition_texture_(Resource::get()->getTexture("title_arcade_edition.png")),
      dust_left_sprite_(std::make_unique<AnimatedSprite>(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))),
      dust_right_sprite_(std::make_unique<AnimatedSprite>(dust_texture_, Resource::get()->getAnimation("title_dust.ani"))),
      coffee_sprite_(std::make_unique<SmartSprite>(coffee_texture_)),
      crisis_sprite_(std::make_unique<SmartSprite>(crisis_texture_)),
      arcade_edition_sprite_(std::make_unique<Sprite>(arcade_edition_texture_, (param.game.width - arcade_edition_texture_->getWidth()) / 2, param.title.arcade_edition_position, arcade_edition_texture_->getWidth(), arcade_edition_texture_->getHeight())),
      x_(x),
      y_(y) {}

// Inicializa las variables
void GameLogo::init() {
    const auto XP = x_ - (coffee_texture_->getWidth() / 2);
    const auto DESP = getInitialVerticalDesp();

    // Configura texturas
    SDL_SetTextureScaleMode(Resource::get()->getTexture("title_arcade_edition.png")->getSDLTexture(), SDL_SCALEMODE_NEAREST);

    // Variables
    coffee_crisis_status_ = Status::DISABLED;
    arcade_edition_status_ = Status::DISABLED;
    shake_.init(1, 2, 8, XP);
    zoom_ = 3.0F * ZOOM_FACTOR;
    post_finished_timer_ = 0.0F;

    // Inicializa el bitmap de 'Coffee'
    coffee_sprite_->setPosX(XP);
    coffee_sprite_->setPosY(y_ - coffee_texture_->getHeight() - DESP);
    coffee_sprite_->setWidth(coffee_texture_->getWidth());
    coffee_sprite_->setHeight(coffee_texture_->getHeight());
    coffee_sprite_->setVelX(0.0F);
    coffee_sprite_->setVelY(COFFEE_VEL_Y);
    coffee_sprite_->setAccelX(0.0F);
    coffee_sprite_->setAccelY(COFFEE_ACCEL_Y);
    coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight());
    coffee_sprite_->setEnabled(true);
    coffee_sprite_->setFinishedDelay(0.0F);
    coffee_sprite_->setDestX(XP);
    coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight());

    // Inicializa el bitmap de 'Crisis'
    crisis_sprite_->setPosX(XP + CRISIS_OFFSET_X);
    crisis_sprite_->setPosY(y_ + DESP);
    crisis_sprite_->setWidth(crisis_texture_->getWidth());
    crisis_sprite_->setHeight(crisis_texture_->getHeight());
    crisis_sprite_->setVelX(0.0F);
    crisis_sprite_->setVelY(CRISIS_VEL_Y);
    crisis_sprite_->setAccelX(0.0F);
    crisis_sprite_->setAccelY(CRISIS_ACCEL_Y);
    crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight());
    crisis_sprite_->setEnabled(true);
    crisis_sprite_->setFinishedDelay(0.0F);
    crisis_sprite_->setDestX(XP + CRISIS_OFFSET_X);
    crisis_sprite_->setDestY(y_);

    // Inicializa el bitmap de 'DustRight'
    dust_right_sprite_->resetAnimation();
    dust_right_sprite_->setPosX(coffee_sprite_->getPosX() + coffee_sprite_->getWidth());
    dust_right_sprite_->setPosY(y_);
    dust_right_sprite_->setWidth(DUST_SIZE);
    dust_right_sprite_->setHeight(DUST_SIZE);
    dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL);

    // Inicializa el bitmap de 'DustLeft'
    dust_left_sprite_->resetAnimation();
    dust_left_sprite_->setPosX(coffee_sprite_->getPosX() - DUST_SIZE);
    dust_left_sprite_->setPosY(y_);
    dust_left_sprite_->setWidth(DUST_SIZE);
    dust_left_sprite_->setHeight(DUST_SIZE);

    // Inicializa el bitmap de 'Arcade Edition'
    arcade_edition_sprite_->setZoom(zoom_);
}

// Pinta la clase en pantalla
void GameLogo::render() {
    // Dibuja el logo
    coffee_sprite_->render();
    crisis_sprite_->render();

    if (arcade_edition_status_ != Status::DISABLED) {
        arcade_edition_sprite_->render();
    }

    // Dibuja el polvillo del logo
    if (coffee_crisis_status_ != Status::MOVING) {
        dust_right_sprite_->render();
        dust_left_sprite_->render();
    }
}

// Actualiza la lógica de la clase (time-based)
void GameLogo::update(float delta_time) {
    updateCoffeeCrisis(delta_time);
    updateArcadeEdition(delta_time);
    updatePostFinishedCounter(delta_time);
}

void GameLogo::updateCoffeeCrisis(float delta_time) {
    switch (coffee_crisis_status_) {
        case Status::MOVING:
            handleCoffeeCrisisMoving(delta_time);
            break;
        case Status::SHAKING:
            handleCoffeeCrisisShaking(delta_time);
            break;
        case Status::FINISHED:
            handleCoffeeCrisisFinished(delta_time);
            break;
        default:
            break;
    }
}

void GameLogo::updateArcadeEdition(float delta_time) {
    switch (arcade_edition_status_) {
        case Status::MOVING:
            handleArcadeEditionMoving(delta_time);
            break;
        case Status::SHAKING:
            handleArcadeEditionShaking(delta_time);
            break;
        default:
            break;
    }
}

void GameLogo::handleCoffeeCrisisMoving(float delta_time) {
    coffee_sprite_->update(delta_time);
    crisis_sprite_->update(delta_time);

    if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) {
        coffee_crisis_status_ = Status::SHAKING;
        playTitleEffects();
    }
}

void GameLogo::handleCoffeeCrisisShaking(float delta_time) {
    if (shake_.remaining > 0) {
        processShakeEffect(coffee_sprite_.get(), crisis_sprite_.get(), delta_time);
    } else {
        finishCoffeeCrisisShaking();
    }

    updateDustSprites(delta_time);
}

void GameLogo::handleCoffeeCrisisFinished(float delta_time) {
    updateDustSprites(delta_time);
}

void GameLogo::handleArcadeEditionMoving(float delta_time) {
    // DeltaTime en segundos: decremento por segundo
    zoom_ -= (ZOOM_DECREMENT_PER_S * ZOOM_FACTOR) * delta_time;
    arcade_edition_sprite_->setZoom(zoom_);

    if (zoom_ <= 1.0F) {
        finishArcadeEditionMoving();
    }
}

void GameLogo::handleArcadeEditionShaking(float delta_time) {
    if (shake_.remaining > 0) {
        processArcadeEditionShake(delta_time);
    } else {
        arcade_edition_sprite_->setX(shake_.origin);
        arcade_edition_status_ = Status::FINISHED;
    }
}

void GameLogo::processShakeEffect(SmartSprite* primary_sprite, SmartSprite* secondary_sprite, float delta_time) {
    shake_.time_accumulator += delta_time;

    if (shake_.time_accumulator >= SHAKE_DELAY_S) {
        shake_.time_accumulator -= SHAKE_DELAY_S;
        const auto DISPLACEMENT = calculateShakeDisplacement();
        primary_sprite->setPosX(shake_.origin + DISPLACEMENT);
        if (secondary_sprite != nullptr) {
            secondary_sprite->setPosX(shake_.origin + DISPLACEMENT + CRISIS_OFFSET_X);
        }
        shake_.remaining--;
    }
}

void GameLogo::processArcadeEditionShake(float delta_time) {
    // Delay fijo en segundos (shake_.delay era frames, ahora usamos constante)
    float delay_time = SHAKE_DELAY_S;

    shake_.time_accumulator += delta_time;

    if (shake_.time_accumulator >= delay_time) {
        shake_.time_accumulator -= delay_time;
        const auto DISPLACEMENT = calculateShakeDisplacement();
        arcade_edition_sprite_->setX(shake_.origin + DISPLACEMENT);
        shake_.remaining--;
    }
}

auto GameLogo::calculateShakeDisplacement() const -> int {
    return shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
}

void GameLogo::finishCoffeeCrisisShaking() {
    coffee_sprite_->setPosX(shake_.origin);
    crisis_sprite_->setPosX(shake_.origin + CRISIS_OFFSET_X);
    coffee_crisis_status_ = Status::FINISHED;
    arcade_edition_status_ = Status::MOVING;
}

void GameLogo::finishArcadeEditionMoving() {
    arcade_edition_status_ = Status::SHAKING;
    zoom_ = 1.0F;
    arcade_edition_sprite_->setZoom(zoom_);
    shake_.init(1, 2, 8, arcade_edition_sprite_->getX());
    playTitleEffects();
}

void GameLogo::playTitleEffects() {
    Audio::get()->playSound("title.wav");
    Screen::get()->flash(FLASH_COLOR, FLASH_DURATION_S, FLASH_DELAY_S);
    Screen::get()->shake();
}

void GameLogo::updateDustSprites(float delta_time) {
    dust_right_sprite_->update(delta_time);
    dust_left_sprite_->update(delta_time);
}

void GameLogo::updatePostFinishedCounter(float delta_time) {
    if (coffee_crisis_status_ == Status::FINISHED &&
        arcade_edition_status_ == Status::FINISHED) {
        post_finished_timer_ += delta_time;
    }
}

// Activa la clase
void GameLogo::enable() {
    init();
    coffee_crisis_status_ = Status::MOVING;
}

// Indica si ha terminado la animación
auto GameLogo::hasFinished() const -> bool {
    return post_finished_timer_ >= post_finished_delay_s_;
}

// Calcula el desplazamiento vertical inicial
auto GameLogo::getInitialVerticalDesp() const -> int {
    const float OFFSET_UP = y_;
    const float OFFSET_DOWN = param.game.height - y_;

    return std::max(OFFSET_UP, OFFSET_DOWN);
}