#include <unordered_map>
#include <string>
#include <set>
#include <utility>
#include <map>
#include <functional>
#include <vector>

bool operator>(const GameObject &a, const GameObject &b) {
  return a.id > b.id;
}

template<class Tp, template<class> class Compare>
class DereferenceCompare {
  Compare<Tp> comp;

 public:
  bool operator()(const Tp *const a, const Tp *const b) const {
    return comp(*a, *b);
  }
};

class GameDatabase {
 public:
  GameDatabase() = default;

  void Insert(ObjectId id, string name, size_t x, size_t y) {
    fastId.erase(id);
    fastId.insert({id, GameObject{id, name, x, y}});
    fastPos[pair<size_t, size_t>(x, y)].insert(&fastId[id]);
    fastName[name].insert(&fastId[id]);
  }
  void Remove(ObjectId id) {
    fastId.erase(id);
  }
  vector<GameObject> DataByName(string name) const {
    vector<GameObject> out;
    if (fastName.count(name)) {
      for (auto it : fastName.find(name)->second) {
        out.push_back(*it);
      }
    }
    return out;
  }
  vector<GameObject> DataByPosition(size_t x, size_t y) const {
    vector<GameObject> out;
    if (fastPos.count(pair<size_t, size_t>(x, y))) {
      for (auto it : fastPos.find(pair<size_t, size_t>(x, y))->second) {
        out.push_back(*it);
      }
    }
    return out;
  }
  vector<GameObject> Data() const {
    vector<GameObject> out;
    for (auto it : fastId) {
      out.push_back(it.second);
    }
    return out;
  }

 private:
  map<ObjectId, GameObject, std::greater<ObjectId>> fastId;

  map<std::pair<size_t, size_t>, std::set<GameObject *,
  DereferenceCompare<GameObject, std::greater>>> fastPos;

  unordered_map<string, std::set<GameObject *,
  DereferenceCompare<GameObject, std::greater>>> fastName;
};