#include "tilemap.hpp"
#include "dbc.hpp"
#include "constants.hpp"

using nlohmann::json;
using components::Tile;

TileMap::TileMap(size_t width, size_t height) :
  $config("./assets/tiles.json"),
  $width(width),
  $height(height),
  $tile_ids(height, matrix::Row(width, SPACE_VALUE)),
  $display(height, TileRow(width, {"", {0,0,0}, {0,0,0}}))
{
}

void TileMap::dump(int show_x, int show_y) {
  for(matrix::each_row it{$tile_ids}; it.next();) {
    const Tile &cell = $display[it.y][it.x];

    if(int(it.x) == show_x && int(it.y) == show_y) {
      fmt::print("{}<", cell.display);
    } else {
      fmt::print("{} ", cell.display);
    }

    if(it.row) fmt::print("\n");
  }
}

void TileMap::set_tile(size_t x, size_t y, string tile_name) {
    std::wstring tile_id = $config.wstring(tile_name, "display");
    json tile_conf = $config[tile_name];
    auto tile = components::convert<Tile>(tile_conf);
    $tile_ids[y][x] = tile_id[0];
    $display[y][x] = tile;
}

void TileMap::load(matrix::Matrix &walls) {
  for(matrix::each_cell it{walls}; it.next();) {
    string tile_name = walls[it.y][it.x] == SPACE_VALUE ? "FLOOR_TILE" : "WALL_PLAIN";
    set_tile(it.x, it.y, tile_name);
  }
}

const Tile &TileMap::at(size_t x, size_t y) {
  return $display[y][x];
}

std::vector<std::string> TileMap::tile_names(bool collision) {
  const auto &json = $config.json();
  std::vector<std::string> keys;

  for(const auto& el : json.items()) {
    const auto &val = el.value();
    if(val["collision"] == collision) {
      keys.push_back(el.key());
    }
  }

  return keys;
}

bool TileMap::INVARIANT() {
  dbc::check(matrix::height($tile_ids) == $height, "$tile_ids has wrong height");
  dbc::check(matrix::width($tile_ids) == $width, "$tile_ids has wrong width");
  return true;
}