#pragma once

#include <SDL3/SDL.h>

#include <cstdint>  // Para uint8_t
#include <vector>   // Para vector

#include "utils/utils.hpp"  // Para LineHorizontal, LineDiagonal, LineVertical

/**
 * @brief Mapa de colisiones de una habitación
 *
 * Responsabilidades:
 * - Almacenar la geometría de colisión (superficies, rampas, conveyor belts)
 * - Generar geometría a partir del tilemap
 * - Proporcionar queries de colisión para Player y otras entidades
 * - Determinar tipo de tile en posiciones específicas
 */
class CollisionMap {
   public:
    // Enumeración de tipos de tile (para colisiones)
    enum class Tile : std::uint8_t {
        EMPTY,
        WALL,
        PASSABLE,
        SLOPE_L,
        SLOPE_R,
        KILL,
        ANIMATED
    };

    /**
     * @brief Constructor
     * @param tile_map Vector con índices de tiles de la habitación
     * @param tile_set_width Ancho del tileset en tiles (para calcular tipo de tile)
     * @param conveyor_belt_direction Dirección de las cintas transportadoras (-1, 0, +1)
     */
    CollisionMap(std::vector<int> tile_map, int tile_set_width, int conveyor_belt_direction);
    ~CollisionMap() = default;

    // Prohibir copia y movimiento
    CollisionMap(const CollisionMap&) = delete;
    auto operator=(const CollisionMap&) -> CollisionMap& = delete;
    CollisionMap(CollisionMap&&) = delete;
    auto operator=(CollisionMap&&) -> CollisionMap& = delete;

    // --- Queries de tipo de tile ---
    [[nodiscard]] auto getTile(SDL_FPoint point) const -> Tile;  // Devuelve el tipo de tile en un punto (pixel)
    [[nodiscard]] auto getTile(int index) const -> Tile;         // Devuelve el tipo de tile en un índice del tilemap

    // --- Queries de colisión con superficies ---
    auto checkRightSurfaces(const SDL_FRect& rect) -> int;   // Colisión con paredes derechas (retorna X)
    auto checkLeftSurfaces(const SDL_FRect& rect) -> int;    // Colisión con paredes izquierdas (retorna X)
    auto checkTopSurfaces(const SDL_FRect& rect) -> int;     // Colisión con techos (retorna Y)
    auto checkTopSurfaces(const SDL_FPoint& p) -> bool;      // Colisión punto con techos
    auto checkBottomSurfaces(const SDL_FRect& rect) -> int;  // Colisión con suelos (retorna Y)

    // --- Queries de colisión con superficies automáticas (conveyor belts) ---
    auto checkAutoSurfaces(const SDL_FRect& rect) -> int;  // Colisión con conveyor belts (retorna Y)
    auto checkConveyorBelts(const SDL_FPoint& p) -> bool;  // Colisión punto con conveyor belts

    // --- Queries de colisión con rampas ---
    auto checkLeftSlopes(const LineVertical& line) -> int;                                 // Colisión línea con rampas izquierdas (retorna Y)
    auto checkLeftSlopes(const SDL_FPoint& p) -> bool;                                     // Colisión punto con rampas izquierdas
    auto checkRightSlopes(const LineVertical& line) -> int;                                // Colisión línea con rampas derechas (retorna Y)
    auto checkRightSlopes(const SDL_FPoint& p) -> bool;                                    // Colisión punto con rampas derechas
    [[nodiscard]] auto getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal*;  // Obtiene puntero a slope en un punto

    // --- Métodos estáticos ---
    static auto getTileSize() -> int { return TILE_SIZE; }        // Tamaño del tile en pixels
    static auto getSlopeHeight(SDL_FPoint p, Tile slope) -> int;  // Altura de rampa en un punto

    // --- Getters ---
    [[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; }

    // Getters para debug visualization
    [[nodiscard]] auto getBottomFloors() const -> const std::vector<LineHorizontal>& { return bottom_floors_; }
    [[nodiscard]] auto getTopFloors() const -> const std::vector<LineHorizontal>& { return top_floors_; }
    [[nodiscard]] auto getLeftWalls() const -> const std::vector<LineVertical>& { return left_walls_; }
    [[nodiscard]] auto getRightWalls() const -> const std::vector<LineVertical>& { return right_walls_; }
    [[nodiscard]] auto getLeftSlopes() const -> const std::vector<LineDiagonal>& { return left_slopes_; }
    [[nodiscard]] auto getRightSlopes() const -> const std::vector<LineDiagonal>& { return right_slopes_; }
    [[nodiscard]] auto getConveyorBeltFloors() const -> const std::vector<LineHorizontal>& { return conveyor_belt_floors_; }

   private:
    // --- Constantes ---
    static constexpr int TILE_SIZE = 8;    // Tamaño del tile en pixels
    static constexpr int MAP_WIDTH = 32;   // Ancho del mapa en tiles
    static constexpr int MAP_HEIGHT = 16;  // Alto del mapa en tiles

    // --- Datos de la habitación ---
    std::vector<int> tile_map_;    // Índices de tiles de la habitación
    int tile_set_width_;           // Ancho del tileset en tiles
    int conveyor_belt_direction_;  // Dirección de conveyor belts

    // --- Geometría de colisión ---
    std::vector<LineHorizontal> bottom_floors_;         // Superficies inferiores (suelos)
    std::vector<LineHorizontal> top_floors_;            // Superficies superiores (techos)
    std::vector<LineVertical> left_walls_;              // Paredes izquierdas
    std::vector<LineVertical> right_walls_;             // Paredes derechas
    std::vector<LineDiagonal> left_slopes_;             // Rampas que suben hacia la izquierda
    std::vector<LineDiagonal> right_slopes_;            // Rampas que suben hacia la derecha
    std::vector<LineHorizontal> conveyor_belt_floors_;  // Superficies automáticas (conveyor belts)

    // --- Métodos privados de generación de geometría ---
    void initializeSurfaces();  // Inicializa todas las superficies de colisión

    // Helpers para recopilar tiles
    auto collectBottomTiles() -> std::vector<int>;    // Tiles con superficie inferior
    auto collectTopTiles() -> std::vector<int>;       // Tiles con superficie superior
    auto collectAnimatedTiles() -> std::vector<int>;  // Tiles animados (conveyor belts)

    // Construcción de geometría
    static void buildHorizontalLines(const std::vector<int>& tiles, std::vector<LineHorizontal>& lines, bool is_bottom_surface);
    void setBottomSurfaces();  // Calcula superficies inferiores
    void setTopSurfaces();     // Calcula superficies superiores
    void setLeftSurfaces();    // Calcula paredes izquierdas
    void setRightSurfaces();   // Calcula paredes derechas
    void setLeftSlopes();      // Calcula rampas izquierdas
    void setRightSlopes();     // Calcula rampas derechas
    void setAutoSurfaces();    // Calcula conveyor belts
};
