#include "tilemap.hpp"
#include "dbc.hpp"
#include "constants.hpp"
#include <iostream>

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, {L'#', {0,0,0}, {0,0,0}}))
{
}

std::string TileMap::to_string(int show_x, int show_y) {
  std::string result;

  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) {
      result += "@";
    } else {
      result += cell.display;
    }

    if(it.row) result += "\n";
  }

  return result;
}

void TileMap::dump(int show_x, int show_y) {
  std::cout << to_string(show_x, show_y) << std::endl;
}

void TileMap::set_tile(size_t x, size_t y, string tile_name) {
    json tile_conf = $config[tile_name];
    auto tile = components::convert<Tile>(tile_conf);
    $tile_ids[y][x] = tile.display;
    $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;
}