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

using std::vector;

class GameDatabase {
 public:
  GameDatabase() = default;

  void Insert(ObjectId id, string name, size_t x, size_t y) {
    GameObject g = { id, name, x, y };

    if (ids.find(id) != ids.end())
      Remove(id);

    ids.insert({ id, g });
    auto item = &ids.at(id);
    positions[{ x, y }].insert(item);
    names[name].insert(item);
  }

  void Remove(ObjectId id) {
    auto item = ids.find(id);
    if (item != ids.end()) {
      GameObject *g = &((*item).second);

      (*positions.find({ g->x, g->y })).second.erase(g);
      (*names.find(g->name)).second.erase(g);
      ids.erase(item);
    }
  }

  vector<GameObject> DataByName(string name) const {
    vector<GameObject> v;

    auto it = names.find(name);
    if (it == names.end())
      return v;

    for (auto b = (*it).second.begin(); b != (*it).second.end(); b++)
      v.push_back(**b);

    return v;
  }

  vector<GameObject> DataByPosition(size_t x, size_t y) const {
    vector<GameObject> v;

    auto it = positions.find({ x, y });
    if (it == positions.end())
      return v;

    for (auto b = (*it).second.begin(); b != (*it).second.end(); b++)
      v.push_back(**b);

    return v;
  }

  vector<GameObject> Data() const {
    vector<GameObject> v;

    for (auto it = ids.begin(); it != ids.end(); it++)
      v.push_back((*it).second);

    return v;
  }

 private:
  std::map<ObjectId, GameObject, std::greater<ObjectId>> ids;
  std::map<
    std::pair<size_t, size_t>,
    std::set<
      GameObject*,
      DereferenceCompare<GameObject, std::greater>
    >
  > positions;
  std::unordered_map<
    string, std::set<
      GameObject*,
      DereferenceCompare<GameObject, std::greater>
    >
  > names;
};