#include <type_traits>
#include <exception>

class NonCopyable
{
public:
	NonCopyable(const NonCopyable&) = delete;
	NonCopyable(NonCopyable&&) = delete;
	NonCopyable& operator=(const NonCopyable&) = delete;
	NonCopyable& operator=(NonCopyable&&) = delete;
	~NonCopyable() = default;
protected:
	NonCopyable() = default;
};

template <typename T>
class Subsystem : NonCopyable
{
public:
	template<class ...Args>
	static bool Create(Args&& ...args)
	{
		if( IsValid() )
			throw ("Trying to create an already created module.");

		instance() = new T(std::forward<Args>(args)...);
		isCreate() = true;
		return IsValid();
	}

	template<typename SubClass, class ...Args>
	static bool Create(Args&& ...args)
	{
		if( IsValid() )
			throw ("Trying to create an already created module.");

		instance() = static_cast<T*>(new SubClass(std::forward<Args>(args)...));
		isCreate() = true;
		return IsValid();
	}

	static void Destroy()
	{
		delete instance();
		valid() = false;
		isCreate() = false;
	}

	static T& Get()
	{
		if( !IsValid() )
			throw ("Trying to access a module but it hasn't been created up yet.");

		return *instance();
	}

	static const bool IsValid()
	{
		return valid() && isCreate();
	}

protected:
	Subsystem() = default;
	~Subsystem() = default;

	static T*& instance()
	{
		static T* inst = nullptr;
		return inst;
	}

	static bool& valid()
	{
		static bool inst = false;
		return inst;
	}

	static bool& isCreate()
	{
		static bool inst = false;
		return inst;
	}

	static void setValid(bool bvalid)
	{
		valid() = bvalid;
	}
};

template <typename T>
inline T& GetSubsystem()
{
	return T::Get();
}

class Foo1 : public Subsystem<Foo1>
{
public:
	Foo1(int ni) : i(ni) 
	{
		if (i == 0) 
			return; 
		setValid(true);
	}
	int i;
	
	void Bar(){i = i + 10;}
};

class Foo2 : public Subsystem<Foo2>
{
public:
	Foo2(int nx, int ny) : x(nx), y(ny) {setValid(true);}
	int x,y;
};

class Foo3 : public Subsystem<Foo3>
{
public:
	Foo3(float nf) : f(nf) {setValid(true);}
	float f;
};

int main()
{
	Foo1::Create(10);
	Foo2::Create(10,20);
	Foo3::Create(10.4f);
	
	
	GetSubsystem<Foo1>().Bar();
	
	
	Foo3::Destroy();
	Foo2::Destroy();
	Foo1::Destroy();
	
	return 0;
}