#pragma once

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

#include <cstddef>        // Para size_t
#include <memory>         // Para shared_ptr
#include <string>         // Para basic_string, string, hash
#include <unordered_map>  // Para unordered_map
#include <utility>        // Para move
#include <vector>         // Para vector

#include "core/rendering/sprite/moving_sprite.hpp"  // for MovingSprite

// Declaración adelantada
class Texture;

// --- Estructuras ---
struct Animation {
    static constexpr float DEFAULT_SPEED = 80.0F;

    std::string name;               // Nombre de la animación
    std::vector<SDL_FRect> frames;  // Frames que componen la animación
    float speed{DEFAULT_SPEED};     // Velocidad de reproducción (ms entre frames)
    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
    float time_accumulator{0.0F};   // Acumulador de tiempo para animaciones time-based
    bool paused{false};             // La animación no avanza

    Animation() = default;
};

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

// --- Tipos ---
using AnimationsFileBuffer = std::vector<std::string>;  // Buffer de animaciones

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

// --- 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(std::move(texture)) {}
    ~AnimatedSprite() override = default;

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

    // --- 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(float value);                                                                                     // Establece la velocidad de la animación
    [[nodiscard]] auto getAnimationSpeed() const -> float { return animations_[current_animation_].speed; }                  // Obtiene la velocidad de la animación actual
    void animtionPause() { animations_[current_animation_].paused = true; }                                                  // Detiene la animación
    void animationResume() { animations_[current_animation_].paused = false; }                                               // Reanuda la animación
    [[nodiscard]] auto getCurrentAnimationFrame() const -> size_t { return animations_[current_animation_].current_frame; }  // Obtiene el numero de frame de la animación actual

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

   protected:
    // --- Variables de estado ---
    std::vector<Animation> animations_;                       // Vector de animaciones disponibles
    std::unordered_map<std::string, int> animation_indices_;  // Mapa para búsqueda rápida por nombre
    int current_animation_ = 0;                               // Índice de la animación activa

    // --- Métodos internos ---
    void animate(float delta_time);                                                                                               // Calcula el frame correspondiente a la animación (time-based)
    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)
    void updateSpriteClip();                                                                                                      // Actualiza el spriteClip con el frame correspondiente
};