#include "tilemap_renderer.hpp"

#include "core/rendering/screen.hpp"
#include "core/rendering/surface.hpp"
#ifdef _DEBUG
#include "core/resources/resource_cache.hpp"  // Para Resource::Cache (collision.gif)
#include "core/system/debug.hpp"
#endif

TilemapRenderer::TilemapRenderer(std::vector<int> tile_map, int tile_set_width, std::shared_ptr<Surface> tileset_surface, Uint8 bg_color)
    : tile_map_(std::move(tile_map)),
      tile_set_width_(tile_set_width),
      tileset_surface_(std::move(tileset_surface)),
      bg_color_(bg_color),
      map_surface_(std::make_shared<Surface>(PlayArea::WIDTH, PlayArea::HEIGHT)) {}

void TilemapRenderer::initialize(const std::vector<int>& collision_tile_map) {
    fillMapTexture(collision_tile_map);
}

void TilemapRenderer::update(float /*delta_time*/) {
    // De momento no hay tiles animados; el sistema de conveyor belts antiguo se
    // eliminó. Cuando se reimplementen como tipos de tile concretos, irá aquí.
}

void TilemapRenderer::render() {
    SDL_FRect dest = {.x = 0, .y = 0, .w = PlayArea::WIDTH, .h = PlayArea::HEIGHT};
    map_surface_->render(nullptr, &dest);
}

#ifdef _DEBUG
// Redibuja el tilemap (para actualizar modo debug: pinta collision tilemap o tiles normales)
void TilemapRenderer::redrawMap(const std::vector<int>& collision_tile_map) {
    fillMapTexture(collision_tile_map);
}

void TilemapRenderer::setTile(int index, int tile_value) {
    if (index < 0 || index >= static_cast<int>(tile_map_.size())) { return; }

    tile_map_[index] = tile_value;

    int col = index % MAP_WIDTH;
    int row = index / MAP_WIDTH;

    auto previous_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(map_surface_);

    SDL_FRect cell = {.x = static_cast<float>(col * TILE_SIZE), .y = static_cast<float>(row * TILE_SIZE), .w = static_cast<float>(TILE_SIZE), .h = static_cast<float>(TILE_SIZE)};
    map_surface_->fillRect(&cell, bg_color_);

    if (tile_value > -1) {
        SDL_FRect clip = {.x = static_cast<float>((tile_value % tile_set_width_) * TILE_SIZE),
            .y = static_cast<float>((tile_value / tile_set_width_) * TILE_SIZE),
            .w = static_cast<float>(TILE_SIZE),
            .h = static_cast<float>(TILE_SIZE)};
        tileset_surface_->render(col * TILE_SIZE, row * TILE_SIZE, &clip);
    }

    Screen::get()->setRendererSurface(previous_renderer);
}

// Renderiza el collision tilemap superpuesto (modo debug)
static void renderDebugCollisionTilemap(const std::vector<int>& collision_tile_map) {
    auto collision_surface = Resource::Cache::get()->getSurface("collision.gif");
    if (!collision_surface) { return; }

    for (int y = 0; y < Map::HEIGHT; ++y) {
        for (int x = 0; x < Map::WIDTH; ++x) {
            int index = (y * Map::WIDTH) + x;
            if (index >= static_cast<int>(collision_tile_map.size())) { continue; }
            int tile = collision_tile_map[index];
            if (tile <= 0) { continue; }
            SDL_FRect clip = {
                .x = static_cast<float>(tile * Tile::SIZE),
                .y = 0,
                .w = static_cast<float>(Tile::SIZE),
                .h = static_cast<float>(Tile::SIZE)};
            collision_surface->render(x * Tile::SIZE, y * Tile::SIZE, &clip);
        }
    }
}
#endif

// Pinta el mapa estático
void TilemapRenderer::fillMapTexture(const std::vector<int>& collision_tile_map) {
    auto previous_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(map_surface_);
    map_surface_->clear(bg_color_);

    SDL_FRect clip = {.x = 0, .y = 0, .w = TILE_SIZE, .h = TILE_SIZE};
    for (int y = 0; y < MAP_HEIGHT; ++y) {
        for (int x = 0; x < MAP_WIDTH; ++x) {
            const int INDEX = (y * MAP_WIDTH) + x;
            const bool HAS_TILE = tile_map_[INDEX] > -1;
            if (!HAS_TILE) { continue; }

            clip.x = (tile_map_[INDEX] % tile_set_width_) * TILE_SIZE;
            clip.y = (tile_map_[INDEX] / tile_set_width_) * TILE_SIZE;
#ifdef _DEBUG
            if (!Debug::get()->isEnabled()) {
                tileset_surface_->render(x * TILE_SIZE, y * TILE_SIZE, &clip);
            }
#else
            tileset_surface_->render(x * TILE_SIZE, y * TILE_SIZE, &clip);
#endif
        }
    }

#ifdef _DEBUG
    // En modo debug, pintar el collision tilemap en vez de las líneas de colisión antiguas
    if (Debug::get()->isEnabled()) {
        renderDebugCollisionTilemap(collision_tile_map);
    }
#endif
    Screen::get()->setRendererSurface(previous_renderer);
}
