#pragma once

#include <SDL3/SDL.h>

#include <memory>  // Para shared_ptr
#include <string>  // Para string
#include <vector>  // Para vector

#include "utils/defines.hpp"  // Para GAMECANVAS_WIDTH, GAMECANVAS_FIRST_QUAR...
class SurfaceAnimatedSprite;  // lines 9-9
class SurfaceMovingSprite;    // lines 10-10

class Ending2 {
    private:
        // Enum para representar los estados del final
        enum class EndingState : int {
            PRE_CREDITS,   // Estado previo a los créditos
            CREDITS,       // Estado de los créditos
            POST_CREDITS,  // Estado posterior a los créditos
            FADING,        // Estado de fundido de los textos a negrp
        };

        // Estructura para controlar los estados y su duración
        struct State {
                EndingState state;  // Estado actual
                Uint32 init_ticks;  // Ticks en los que se inicializó el estado
                Uint32 duration;    // Duración en milisegundos para el estado actual

                // Constructor parametrizado para inicializar la estructura
                State(EndingState initial_state, Uint32 initial_ticks, Uint32 state_duration)
                    : state(initial_state),
                      init_ticks(initial_ticks),
                      duration(state_duration) {}

                // Método para comprobar si el estado ha terminado y verifica el nombre del estado
                [[nodiscard]] auto hasEnded(EndingState expected_state) const -> bool {
                    // Comprobar si el estado actual coincide con el estado esperado
                    if (state != expected_state) {
                        return false;  // Si no coincide, considerar que no ha terminado
                    }

                    // Comprobar si el tiempo transcurrido excede la duración
                    return (SDL_GetTicks() - init_ticks) >= duration;
                }

                // Método para establecer un nuevo estado
                void set(EndingState new_state, Uint32 new_duration) {
                    state = new_state;            // Actualizar el estado
                    init_ticks = SDL_GetTicks();  // Reiniciar el tiempo de inicio
                    duration = new_duration;      // Actualizar la duración
                }
        };

        // Constantes
        static constexpr int FIRST_COL = GAMECANVAS_FIRST_QUARTER_X + (GAMECANVAS_WIDTH / 16);   // Primera columna por donde desfilan los sprites
        static constexpr int SECOND_COL = GAMECANVAS_THIRD_QUARTER_X - (GAMECANVAS_WIDTH / 16);  // Segunda columna por donde desfilan los sprites
        static constexpr int DIST_SPRITE_TEXT = 8;                                               // Distancia entre el sprite y el texto que lo acompaña
        static constexpr int DIST_SPRITE_SPRITE = 0;                                             // Distancia entre dos sprites de la misma columna
        static constexpr float SPRITE_DESP_SPEED = -0.2F;                                        // Velocidad de desplazamiento de los sprites
        static constexpr int STATE_PRE_CREDITS_DURATION = 3000;
        static constexpr int STATE_POST_CREDITS_DURATION = 5000;
        static constexpr int STATE_FADE_DURATION = 5000;

        // Objetos y punteros
        std::vector<std::shared_ptr<SurfaceAnimatedSprite>> sprites_;     // Vector con todos los sprites a dibujar
        std::vector<std::shared_ptr<SurfaceMovingSprite>> sprite_texts_;  // Vector con los sprites de texto de los sprites
        std::vector<std::shared_ptr<SurfaceMovingSprite>> texts_;         // Vector con los sprites de texto

        // Variables
        Uint32 ticks_ = 0;                      // Contador de ticks para ajustar la velocidad del programa
        std::vector<std::string> sprite_list_;  // Lista con todos los sprites a dibujar
        std::vector<Uint8> colors_;             // Vector con los colores para el fade
        float sprite_max_width_ = 0;            // El valor de ancho del sprite mas ancho
        float sprite_max_height_ = 0;           // El valor de alto del sprite mas alto
        State state_;                           // Controla el estado de la clase

        // Actualiza el objeto
        void update();

        // Dibuja el final en pantalla
        void render();

        // Comprueba el manejador de eventos
        static void checkEvents();

        // Comprueba las entradas
        static void checkInput();

        // Actualiza el estado
        void updateState();

        // Inicializa la lista de sprites
        void iniSpriteList();

        // Carga todos los sprites desde una lista
        void loadSprites();

        // Actualiza los sprites
        void updateSprites();

        // Actualiza los sprites de texto
        void updateTextSprites();

        // Actualiza los sprites de texto del final
        void updateTexts();

        // Dibuja los sprites
        void renderSprites();

        // Dibuja los sprites con el texto
        void renderSpriteTexts();

        // Dibuja los sprites con el texto del final
        void renderTexts();

        // Coloca los sprites en su sito
        void placeSprites();

        // Crea los sprites con las texturas con los textos
        void createSpriteTexts();

        // Crea los sprites con las texturas con los textos del final
        void createTexts();

        // Actualiza el fade final
        void updateFinalFade();

        // Actualiza el volumen de la musica
        void updateMusicVolume() const;

    public:
        // Constructor
        Ending2();

        // Destructor
        ~Ending2() = default;

        // Bucle principal
        void run();
};
