#include "collider.hpp"
#include <fmt/core.h>

using namespace fmt;

using DinkyECS::Entity;

void spatial_map::insert(Point pos, Entity ent) {
  table[pos] = ent;
}

void spatial_map::remove(Point pos) {
  table.erase(pos);
}

void spatial_map::move(Point from, Point to, Entity ent) {
  remove(from);
  insert(to, ent);
}

bool spatial_map::occupied(Point at) const {
  return table.contains(at);
}

Entity spatial_map::get(Point at) const {
  return table.at(at);
}

/*
 * Avoid doing work by using the dy,dx and confirming that
 * at.x or at.y is > 0.  If either is 0 then there can't be
 * a neighbor since that's out of bounds.
 */
inline void find_neighbor(const PointEntityMap &table, EntityList &result, Point at, int dy, int dx) {
  // don't bother checking for cells out of bounds
  if((dx < 0 && at.x <= 0) || (dy < 0 && at.y <= 0)) {
    return;
  }

  Point cell = {at.x + dx, at.y + dy};

  auto it = table.find(cell);
  if (it != table.end()) {
    result.insert(result.end(), it->second);
  }
}

FoundEntities spatial_map::neighbors(Point cell, bool diag) const {
  EntityList result;

  // just unroll the loop since we only check four directions
  // this also solves the problem that it was detecting that the cell was automatically included as a "neighbor" but it's not
  find_neighbor(table, result, cell, 0, 1); // north
  find_neighbor(table, result, cell, 0, -1); // south
  find_neighbor(table, result, cell, 1, 0); // east
  find_neighbor(table, result, cell, -1, 0); // west

  if(diag) {
    find_neighbor(table, result, cell, 1, -1); // south east
    find_neighbor(table, result, cell, -1, -1); // south west
    find_neighbor(table, result, cell, 1, 1); // north east
    find_neighbor(table, result, cell, -1, 1); // north west
  }

  return {!result.empty(), result};
}
