#pragma once

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

#include <functional>  // Para std::function
#include <memory>      // Para shared_ptr
#include <utility>
#include <vector>  // Para vector

#include "core/rendering/sprite/sprite.hpp"  // Para Sprite

class Texture;

// --- Enums ---
enum class PathType {  // Tipos de recorrido
    VERTICAL,
    HORIZONTAL,
};

enum class PathCentered {  // Centrado del recorrido
    ON_X,
    ON_Y,
    NONE,
};

// --- Estructuras ---
struct Path {                                       // Define un recorrido para el sprite
    float start_pos;                                // Posición inicial
    float end_pos;                                  // Posición final
    PathType type;                                  // Tipo de movimiento (horizontal/vertical)
    float fixed_pos;                                // Posición fija en el eje contrario
    float duration_s;                               // Duración de la animación en segundos
    float waiting_time_s;                           // Tiempo de espera una vez en el destino
    std::function<double(double)> easing_function;  // Función de easing
    float elapsed_time = 0.0F;                      // Tiempo transcurrido
    float waiting_elapsed = 0.0F;                   // Tiempo de espera transcurrido
    bool on_destination = false;                    // Indica si ha llegado al destino
    bool finished = false;                          // Indica si ha terminado de esperarse

    // Constructor para paths generados
    Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
        : start_pos(start),
          end_pos(end),
          type(path_type),
          fixed_pos(fixed),
          duration_s(duration),
          waiting_time_s(waiting),
          easing_function(std::move(easing)) {}

    // Constructor para paths por puntos (convertido a segundos)
    Path(const std::vector<SDL_FPoint>& spots_init, float waiting_time_s_init);

    // Variables para paths por puntos
    std::vector<SDL_FPoint> spots;  // Solo para paths por puntos
    int counter = 0;                // Solo para paths por puntos
    bool is_point_path = false;     // Indica si es un path por puntos
};

// --- Funciones ---
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)>& easing_function) -> std::vector<SDL_FPoint>;  // Devuelve un vector con los puntos que conforman la ruta

// --- Clase PathSprite: Sprite que sigue uno o varios recorridos ---
class PathSprite : public Sprite {
   public:
    // --- Constructor y destructor ---
    explicit PathSprite(std::shared_ptr<Texture> texture)
        : Sprite(std::move(texture)) {}
    ~PathSprite() override = default;

    // --- Métodos principales ---
    void update(float delta_time);  // Actualiza la posición del sprite según el recorrido (delta_time en segundos)
    void render() override;         // Muestra el sprite por pantalla

    // --- Gestión de recorridos ---
    void addPath(Path path, bool centered = false);                                                                                                                             // Añade un recorrido (Path)
    void addPath(const std::vector<SDL_FPoint>& spots, float waiting_time_s = 0.0F);                                                                                            // Añade un recorrido a partir de puntos
    void addPath(float start, float end, PathType type, float fixed_pos, float duration_s, const std::function<double(double)>& easing_function, float waiting_time_s = 0.0F);  // Añade un recorrido generado

    // --- Estado y control ---
    void enable();                                   // Habilita el objeto
    [[nodiscard]] auto hasFinished() const -> bool;  // Indica si ha terminado todos los recorridos

    // --- Getters ---
    [[nodiscard]] auto getCurrentPath() const -> int { return current_path_; }  // Devuelve el índice del recorrido actual

   private:
    // --- Variables internas ---
    bool enabled_ = false;       // Indica si el objeto está habilitado
    bool has_finished_ = false;  // Indica si el objeto ha finalizado el recorrido
    int current_path_ = 0;       // Recorrido que se está recorriendo actualmente
    std::vector<Path> paths_;    // Caminos a recorrer por el sprite

    // --- Métodos internos ---
    void moveThroughCurrentPath(float delta_time);  // Coloca el sprite en los diferentes puntos del recorrido
    void goToNextPathOrDie();                       // Cambia de recorrido o finaliza

    // --- Métodos auxiliares para addPath ---
    [[nodiscard]] static auto determineCenteringType(const Path& path, bool centered) -> PathCentered;  // Determina el tipo de centrado
    static void centerPathOnX(Path& path, float offset);                                                // Aplica centrado en el eje X
    static void centerPathOnY(Path& path, float offset);                                                // Aplica centrado en el eje Y
};