#pragma once

#include <SDL3/SDL.h>  // Para SDL_FRect

#include <algorithm>      // Para max
#include <cstddef>        // Para size_t
#include <memory>         // Para allocator, shared_ptr
#include <string>         // Para string, hash
#include <unordered_map>  // Para unordered_map
#include <vector>         // Para vector

#include "moving_sprite.h"  // Para MovingSprite

// Declaración adelantada
class Texture;

// Estructura de Animación
struct Animation {
        static constexpr int DEFAULT_SPEED = 5;

        std::string name;               // Nombre de la animación
        std::vector<SDL_FRect> frames;  // Frames que componen la animación
        int speed{DEFAULT_SPEED};       // Velocidad de reproducción
        int loop{0};                    // Frame de vuelta al terminar (-1 para no repetir)
        bool completed{false};          // Indica si la animación ha finalizado
        size_t current_frame{0};        // Frame actual en reproducción
        int counter{0};                 // Contador para la animación

        Animation() = default;
};

struct AnimationConfig {
        float frame_width = 1.0F;
        float frame_height = 1.0F;
        int frames_per_row = 1;
        int max_tiles = 1;
};

// Alias de tipo para buffer de animaciones
using AnimationsFileBuffer = std::vector<std::string>;

// Carga las animaciones desde un fichero en un vector de strings
auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffer;

// Clase AnimatedSprite: Sprite animado que hereda de MovingSprite
class AnimatedSprite : public MovingSprite {
    public:
        // --- Constructores y destructor ---
        AnimatedSprite(std::shared_ptr<Texture> texture, const std::string& file_path);
        AnimatedSprite(std::shared_ptr<Texture> texture, const AnimationsFileBuffer& animations);
        explicit AnimatedSprite(std::shared_ptr<Texture> texture) : MovingSprite(texture) {}
        ~AnimatedSprite() override = default;

        // --- Métodos principales ---
        void update() override;  // Actualiza la animación

        // --- Control de animaciones ---
        void setCurrentAnimation(const std::string& name = "default", bool reset = true);           // Establece la animación por nombre
        void setCurrentAnimation(int index = 0, bool reset = true);                                 // Establece la animación por índice
        void resetAnimation();                                                                      // Reinicia la animación actual
        void setAnimationSpeed(size_t value);                                                       // Establece la velocidad de la animación
        auto getAnimationSpeed() const -> size_t { return animations_[current_animation_].speed; }  // Obtiene la velocidad de la animación actual

        // --- Consultas ---
        auto animationIsCompleted() -> bool;            // Comprueba si la animación ha terminado
        auto getIndex(const std::string& name) -> int;  // Obtiene el índice de una animación por nombre

    protected:
        // --- Datos de animación ---
        std::vector<Animation> animations_;  // Vector de animaciones disponibles
        int current_animation_ = 0;          // Índice de la animación activa

        // --- Mapa para búsqueda rápida de animaciones por nombre ---
        std::unordered_map<std::string, int> animation_indices_;

        // --- Métodos internos ---
        void animate();                                                                                                               // Calcula el frame correspondiente a la animación
        void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source);                                                        // Carga la animación desde un vector de cadenas
        void processConfigLine(const std::string& line, AnimationConfig& config);                                                     // Procesa una línea de configuración
        void updateFrameCalculations(AnimationConfig& config);                                                                        // Actualiza los cálculos basados en las dimensiones del frame
        auto processAnimationBlock(const AnimationsFileBuffer& source, size_t start_index, const AnimationConfig& config) -> size_t;  // Procesa un bloque completo de animación
        static void processAnimationParameter(const std::string& line, Animation& animation, const AnimationConfig& config);          // Procesa un parámetro individual de animación
        static void parseFramesParameter(const std::string& frames_str, Animation& animation, const AnimationConfig& config);         // Parsea el parámetro de frames (lista separada por comas)
};