/*
	Author: Marco Costalba (C) 2007

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

 There are no external library requirements but a string class
 and a multi-valued associative container. All remaining stuff
 is pure C++. No RTTI or other meta-info feature is required.

 There is only one (small) 'simple_factory.hpp' file to be
 included in implementation files where factory is actually used.

 To compile: g++ -O2 -o factory main.cpp
*/
#include "simple_factory.hpp"

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

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(ParentClass*) : customCreatorWasHere(false) {}
	bool customCreatorWasHere;
};
class OtherClass { public:
	OtherClass() {}
	OtherClass(int& cnt) { cnt++; }
};

/* 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>(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. The third 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

// we can (optionally) provide our custom 'create' function, see later
DerivedClass* customCreator(ParentClass* p) {
	DerivedClass* d = new DerivedClass(p);
	d->customCreatorWasHere = true;
	return d;
}
}

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 always redefine an object creator, as example to set
	// our custom create function instead of the default one. Note
	// how we pass a second template parameter that is the type of
	// the argument required by our customCreator() function.
	// Failing to do so results in a (safe) compile time error
	Factory<BaseClass>::
	register_class<DerivedClass, ParentClass*>("Derived", &customCreator);

	// now any new DerivedClass 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<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
	o = Factory<OtherClass>::object<int&>("Other", objCnt);
	cout << "Integer 'objCnt' incremented to " << objCnt << "\n";


	// delete and remove some compiler warning noise
	delete b; delete d; 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 only default (no argument) and one argument c'tors
	     are supported, tough is easily extendible to many args c'tors

	   - 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

	   - Currently custom creator is a function with a given signature. It
	     would be nice to extend to any callable entity. I still didn't
	     figured out how to do it preserving the current user syntax

	   - 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;
}