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

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:
     map<ObjectId, GameObject, greater<ObjectId>> ID;
     map<pair<size_t, size_t>, set<GameObject*,
         DereferenceCompare<GameObject, greater>>> pos;
     unordered_map<string, set<GameObject*,
         DereferenceCompare<GameObject, greater>>> title;

     GameDatabase() = default;

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

         if (ID.find(id) != ID.end()) {
             Remove(id);
         }

         ID.insert({ id, chk });

         auto add = &ID.at(id);

         pos[{x, y}].insert(add);
         title[name].insert(add);
     }

     void Remove(ObjectId id) {
         if (ID.find(id) != ID.end()) {
             GameObject* del = &ID.find(id)->second;

             pos.find({ del->x, del->y })->second.erase(del);
             title.find({ del->name })->second.erase(del);
             ID.erase(ID.find(id));
         }
     }

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

         auto it = title.find(name);

         if (it == title.end()) {
             return res;
         }

         for (auto x : it->second) {
             res.push_back(*x);
         }

         return res;
     }

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

         auto it = pos.find({ x, y });

         if (it == pos.end()) {
             return res;
         }

         for (auto x : it->second) {
             res.push_back(*x);
         }

         return res;
     }

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

         for (auto x : ID) {
             res.push_back(x.second);
         }

         return res;
     }
};