#pragma once
#include <vector>
#include <utility>
#include <string>
#include <random>
#include <algorithm>
#include <fmt/core.h>
#include "point.hpp"
#include "tser.hpp"
#include "lights.hpp"
#include "pathing.hpp"
#include "matrix.hpp"
#include "constants.hpp"
#include "tilemap.hpp"

using lighting::LightSource;

struct Room {
  size_t x = 0;
  size_t y = 0;
  size_t width = 0;
  size_t height = 0;
  Point entry{(size_t)-1, (size_t)-1};
  Point exit{(size_t)-1, (size_t)-1};

  DEFINE_SERIALIZABLE(Room, x, y, width, height);
};

class Map {
public:
  size_t $width;
  size_t $height;
  TileMap $tiles;
  Matrix $walls;
  Pathing $paths;
  std::vector<Room> $rooms;

  Map(size_t width, size_t height);

  Map(Matrix &walls, Pathing &paths);

  // disable copying
  Map(Map &map) = delete;

  Matrix& paths() { return $paths.paths(); }
  TileMap& tiles() { return $tiles; }
  Matrix& input_map() { return $paths.input(); }
  Matrix& walls() { return $walls; }
  size_t width() { return $width; }
  size_t height() { return $height; }
  int distance(Point to) { return $paths.distance(to); }

  Room &room(size_t at) { return $rooms[at]; }
  size_t room_count() { return $rooms.size(); }

  Point place_entity(size_t room_index);
  bool inmap(size_t x, size_t y);
  bool iswall(size_t x, size_t y);
  bool can_move(Point move_to);
  // BUG: this isn't really neighbors anymore. Maybe move? Walk?
  bool neighbors(Point &out, bool random=false);

  void make_paths();
  void set_target(const Point &at, int value=0);
  void clear_target(const Point &at);

  Point map_to_camera(const Point &loc, const Point &cam_orig);
  Point center_camera(const Point &around, size_t view_x, size_t view_y);

  void expand();

  void dump(int show_x=-1, int show_y=-1);
  bool INVARIANT();

  void load_tiles();
};
