#include "game_logo.h"
#include <algorithm>         // for max
#include <string>            // for basic_string
#include "animated_sprite.h" // for AnimatedSprite
#include "asset.h"           // for Asset
#include "jail_audio.h"      // for JA_DeleteSound, JA_LoadSound, JA_PlaySound
#include "param.h"           // for param
#include "screen.h"          // for Screen
#include "smart_sprite.h"    // for SmartSprite
#include "sprite.h"          // for Sprite
#include "texture.h"         // for Texture
#include "utils.h"           // for Param, ParamGame, ParamTitle

// Constructor
GameLogo::GameLogo(int x, int y)
    : x(x), y(y)
{
    // Crea los objetos
    dustTexture = std::make_shared<Texture>(Screen::get()->getRenderer(), Asset::get()->get("title_dust.png"));
    coffeeTexture = std::make_shared<Texture>(Screen::get()->getRenderer(), Asset::get()->get("title_coffee.png"));
    crisisTexture = std::make_shared<Texture>(Screen::get()->getRenderer(), Asset::get()->get("title_crisis.png"));
    arcadeEditionTexture = std::make_shared<Texture>(Screen::get()->getRenderer(), Asset::get()->get("title_arcade_edition.png"));

    coffeeSprite = std::make_unique<SmartSprite>(coffeeTexture);
    crisisSprite = std::make_unique<SmartSprite>(crisisTexture);

    arcadeEditionSprite = std::make_unique<Sprite>((param.game.width - arcadeEditionTexture->getWidth()) / 2, param.title.arcade_edition_position, arcadeEditionTexture->getWidth(), arcadeEditionTexture->getHeight(), arcadeEditionTexture);

    dustLSprite = std::make_unique<AnimatedSprite>(dustTexture, Asset::get()->get("title_dust.ani"));
    dustRSprite = std::make_unique<AnimatedSprite>(dustTexture, Asset::get()->get("title_dust.ani"));

    // Sonidos
    crashSound = JA_LoadSound(Asset::get()->get("title.wav").c_str());

    // Inicializa las variables
    init();
}

// Destructor
GameLogo::~GameLogo()
{
    JA_DeleteSound(crashSound);
}

// Inicializa las variables
void GameLogo::init()
{
    const auto xp = x - coffeeSprite->getWidth() / 2;
    const auto desp = getInitialVerticalDesp();

    // Variables
    status = Status::DISABLED;
    shake.desp = 1;
    shake.delay = 2;
    shake.lenght = 8;
    shake.remaining = shake.lenght;
    shake.counter = shake.delay;
    shake.origin = xp;

    // Inicializa el bitmap de 'Coffee'
    coffeeSprite->init();
    coffeeSprite->setPosX(xp);
    coffeeSprite->setPosY(y - coffeeTexture->getHeight() - desp);
    coffeeSprite->setWidth(coffeeTexture->getWidth());
    coffeeSprite->setHeight(coffeeTexture->getHeight());
    coffeeSprite->setVelX(0.0f);
    coffeeSprite->setVelY(2.5f);
    coffeeSprite->setAccelX(0.0f);
    coffeeSprite->setAccelY(0.1f);
    coffeeSprite->setSpriteClip(0, 0, coffeeTexture->getWidth(), coffeeTexture->getHeight());
    coffeeSprite->setEnabled(true);
    coffeeSprite->setFinishedCounter(0);
    coffeeSprite->setDestX(xp);
    coffeeSprite->setDestY(y - coffeeTexture->getHeight());

    // Inicializa el bitmap de 'Crisis'
    crisisSprite->init();
    crisisSprite->setPosX(xp + 15);
    crisisSprite->setPosY(y + desp);
    crisisSprite->setWidth(crisisTexture->getWidth());
    crisisSprite->setHeight(crisisTexture->getHeight());
    crisisSprite->setVelX(0.0f);
    crisisSprite->setVelY(-2.5f);
    crisisSprite->setAccelX(0.0f);
    crisisSprite->setAccelY(-0.1f);
    crisisSprite->setSpriteClip(0, 0, crisisTexture->getWidth(), crisisTexture->getHeight());
    crisisSprite->setEnabled(true);
    crisisSprite->setFinishedCounter(0);
    crisisSprite->setDestX(xp + 15);
    crisisSprite->setDestY(y);

    // Inicializa el bitmap de 'DustRight'
    dustRSprite->resetAnimation();
    dustRSprite->setPosX(coffeeSprite->getPosX() + coffeeSprite->getWidth());
    dustRSprite->setPosY(y);
    dustRSprite->setWidth(16);
    dustRSprite->setHeight(16);
    dustRSprite->setFlip(SDL_FLIP_HORIZONTAL);

    // Inicializa el bitmap de 'DustLeft'
    dustLSprite->resetAnimation();
    dustLSprite->setPosX(coffeeSprite->getPosX() - 16);
    dustLSprite->setPosY(y);
    dustLSprite->setWidth(16);
    dustLSprite->setHeight(16);
}

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

    if (status == Status::FINISHED)
    {
        arcadeEditionSprite->render();
    }

    // Dibuja el polvillo del logo
    dustRSprite->render();
    dustLSprite->render();
}

// Actualiza la lógica de la clase
void GameLogo::update()
{
    if (status == Status::MOVING)
    {
        coffeeSprite->update();
        crisisSprite->update();

        // Si los objetos han llegado a su destino, cambiamos de Sección
        if (coffeeSprite->hasFinished() && crisisSprite->hasFinished())
        {
            status = Status::SHAKING;

            // Reproduce el efecto sonoro
            JA_PlaySound(crashSound);
        }
    }

    else if (status == Status::SHAKING)
    {
        // Agita el logo
        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;
                coffeeSprite->setPosX(shake.origin + desp);
                crisisSprite->setPosX(shake.origin + desp + 15);
                shake.remaining--;
            }
        }
        else
        {
            coffeeSprite->setPosX(shake.origin);
            crisisSprite->setPosX(shake.origin + 15);
            status = Status::FINISHED;
        }

        dustRSprite->update();
        dustLSprite->update();
    }

    else if (status == Status::FINISHED)
    {
        dustRSprite->update();
        dustLSprite->update();
    }
}

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

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

// Recarga las texturas
void GameLogo::reLoad()
{
    dustTexture->reLoad();
    coffeeTexture->reLoad();
    crisisTexture->reLoad();
}

// Calcula el desplazamiento vertical inicial
int GameLogo::getInitialVerticalDesp()
{
    auto despUp = y;
    auto despDown = param.game.height - y;

    return std::max(despUp, despDown);
}