#include <string>
#include <vector>
#include <map>
#include <set>

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

class GameDatabase {
 public:
  GameDatabase() = default;
  void Insert(ObjectId id, const std::string &name, size_t x, size_t y) {
    GameObject tmp;
    tmp.id = id;
    tmp.name = name;
    tmp.x = x;
    tmp.y = y;
    if (this->id_objects_.find(id) != this->id_objects_.end()) {
      this->Remove(id);
    }
    this->id_objects_[id] = tmp;
    this->pos_objects_[std::make_pair(x, y)].insert(id);
    this->str_objects_[name].insert(id);
  }
  void Remove(ObjectId id) {
    auto it = this->id_objects_.find(id);
    if (it != this->id_objects_.end()) {
      GameObject tmp = this->id_objects_[id];
      this->str_objects_[tmp.name].erase(id);
      this->pos_objects_[std::make_pair(tmp.x, tmp.y)].erase(id);
      this->id_objects_.erase(it);
    }
  }
  [[nodiscard]] std::pair<GameObject, bool> DataById(ObjectId id) const {
    auto tmp = this->id_objects_.find(id);
    return std::make_pair(tmp->second, tmp != this->id_objects_.end());
  }
  [[nodiscard]] std::vector<GameObject>
      DataByName(const std::string& name) const {
    auto tmp = this->str_objects_.find(name);
    std::vector<GameObject> result;
    if (tmp != this->str_objects_.end()) {
      for (auto it : tmp->second)
        result.push_back(DataById(it).first);
    }
    return result;
  }
  [[nodiscard]] std::vector<GameObject>
      DataByPosition(size_t x, size_t y) const {
    auto tmp = this->pos_objects_.find(std::make_pair(x, y));
    std::vector<GameObject> result;
    if (tmp != this->pos_objects_.end()) {
      for (auto it : tmp->second)
        result.push_back(DataById(it).first);
    }
    return result;
  }
  [[nodiscard]] std::vector<GameObject> Data() const {
    std::vector<GameObject> result;
    for (const auto &it : this->id_objects_) {
      result.push_back(it.second);
    }
    return result;
  }

 private:
  std::map<
      ObjectId,
      GameObject,
      std::greater<>
  > id_objects_;
  std::map<
      std::pair<size_t, size_t>,
      std::set<ObjectId, std::greater<>>
  > pos_objects_;
  std::map<
      std::string,
      std::set<ObjectId, std::greater<>>
  > str_objects_;
};