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

struct Base
{ 
	static std::shared_ptr<Base> Create(int params)
	{
		std::cout << "Creating Base with params = " << params << "\n";
		return std::make_shared<Base>();
	}
};

namespace Utility
{
	struct FactoryToolBase
	{
		virtual std::shared_ptr<Base> Create(int params) = 0;
	};
	
	template<typename T> struct FactoryTool: FactoryToolBase
	{
		std::shared_ptr<Base> Create(int params)
		{
			return T::Create(params);
		}
	};
	
	struct Factory
	{
		std::map<std::string, std::unique_ptr<FactoryToolBase>> tools;
		
		template<typename T> Factory& Register(std::string name)
		{
			tools[name] = std::unique_ptr<FactoryToolBase>(new FactoryTool<T>);
			return *this;
		}
		
		std::shared_ptr<Base> Create(std::string name, int params)
		{
			return tools[name]->Create(params);
		}
	};
}

namespace Trivial
{
	struct Derived: Base
	{
		static std::shared_ptr<Base> Create(int params)
		{
			if (params == 0)
			{
				return Base::Create(params);
			}
			else
			{
				std::cout << "Creating Derived with params = " << params << std::endl;
				return std::make_shared<Derived>();
			}
		}
	};
}

namespace Complex
{
	template<typename T> struct Inherited
	{
		static std::shared_ptr<Base> Create(int params)
		{
			return T::Create(params);
		}
	};
	
	struct Derived: Base, Inherited<Base>
	{
		static std::shared_ptr<Base> Create(int params)
		{
			if (params == 0)
			{
				return Inherited::Create(params);
			}
			else
			{
				std::cout << "Creating Derived with params = " << params << std::endl;
				return std::make_shared<Derived>();
			}
		}
	};
	
	struct EvenMoreDerived: Base, Inherited<Derived>
	{
		static std::shared_ptr<Base> Create(int params)
		{
			if (params < 1000)
			{
				return Inherited::Create(params);
			}
			else
			{
				std::cout << "Creating EvenMoreDerived with params = " << params << std::endl;
			}
		}
	};
}

int main()
{
	std::cout << "Trivial static inheritance" << std::endl;
	Utility::Factory trivial;
	trivial
		.Register<Base>("Base")
		.Register<Trivial::Derived>("Derived");
	trivial.Create("Base", 123);    // -> Base::Create(123)
	trivial.Create("Derived", 0);   // -> Base::Create(0)
	trivial.Create("Derived", 123); // -> Derived::Create(123)
	
	std::cout << std::endl << "Complex static inheritance" << std::endl;
	Utility::Factory complex;
	complex
		.Register<Base>("Base")
		.Register<Complex::Derived>("Derived")
		.Register<Complex::EvenMoreDerived>("EvenMoreDerived");
	complex.Create("Base", 123);             // -> Base::Create(123)
	complex.Create("Derived", 0);            // -> Base::Create(0)
	complex.Create("Derived", 123);          // -> Derived::Create(123)
	complex.Create("EvenMoreDerived", 0);    // -> Base::Create(0)
	complex.Create("EvenMoreDerived", 123);  // -> Derived::Create(123)
	complex.Create("EvenMoreDerived", 1234); // -> EvenMorederived::Create(1234)
	
	return 0;
}