#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
#include <fstream>
#include "map.hpp"
#include "levelmanager.hpp"

using namespace fmt;
using namespace nlohmann;
using std::string;

json load_test_data(const string &fname) {
  std::ifstream infile(fname);
  return json::parse(infile);
}

TEST_CASE("camera control", "[map]") {
  textures::init();
  components::init();
  LevelManager levels;
  GameLevel level = levels.current();
  auto &map = *level.map;

  Point center = map.center_camera({10,10}, 5, 5);

  map.dump(center.x, center.y);
  REQUIRE(center.x == 8);
  REQUIRE(center.y == 8);

  Point translation = map.map_to_camera({10,10}, center);

  REQUIRE(translation.x == 2);
  REQUIRE(translation.y == 2);
}

TEST_CASE("map placement test", "[map:placement]") {
  textures::init();
  components::init();
  for(int i = 0; i < 20; i++) {
    LevelManager levels;
    GameLevel level = levels.current();
    auto &map = *level.map;

    for(size_t rnum = 0; rnum < map.room_count(); rnum++) {
      Point pos;

      REQUIRE(map.place_entity(rnum, pos));

      REQUIRE(!map.iswall(pos.x, pos.y));
      REQUIRE(map.inmap(pos.x, pos.y));
    }
  }
}

TEST_CASE("dijkstra algo test", "[map]") {
  textures::init();
  json data = load_test_data("./tests/dijkstra.json");

  for(auto &test : data) {
    Matrix expected = test["expected"];
    Matrix input = test["input"];
    Matrix walls = test["walls"];
    Map map(input.size(), input[0].size());
    map.$walls = walls;
    map.$paths.$input = input;

    REQUIRE(map.INVARIANT());

    map.make_paths();
    Matrix &paths = map.paths();

    if(paths != expected) {
      println("ERROR! ------");
      matrix::dump("EXPECTED", expected);
      matrix::dump("RESULT", paths);
    }

    REQUIRE(map.INVARIANT());
    // FIX ME: REQUIRE(paths == expected);
  }
}


TEST_CASE("map image test", "[map-sprite]") {
  components::init();
  textures::init();
  LevelManager levels;
  GameLevel level = levels.current();
  auto &walls = level.map->tiles();
  auto &tile_set = textures::get_map_tile_set();

  sf::Vector2i size{64,64};
  matrix::dump("TILES?", walls);

  std::unordered_map<wchar_t, sf::Vector2i> sprite_coord;

  Config config("./assets/map_tiles.json");
  json& tiles = config.json();

  for(auto tile : tiles) {
    sf::Vector2i coords{tile["x"], tile["y"]};
    sprite_coord.insert_or_assign(tile["display"], coords);
  }

  sf::Vector2u dim{
    (unsigned int)matrix::width(walls) * size.x,
    (unsigned int)matrix::height(walls) * size.y};

  sf::RenderTexture render{dim};
  render.clear({50,50,50,255});

  sf::Texture map_sprites{"./assets/map_tiles.png"};

  for(matrix::each_row it{walls}; it.next();) {
    size_t tid = walls[it.y][it.x];
    wchar_t display = tile_set[tid];
    REQUIRE(sprite_coord.contains(display));

    auto coords = sprite_coord.at(display);
    sf::IntRect square{coords, size};
    sf::Sprite sprite{map_sprites, square};
    sprite.setColor({150,150,150,255});
    sprite.setPosition({float(it.x) * float(size.x), float(it.y) * float(size.y)});
    render.draw(sprite);
  }

  render.display();
  sf::Image out_img = render.getTexture().copyToImage();
  bool worked = out_img.saveToFile("map_test.png");
  REQUIRE(worked);
}
