#include "game_logo.h"
#include <SDL3/SDL_surface.h> // Para SDL_FlipMode
#include <algorithm>          // Para max
#include "animated_sprite.h"  // Para AnimatedSprite
#include "audio.h"            // Para JA_PlaySound
#include "param.h"            // Para Param, param, ParamGame, ParamTitle
#include "resource.h"         // Para Resource
#include "screen.h"           // Para Screen
#include "smart_sprite.h"     // Para SmartSprite
#include "sprite.h"           // Para Sprite
#include "texture.h"          // Para Texture
#include "utils.h"            // Para Color

constexpr int ZOOM_FACTOR = 5;
constexpr int FLASH_DELAY = 3;
constexpr int FLASH_LENGHT = FLASH_DELAY + 3;

// 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();

    // Variables
    coffee_crisis_status_ = Status::DISABLED;
    arcade_edition_status_ = Status::DISABLED;
    shake_.init(1, 2, 8, xp);
    zoom_ = 3.0f * ZOOM_FACTOR;

    // 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(2.5f);
    coffee_sprite_->setAccelX(0.0f);
    coffee_sprite_->setAccelY(0.1f);
    coffee_sprite_->setSpriteClip(0, 0, coffee_texture_->getWidth(), coffee_texture_->getHeight());
    coffee_sprite_->setEnabled(true);
    coffee_sprite_->setFinishedCounter(0);
    coffee_sprite_->setDestX(xp);
    coffee_sprite_->setDestY(y_ - coffee_texture_->getHeight());

    // Inicializa el bitmap de 'Crisis'
    crisis_sprite_->setPosX(xp + 15);
    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(-2.5f);
    crisis_sprite_->setAccelX(0.0f);
    crisis_sprite_->setAccelY(-0.1f);
    crisis_sprite_->setSpriteClip(0, 0, crisis_texture_->getWidth(), crisis_texture_->getHeight());
    crisis_sprite_->setEnabled(true);
    crisis_sprite_->setFinishedCounter(0);
    crisis_sprite_->setDestX(xp + 15);
    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(16);
    dust_right_sprite_->setHeight(16);
    dust_right_sprite_->setFlip(SDL_FLIP_HORIZONTAL);

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

    // 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
    dust_right_sprite_->render();
    dust_left_sprite_->render();
}

// Actualiza la lógica de la clase
void GameLogo::update()
{
    switch (coffee_crisis_status_)
    {
    case Status::MOVING:
    {
        coffee_sprite_->update();
        crisis_sprite_->update();

        // Si los objetos han llegado a su destino, cambia el estado
        if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished())
        {
            coffee_crisis_status_ = Status::SHAKING;

            // Reproduce el efecto sonoro
            Audio::get().playSound("title.wav");
            Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
            Screen::get()->shake();
        }

        break;
    }

    case Status::SHAKING:
    {
        // Agita "COFFEE CRISIS"
        if (shake_.remaining > 0)
        {
            if (shake_.counter > 0)
            {
                shake_.counter--;
            }
            else
            {
                shake_.counter = shake_.delay;
                const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
                coffee_sprite_->setPosX(shake_.origin + desp);
                crisis_sprite_->setPosX(shake_.origin + desp + 15);
                shake_.remaining--;
            }
        }
        else
        {
            coffee_sprite_->setPosX(shake_.origin);
            crisis_sprite_->setPosX(shake_.origin + 15);
            coffee_crisis_status_ = Status::FINISHED;
            arcade_edition_status_ = Status::MOVING;
        }

        dust_right_sprite_->update();
        dust_left_sprite_->update();

        break;
    }

    case Status::FINISHED:
    {
        dust_right_sprite_->update();
        dust_left_sprite_->update();

        break;
    }

    default:
        break;
    }

    switch (arcade_edition_status_)
    {
    case Status::MOVING:
    {
        zoom_ -= 0.1f * ZOOM_FACTOR;
        arcade_edition_sprite_->setZoom(zoom_);
        if (zoom_ <= 1.0f)
        {
            arcade_edition_status_ = Status::SHAKING;
            zoom_ = 1.0f;
            arcade_edition_sprite_->setZoom(zoom_);
            shake_.init(1, 2, 8, arcade_edition_sprite_->getX());
            Audio::get().playSound("title.wav");
            Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY);
            Screen::get()->shake();
        }
        break;
    }

    case Status::SHAKING:
    {
        // Agita "ARCADE EDITION"
        if (shake_.remaining > 0)
        {
            if (shake_.counter > 0)
            {
                shake_.counter--;
            }
            else
            {
                shake_.counter = shake_.delay;
                const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp;
                arcade_edition_sprite_->setX(shake_.origin + desp);
                shake_.remaining--;
            }
        }
        else
        {
            arcade_edition_sprite_->setX(shake_.origin);
            arcade_edition_status_ = Status::FINISHED;
        }
        break;
    }

    default:
        break;
    }

    if (coffee_crisis_status_ == Status::FINISHED &&
        arcade_edition_status_ == Status::FINISHED &&
        post_finished_counter_ > 0)
    {
        --post_finished_counter_;
    }
}

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

// Indica si ha terminado la animación
bool GameLogo::hasFinished() const
{
    return post_finished_counter_ == 0;
}

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

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