#include <string>
#include <memory>
#include <map>
#include <iostream>

using namespace std;

class Tank;
using UTank = unique_ptr<Tank>;

class Tank {
public:
    enum Id { LEOPARD_1, LEOPARD_2, LEOPARD_2E, ABRAMS_M1, ABRAMS_M1A1 };
    Tank(const string& version) : version_(version) {}
    virtual ~Tank() {}

    const string& getVersion() const { return version_; }

    virtual UTank clone() const = 0;
private:
    string version_;
};


class Leopard : public Tank {
public:
    Leopard(const string& version) : Tank(version) {}
    UTank clone() const { return make_unique<Leopard>(getVersion()); }
};

class Abrams : public Tank {
public:
    Abrams(const string& version) : Tank(version) {}
    UTank clone() const { return make_unique<Abrams>(getVersion()); }
};

class TankFactory { //fabryka prototypow
public:
    class UnknownIdError : public std::exception {};


    static TankFactory& getInstance() {
        static TankFactory instance;
        return instance;
    }

    UTank create(Tank::Id id) const {
        auto found = prototypes_.find(id);

        if(found != prototypes_.end() ) {
            return found->second->clone();
        }
        throw UnknownIdError();
    }
private:
    TankFactory() {
        prototypes_.insert( make_pair(Tank::LEOPARD_1, make_unique<Leopard>("1")));
        prototypes_.insert( make_pair(Tank::LEOPARD_2, make_unique<Leopard>("2")));
        prototypes_.insert( make_pair(Tank::LEOPARD_2E, make_unique<Leopard>("2E")));
        prototypes_.insert( make_pair(Tank::ABRAMS_M1, make_unique<Abrams>("M1")));
        prototypes_.insert( make_pair(Tank::ABRAMS_M1A1, make_unique<Abrams>("M1A1")));
    }
    map<Tank::Id, UTank> prototypes_;
};

int main() {
    UTank tank1 = TankFactory::getInstance().create(Tank::Id::LEOPARD_1);
    cout << tank1->getVersion() << endl;
    return 0;
}
