#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, InputAction
#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, Section

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

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

    event_handler_ = new SDL_Event();

    sprite_ = new Sprite(0, 0, 16, 16, item_textures_[0], renderer);
    text_ = resource->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::current_scale_mode);
    } else {
        std::cout << "Error: textTexture could not be created!\nSDL Error: " << SDL_GetError() << '\n';
    }

    // Inicializa variables
    ticks_ = 0;
    ticks_speed_ = 15;
    manual_quit_ = false;
    counter_ = 0;
    counter_end_ = 600;
    finished_ = false;
    quit_requested_ = false;
}

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

    delete sprite_;
    delete event_handler_;

    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_ > ticks_speed_) {
        // Actualiza el contador de ticks
        ticks_ = SDL_GetTicks();

        if (mode_ == Mode::AUTO) {  // Modo automático
            counter_++;

            if (counter_ == counter_end_) {
                finished_ = true;
            }
        } else {  // Modo manual
            ++counter_ %= 60000;

            if (manual_quit_) {
                finished_ = true;
            }
        }
    }
}

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

    const Color ORANGE_COLOR = {0xFF, 0x7A, 0x00};

    const SDL_Rect DEST_RECT1 = {60, 88 + (16 * 0), 16, 16};  // Disquito
    const SDL_Rect DEST_RECT2 = {60, 88 + (16 * 1), 16, 16};  // Gavineixon
    const SDL_Rect DEST_RECT3 = {60, 88 + (16 * 2), 16, 16};  // Pacmar
    const SDL_Rect DEST_RECT4 = {60, 88 + (16 * 3), 16, 16};  // Time Stopper
    const SDL_Rect DEST_RECT5 = {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, ORANGE_COLOR, 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, ORANGE_COLOR, 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_ == Mode::MANUAL) && (counter_ % 50 > 14)) {
        text_->writeDX(TXT_CENTER | TXT_COLOR | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - 12, Lang::get()->getText(22), 1, ORANGE_COLOR, 1, shdwTxtColor);
    }

    // Disquito
    sprite_->setTexture(item_textures_[0]);
    sprite_->setPos(DEST_RECT1);
    src_rect.y = 16 * (((counter_ + 12) / 36) % 2);
    sprite_->setSpriteClip(src_rect);
    sprite_->render();

    // Gavineixon
    sprite_->setTexture(item_textures_[1]);
    sprite_->setPos(DEST_RECT2);
    src_rect.y = 16 * (((counter_ + 9) / 36) % 2);
    sprite_->setSpriteClip(src_rect);
    sprite_->render();

    // Pacmar
    sprite_->setTexture(item_textures_[2]);
    sprite_->setPos(DEST_RECT3);
    src_rect.y = 16 * (((counter_ + 6) / 36) % 2);
    sprite_->setSpriteClip(src_rect);
    sprite_->render();

    // Time Stopper
    sprite_->setTexture(item_textures_[3]);
    sprite_->setPos(DEST_RECT4);
    src_rect.y = 16 * (((counter_ + 3) / 36) % 2);
    sprite_->setSpriteClip(src_rect);
    sprite_->render();

    // Coffee
    sprite_->setTexture(item_textures_[4]);
    sprite_->setPos(DEST_RECT5);
    src_rect.y = 16 * (((counter_ + 0) / 36) % 2);
    sprite_->setSpriteClip(src_rect);
    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_ == Mode::AUTO) {
        window.y = std::max(8, GAMECANVAS_HEIGHT - counter_ + 100);
    } else {
        window.y = 0;
    }

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

    // 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 (static_cast<int>(SDL_PollEvent(event_handler_)) != 0) {
        // Evento de salida de la aplicación
        if (event_handler_->type == SDL_EVENT_QUIT) {
            quit_requested_ = true;
            finished_ = true;
            break;
        }
    }
#endif
}

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

    if (Input::get()->checkInput(PAUSE, REPEAT_FALSE) || Input::get()->checkInput(ACCEPT, REPEAT_FALSE) || Input::get()->checkInput(FIRE_LEFT, REPEAT_FALSE) || Input::get()->checkInput(FIRE_CENTER, REPEAT_FALSE) || Input::get()->checkInput(FIRE_RIGHT, REPEAT_FALSE)) {
        if (mode_ == Mode::AUTO) {
            finished_ = true;
        } else {
            if (counter_ > 30) {
                manual_quit_ = true;
            }
        }
    }
}

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

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

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

// Inicia las instrucciones (sin bucle)
void Instructions::start(Mode mode) {
    mode_ = mode;
    finished_ = false;
    quit_requested_ = false;
    manual_quit_ = false;
    counter_ = 0;
    ticks_ = 0;
}

// Indica si las instrucciones han terminado
auto Instructions::hasFinished() const -> bool {
    return finished_;
}

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