/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__PLUGIN_MANAGER__
#define	__PLUGIN_MANAGER__


		#include "shared/shared_library.h"
		#include "plugin/shared_systems.h"
		#include "container/nlist.h"
		#include "nstring/nstring.h"
		#include "platform.h"


namespace GS {

static const int PluginLoadOk = 0;
static const int PluginLoadError = 1;

static const int PluginMissingClass = 2;
static const int PluginClassError = 3;
static const int PluginMissingVersion = 4;
static const int PluginVersionError = 5;
static const int PluginInterfaceError = 6;

/*!
	@short	Plugin manager
	@author	Emmanuel Julien (ejulien@owloh.com)
*/
template <class T> class PluginManager
{
		typedef	void		SetSharedSystems(SharedSystems &);
		typedef	T			*Factory();
		typedef	const char	*GetPluginClass();
		typedef	uint		GetPluginVersion();

public:

		struct	Plugin
		{
			String	path;
			AutoPtr <ISharedLib> shared_lib;
		};

private:

		AutoList <Plugin *> plugins;

public:

		/// Return a new instance to concrete interface the plugin implements.
		T			*CreatePluginInterface(Plugin *p)
		{
			Factory *p_factory = p ? (Factory *)p->shared_lib->GetFunctionPointer("createPluginInterface") : 0;
			return p_factory ? p_factory() : 0;
		}

		/*
			@short	Load a plugin.

			The plugin is dynamically loaded from the specified path and its
			class and version are checked for compatibility with the host
			interface class and version.

			A return code can be passed to get more information on failure.
		*/
		Plugin		*LoadPlugin(const char *path, int *code = 0)
		{
			AutoPtr <Plugin> p(new Plugin);
			if	(p.IsNull())
				return 0;

			if	(code)
				*code = PluginLoadOk;

			#define	Error(V) { if (code) *code = V; return 0; }

			p->shared_lib = Platform::Get().LoadSharedLibrary(path);
			if	(p->shared_lib.IsNull())
				Error(PluginLoadError);

			GetPluginClass *get_plugin_class = (GetPluginClass *)p->shared_lib->GetFunctionPointer("getPluginClass");
			GetPluginVersion *get_plugin_version = (GetPluginVersion *)p->shared_lib->GetFunctionPointer("getPluginVersion");
			SetSharedSystems *set_shared_systems = (SetSharedSystems *)p->shared_lib->GetFunctionPointer("setSharedSystems");

			if	(!get_plugin_class)
				Error(PluginMissingClass);
			if (!get_plugin_version)
				Error(PluginMissingVersion);
			if	(!set_shared_systems)
				Error(PluginInterfaceError);
			if (String(T::GetPluginClass()) != get_plugin_class())
				Error(PluginClassError);
			if	(T::GetPluginVersion() != get_plugin_version())
				Error(PluginVersionError);

			// Set the shared platform pointer.
			SharedSystems shared;
			set_shared_systems(shared);

			p->path = path;
			plugins.Add(p);

			return p.Detach();
		}
};

} // GS


#endif	// __PLUGIN_MANAGER__
