#pragma once
#include <map>
#include <string>
#include <utility>

class Object {
 public:
     virtual std::string ToString() const = 0;
     virtual ~Object() {}
};

class DeusExCondition : public Object {
 public:
     std::string ID;

     explicit DeusExCondition(const std::string& ID) : ID(ID) {}

     std::string ToString() const {
         return ID;
     }
};

Object* new_list() {
    return new DeusExCondition("list");
}

Object* new_apple() {
    return new DeusExCondition("apple!");
}

Object* new_yet() {
    return new DeusExCondition("yet another identifier");
}

class Factory {
 public:
     std::map<std::string, std::function<Object* ()>> ex_machina;

     Factory() {
         ex_machina.insert(std::make_pair("list", new_list));
         ex_machina.insert(std::make_pair("apple!", new_apple));
         ex_machina.insert(std::make_pair("yet another identifier", new_yet));
     }

     Object* Create(const std::string& ID) {
         auto it = ex_machina.find(ID);

         if (it != ex_machina.end()) {
             return it->second();
         } else {
             return nullptr;
         }
     }

     void Register(const std::string ID, Object*(*deus_ex)()) {
         if (ex_machina.find(ID) == ex_machina.end()) {
             ex_machina.insert(std::make_pair(ID, deus_ex));
         }
     }
};