#include "game/scenes/instructions.h"

#include <SDL3/SDL.h>

#include <algorithm>  // for max
#include <iostream>   // for char_traits, basic_ostream, operator<<
#include <string>     // for basic_string

#include "core/audio/audio.hpp"          // for Audio::update
#include "core/input/global_inputs.hpp"  // for GlobalInputs::handle
#include "core/input/input.h"            // for Input, REPEAT_FALSE, inputs_e
#include "core/locale/lang.h"            // for Lang
#include "core/rendering/screen.h"       // for Screen
#include "core/rendering/sprite.h"       // for Sprite
#include "core/rendering/text.h"         // for Text, TXT_CENTER, TXT_COLOR, TXT_SHADOW
#include "core/rendering/texture.h"      // for Texture
#include "core/resources/resource.h"
#include "game/defaults.hpp"  // for shdwTxtColor, GAMECANVAS_CENTER_X, GAME...
#include "utils/utils.h"      // for color_t, section_t

// Constructor
Instructions::Instructions(SDL_Renderer *renderer, section_t *section) {
    // Copia los punteros
    this->renderer = renderer;
    this->section = section;

    // Texturas (handles compartidos de Resource)
    Resource *R = Resource::get();
    itemTextures.push_back(R->getTexture("item_points1_disk.png"));
    itemTextures.push_back(R->getTexture("item_points2_gavina.png"));
    itemTextures.push_back(R->getTexture("item_points3_pacmar.png"));
    itemTextures.push_back(R->getTexture("item_clock.png"));
    itemTextures.push_back(R->getTexture("item_coffee.png"));
    itemTextures.push_back(R->getTexture("item_coffee_machine.png"));

    eventHandler = new SDL_Event();

    sprite = new Sprite(0, 0, 16, 16, itemTextures[0], renderer);
    text = R->getText("smb2");

    // Crea un backbuffer para el renderizador
    backbuffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT);
    if (backbuffer != nullptr) {
        SDL_SetTextureScaleMode(backbuffer, Texture::currentScaleMode);
    } else {
        std::cout << "Error: textTexture could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
    }

    // Inicializa variables
    ticks = 0;
    ticksSpeed = 15;
    manualQuit = false;
    counter = 0;
    counterEnd = 600;
    finished = false;
    quitRequested = false;
}

// Destructor
Instructions::~Instructions() {
    // itemTextures y text son propiedad de Resource — no liberar.
    itemTextures.clear();

    delete sprite;
    delete eventHandler;

    SDL_DestroyTexture(backbuffer);
}

// Actualiza las variables
void Instructions::update() {
    // Bombea el stream de música: si no se llama, el buffer se vacía y la
    // música se corta hasta que volvamos a una escena que sí lo haga.
    Audio::update();

    // Comprueba las entradas
    checkInput();

    // Actualiza las variables
    if (SDL_GetTicks() - ticks > ticksSpeed) {
        // Actualiza el contador de ticks
        ticks = SDL_GetTicks();

        if (mode == m_auto) {  // Modo automático
            counter++;

            if (counter == counterEnd) {
                finished = true;
            }
        } else {  // Modo manual
            ++counter %= 60000;

            if (manualQuit) {
                finished = true;
            }
        }
    }
}

// Pinta en pantalla
void Instructions::render() {
    // Pinta en pantalla
    SDL_Rect window = {0, 0, GAMECANVAS_WIDTH, GAMECANVAS_HEIGHT};
    SDL_Rect srcRect = {0, 0, 16, 16};

    const color_t orangeColor = {0xFF, 0x7A, 0x00};

    const SDL_Rect destRect1 = {60, 88 + (16 * 0), 16, 16};  // Disquito
    const SDL_Rect destRect2 = {60, 88 + (16 * 1), 16, 16};  // Gavineixon
    const SDL_Rect destRect3 = {60, 88 + (16 * 2), 16, 16};  // Pacmar
    const SDL_Rect destRect4 = {60, 88 + (16 * 3), 16, 16};  // Time Stopper
    const SDL_Rect destRect5 = {60, 88 + (16 * 4), 16, 16};  // Coffee

    // Pinta en el backbuffer el texto y los sprites
    SDL_SetRenderTarget(renderer, backbuffer);
    SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, 255);
    SDL_RenderClear(renderer);

    // Escribe el texto
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 8, Lang::get()->getText(11), 1, orangeColor, 1, shdwTxtColor);
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 24, Lang::get()->getText(12), 1, noColor, 1, shdwTxtColor);
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 34, Lang::get()->getText(13), 1, noColor, 1, shdwTxtColor);
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 48, Lang::get()->getText(14), 1, noColor, 1, shdwTxtColor);
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 58, Lang::get()->getText(15), 1, noColor, 1, shdwTxtColor);
    text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, 75, Lang::get()->getText(16), 1, orangeColor, 1, shdwTxtColor);

    text->writeShadowed(84, 92, Lang::get()->getText(17), shdwTxtColor);
    text->writeShadowed(84, 108, Lang::get()->getText(18), shdwTxtColor);
    text->writeShadowed(84, 124, Lang::get()->getText(19), shdwTxtColor);
    text->writeShadowed(84, 140, Lang::get()->getText(20), shdwTxtColor);
    text->writeShadowed(84, 156, Lang::get()->getText(21), shdwTxtColor);

    if ((mode == m_manual) && (counter % 50 > 14)) {
        text->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - 12, Lang::get()->getText(22), 1, orangeColor, 1, shdwTxtColor);
    }

    // Disquito
    sprite->setTexture(itemTextures[0]);
    sprite->setPos(destRect1);
    srcRect.y = 16 * (((counter + 12) / 36) % 2);
    sprite->setSpriteClip(srcRect);
    sprite->render();

    // Gavineixon
    sprite->setTexture(itemTextures[1]);
    sprite->setPos(destRect2);
    srcRect.y = 16 * (((counter + 9) / 36) % 2);
    sprite->setSpriteClip(srcRect);
    sprite->render();

    // Pacmar
    sprite->setTexture(itemTextures[2]);
    sprite->setPos(destRect3);
    srcRect.y = 16 * (((counter + 6) / 36) % 2);
    sprite->setSpriteClip(srcRect);
    sprite->render();

    // Time Stopper
    sprite->setTexture(itemTextures[3]);
    sprite->setPos(destRect4);
    srcRect.y = 16 * (((counter + 3) / 36) % 2);
    sprite->setSpriteClip(srcRect);
    sprite->render();

    // Coffee
    sprite->setTexture(itemTextures[4]);
    sprite->setPos(destRect5);
    srcRect.y = 16 * (((counter + 0) / 36) % 2);
    sprite->setSpriteClip(srcRect);
    sprite->render();

    // Cambia el destino de renderizado
    SDL_SetRenderTarget(renderer, nullptr);

    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();

    // Limpia la pantalla
    Screen::get()->clean(bgColor);

    // Establece la ventana del backbuffer
    if (mode == m_auto) {
        window.y = std::max(8, GAMECANVAS_HEIGHT - counter + 100);
    } else {
        window.y = 0;
    }

    // Copia el backbuffer al renderizador
    SDL_FRect fWindow = {(float)window.x, (float)window.y, (float)window.w, (float)window.h};
    SDL_RenderTexture(renderer, backbuffer, nullptr, &fWindow);

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->blit();
}

// Comprueba los eventos
void Instructions::checkEvents() {
#ifndef __EMSCRIPTEN__
    // Comprueba los eventos que hay en la cola
    while (SDL_PollEvent(eventHandler) != 0) {
        // Evento de salida de la aplicación
        if (eventHandler->type == SDL_EVENT_QUIT) {
            quitRequested = true;
            finished = true;
            break;
        }
    }
#endif
}

// Comprueba las entradas
void Instructions::checkInput() {
#ifndef __EMSCRIPTEN__
    if (Input::get()->checkInput(input_exit, REPEAT_FALSE)) {
        quitRequested = true;
        finished = true;
        return;
    }
#endif
    if (GlobalInputs::handle()) { return; }

    if (Input::get()->checkInput(input_pause, REPEAT_FALSE) || Input::get()->checkInput(input_accept, REPEAT_FALSE) || Input::get()->checkInput(input_fire_left, REPEAT_FALSE) || Input::get()->checkInput(input_fire_center, REPEAT_FALSE) || Input::get()->checkInput(input_fire_right, REPEAT_FALSE)) {
        if (mode == m_auto) {
            finished = true;
        } else {
            if (counter > 30) {
                manualQuit = true;
            }
        }
    }
}

// Bucle para la pantalla de instrucciones (compatibilidad)
void Instructions::run(mode_e mode) {
    start(mode);

    while (!finished) {
        update();
        checkEvents();
        render();
    }

    // Aplica los cambios de sección según el resultado
    if (quitRequested) {
        section->name = SECTION_PROG_QUIT;
    } else {
        section->name = SECTION_PROG_TITLE;
        section->subsection = (mode == m_auto) ? SUBSECTION_TITLE_1 : SUBSECTION_TITLE_3;
    }
}

// Inicia las instrucciones (sin bucle)
void Instructions::start(mode_e mode) {
    this->mode = mode;
    finished = false;
    quitRequested = false;
    manualQuit = false;
    counter = 0;
    ticks = 0;
}

// Indica si las instrucciones han terminado
bool Instructions::hasFinished() const {
    return finished;
}

// Indica si se ha solicitado salir de la aplicación
bool Instructions::isQuitRequested() const {
    return quitRequested;
}
