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

template <class Base, typename CreatorParam>
class AbstractCreator {
public:
	virtual ~AbstractCreator() {}
    virtual std::shared_ptr<Base> create(const CreatorParam &id) const = 0;
};

template <class ObjectClass, class Base, typename CreatorParam>
class Creator : public AbstractCreator<Base, CreatorParam> {
public:
    Creator() {}
	virtual ~Creator() {}
    std::shared_ptr<Base> create(const CreatorParam &params) const {
        return std::static_pointer_cast<Base>(std::make_shared<ObjectClass>(params));
	}
};

template <class Base, class IdType>
class ObjectFactory {
protected:
	typedef AbstractCreator<Base, IdType> AbstractFactory;
	typedef std::map<IdType, AbstractFactory*> FactoryMap;
	FactoryMap factory_;

public:
	ObjectFactory() {}
	virtual ~ObjectFactory() {
		for (auto& item : factory_)
			delete item.second;
	}

    template <class C, typename CreatorParam>
    void addClass(const IdType &id) {
        registerClass(id, new Creator<C, Base, CreatorParam>());
	}

    template <typename CreatorParam>
    std::shared_ptr<Base> createObject(const IdType &idClass, const CreatorParam &parameters) {
        return factory_[idClass]->create(parameters);
	}

protected:
	void registerClass(const IdType &id, AbstractFactory *ConcreteFabric) {
		typename FactoryMap::iterator it = factory_.find(id);
		if (it == factory_.end())
			factory_[id] = ConcreteFabric;
		else
			delete ConcreteFabric;
	}
};
