/*
	Author: Marco Costalba (C) 2007

 This is a test program to showcase a simple but powerful
 object factory.

 This version requires boost::fusion library

*/
#include "simple_factory.hpp"

#include <iostream>
#include <string>
using std::cout;
using std::endl;

using namespace simple_factory;

/* Some stuff we will use in this demonstration */
class ParentClass {};
class BaseClass { public:
	BaseClass() {}
	BaseClass(ParentClass*) {}
	virtual ~BaseClass() {}
};
class DerivedClass : public BaseClass { public:
	DerivedClass() {}
	DerivedClass(int) {}
	DerivedClass(int, double, std::string) {}
	DerivedClass(ParentClass*) : customCreatorWasHere(false) {}
	bool customCreatorWasHere;
};

DerivedClass* customCreator(ParentClass* p) {
	DerivedClass* d = new DerivedClass(p);
	d->customCreatorWasHere = true;
	return d;
}

class OtherClass { public:
	OtherClass() {}
	OtherClass(int& cnt) { cnt++; }
	DerivedClass* operator()(ParentClass* p) { return customCreator(p); }
};

/* First (and only) thing to do is to 'register' a class with a
 * factory using the function (actually a static method)
 *
 *      bool Factory<Base>::register_class<Derived*(Arg1, ..., ArgN)>(name)
 *
 * where 'Derived' is the class to register, and 'Base' is the base
 * class of our hierarchy. We can have more hierarchies at the
 * same time. More then one class constructor can be registered, use
 * types arg1, arg2,..argN to pass the signature of the requested ctor.
 * If not given default ctor will be assumed.
 *
 * Finally parameter 'name' is a class identifier, i.e. a string with the
 * name we will use to query the factory for an object of 'Derived' type.
 * Normally it's the class name.
 *
 * We can register a class with a factory anywhere in a program,
 * both in file scope (will be registered at startup with static
 * data), as we see here, or in a function body (will be registered
 * when the function is called).
 *
 * the following two definitions will teach 'BaseClass' factory
 * how to create a 'BaseClass' and a 'DerivedClass'
 */
static bool s1 = Factory<BaseClass>::register_class<BaseClass*()>("Base");
static bool s2 = Factory<BaseClass>::register_class<DerivedClass*()>("Derived");

/* Of course you can (better) use an anonymous namespace in
 * implemantation files and avoid the 'static' qualifier
 */
namespace {
// re-registering the same class with the same name (correctly) fails!
bool s3 = Factory<BaseClass>::register_class<BaseClass* ()>("Base"); // s3 == false

}

int main(int argc, char* argv[]) {

	// just after a class has been registered we can start getting objects
	BaseClass* b = Factory<BaseClass>::object("Base");

	// that's all, check if all was ok
	if (dynamic_cast<BaseClass*>(b)) cout << "\nIt's a BaseClass object\n";

	// we can have objects from many hierarchies,
	// just need to registering with proper factory...
	Factory<OtherClass>::register_class<OtherClass* ()>("Other");

	// ...and asking the object
	OtherClass* o = Factory<OtherClass>::object("Other");
	if (dynamic_cast<OtherClass*>(o)) cout << "It's an OtherClass object\n";

	// we can use any string expression to pass the class name,
	// the string value can be also unknown at compile time, well
	// that's the real meaning of using all this factory stuff.
	// As example we could load the class name from a file
	std::string myName("Der");
	b = Factory<BaseClass>::object(myName + "ived");
	if (dynamic_cast<DerivedClass*>(b)) cout << "It's a DerivedClass object\n";

	// we can use any alias when registering a class with a
	// factory, in this case we call 'myClass' a BaseClass class
	Factory<BaseClass>::register_class<BaseClass*(ParentClass*)>("myClass");

	// the second template argument 'ParentClass*' is the type of
	// the argument we will pass to our object constructor. It's
	// optional, if not given, as seen until now, the default
	// object constructor will be called
	//
	// after registration we can create a 'myClass' object
	// anywhere in the program. Here we also pass an argument
	// to our object constructor, in this case a pointer to a
	// ParentClass object called 'parent'
	ParentClass parent;
	b = Factory<BaseClass>::object("myClass", &parent);
	if (dynamic_cast<BaseClass*>(b)) cout << "It's a BaseClass object\n";

	// we can use a custom create function instead of the default one
	Factory<BaseClass>::
	register_class<DerivedClass*(ParentClass*)>("Derived", &customCreator);

	// now any new DerivedClass called with 'ParentClass*' argument will be
	// created with our customCreator() function. See how we pass the
	// mandatory argument 'parent' to feed customCreator(), failing to do
	// is safely detected at runtime
	b = Factory<BaseClass>::object("Derived", &parent);
	DerivedClass* d = dynamic_cast<DerivedClass*>(b);
	if (d->customCreatorWasHere) cout << "customCreator() was used\n";

	// finally we can unregister a class from a factory
	Factory<OtherClass>::unregister("Other");
	o = Factory<OtherClass>::object("Other");
	if (!o)
		cout << "Class 'Other' unregistered\n";


	// Ok, and now lets start with the fancy stuff!

	// as starter we try to pass the wrong type to 'DerivedClass'
	// constructor, we pass a 'OtherClass*' instead of a
	// 'ParentClass*' and see what's happens...
	OtherClass argumentWithWrongType;
	b = Factory<BaseClass>::object("Derived", &argumentWithWrongType);

	// ...it is detected at runtime!
	if (!b)
		cout << "Wrong argument type detected!\n";

	// we want the factory to create a DerivedClass object
	// passing an integer as constructor argument
	Factory<BaseClass>::register_class<DerivedClass*(int)>("Derived");
	b = Factory<BaseClass>::object("Derived", 7);
	if (dynamic_cast<DerivedClass*>(b)) cout << "c'tor DerivedClass(7) called\n";

	// until now nothing new, but try to ask another 'Derived' object
	// always with one argument, this time with type 'ParentClass*'
	b = Factory<BaseClass>::object("Derived", &parent);
	if (dynamic_cast<DerivedClass*>(b)) cout << "c'tor DerivedClass(parent) called\n";

	// also this works! so we have here a ctor overload for "Derived"
	// we can register as many overloads as we need, they all will be
	// nicely handled (and type checked) by our factory
	//
	// each overload can be also individually unregistered,
	// as example now we remove DerivedClass(int) ctor
	Factory<BaseClass>::unregister<BaseClass*(int)>("Derived");
	b = Factory<BaseClass>::object("Derived", 7);
	if (!b)
		cout << "Derived(int) ctor unregistered but...";

	// only ctor Derived(int) has been removed
	b = Factory<BaseClass>::object("Derived", &parent);
	if (dynamic_cast<DerivedClass*>(b))
		cout << "DerivedClass(parent) still available\n";

	// we can also pass an argument by reference as example to
	// be modified by object ctor. As usual first register...
	Factory<OtherClass>::register_class<OtherClass*(int)>("Other");
	int objCnt = 6;

	// ...then ask for the object, in this case explicitly stating
	// argument is passed by reference with 'object<int&>'
	o = Factory<OtherClass>::object<int&>("Other", objCnt);
	cout << "Integer 'objCnt' incremented to " << objCnt << endl;

	// we can also use a functor object instead of a function
	// as our custom creator, as example an 'OtherClass' object,
	// that is also a functor. As always first unregister current one...
	Factory<BaseClass>::unregister<DerivedClass*(ParentClass*)>("Derived");

	// ...then register our new creator and get an object
	Factory<BaseClass>::register_class<DerivedClass*(ParentClass*)>("Derived", o);
	d = dynamic_cast<DerivedClass*>(Factory<BaseClass>::object("Derived", &parent));
	if (d->customCreatorWasHere) cout << "custom functor object was used\n";

	// delete and remove some compiler warning noise
	delete b; delete o;
	s1 = s2 = s3 = false;

	/* So the bottom line is:

	   - Support for many class hierarchies at the same time

	   - Generic constructor argument type or default c'tor

	   - Type safe constructor overloading

	   - Support for user supplied 'object create' functions

	   - Dynamic class registering/unregistering

	   - And finally the most important: 100% type safe also at runtime!


	   Known issues:

	   - Currently key type it's a std::string. It could be easily converted
	     to a generic type but for now is better to keep the code simple

	   - If c'tor type is a pointer, Factory::object() fails to
	     detect the correct type when a NULL value is passed:

	     ParentClass parent, *p = NULL;
	     b = Factory<BaseClass>::object("Derived", &parent);            // OK
	     b = Factory<BaseClass>::object("Derived", p);                  // OK
	     b = Factory<BaseClass>::object<ParentClass*>("Derived", NULL); // OK
	     b = Factory<BaseClass>::object("Derived", NULL); // Compile time warning!

	   - Currently when calling

	         Factory<>::object(name, ctor_arg);

	     the argument 'ctor_arg' is passed by value so this leads to one extra copy
	     in case argument is of a non-POD type. In case argument should be passed
	     by reference this can also have the side effect that 'ctor_arg' is not
	     updated by object constructor that instead updates a copy of 'ctor_arg'.

	     workaround is to explicitly indicate we want to pass 'ctor_arg' by
	     reference using the object<> template parameter:

	     b = Factory<BaseClass>::object<Parent&>("Derived", p); // passing non-POD types
	     b = Factory<BaseClass>::object<int&>("Derived", objCnt); // passing references
	*/
	return 0;
}