#ifndef K3DSDK_MODULE_H
#define K3DSDK_MODULE_H

// K-3D
// Copyright (c) 1995-2004, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

/** \file
		\brief Declares module functions
		\author Tim Shead (tshead@k-3d.com)
*/

#include "iapplication_plugin_factory.h"
#include "idocument_plugin_factory.h"
#include "iplugin_factory.h"
#include "iplugin_registry.h"
#include "module_private.h"
#include "uuid.h"

#include <string>
#include <typeinfo>

/////////////////////////////////////////////////////////////////////////////
// K3D_MODULE_EXPORT

#ifdef K3D_PLATFORM_WIN32

	#define K3D_MODULE_EXPORT __declspec(dllexport)

#else // K3D_PLATFORM_WIN32

	#define K3D_MODULE_EXPORT

#endif // !K3D_PLATFORM_WIN32

/////////////////////////////////////////////////////////////////////////////
// K3D_MODULE_START

#ifdef K3D_STATIC_BUILD

	/// Provides boilerplate for the required entry-point into all K-3D plugin modules
	#define K3D_MODULE_START(Namespace, ModuleID, Registry) \
	extern "C" { void Namespace##_register_k3d_module(k3d::uuid& Module) { Module = ModuleID; } } \
	extern "C" { void Namespace##_register_k3d_plugins(k3d::iplugin_registry& Registry) {

#else // K3D_STATIC_BUILD

	/// Provides boilerplate for the required entry-point into all K-3D plugin modules
	#define K3D_MODULE_START(Namespace, ModuleID, Registry) \
	extern "C" { K3D_MODULE_EXPORT void register_k3d_module(k3d::uuid& Module) { Module = ModuleID; } } \
	extern "C" { K3D_MODULE_EXPORT void register_k3d_plugins(k3d::iplugin_registry& Registry) {

#endif // !K3D_STATIC_BUILD

/////////////////////////////////////////////////////////////////////////////
// K3D_MODULE_END

/// Provides boilerplate for the required entry-point into all K-3D plugin modules
#define K3D_MODULE_END } }

/////////////////////////////////////////////////////////////////////////////
// K3D_DECLARE_EXTERNAL_MODULE

/// Used to forward-declare entry points for a statically-linked module
#define K3D_DECLARE_EXTERNAL_MODULE(Namespace) \
extern "C" { void Namespace##_register_k3d_module(k3d::uuid&); } \
extern "C" { void Namespace##_register_k3d_plugins(k3d::iplugin_registry&); }

namespace k3d
{

/// Defines the signature for the register_k3d_module function that must be exported by all plugin modules
typedef void (*register_module_entry_point)(uuid&);
/// Defines the signature for the register_k3d_plugins function that must be exported by all plugin modules
typedef void (*register_plugins_entry_point)(iplugin_registry&);

/////////////////////////////////////////////////////////////////////////////
// interface lists

/// Defines a null interface type for marking the ends of an interface list
class null_interface
{
};

/// Used to create compile-type lists of interfaces (i.e. typelists - see Alexandrescu "Modern C++ Design")
template<typename head_t, typename tail_t = null_interface>
struct interface_list
{
	typedef head_t head;
	typedef tail_t tail;
};

namespace detail
{

/////////////////////////////////////////////////////////////////////////////
// implements_interface

/// Generates code at compile-time that test whether an interface_list contains a specific interface type
template<class interface_type>
struct implements_interface;

/// Generates code at compile-time that test whether an interface_list contains a specific interface type
template<>
struct implements_interface<null_interface>
{
	bool operator()(const std::type_info& InterfaceType)
	{
		return false;
	}
};

/// Generates code at compile-time that test whether an interface_list contains a specific interface type
template<typename head_t, typename tail_t>
struct implements_interface<interface_list<head_t, tail_t> >
{
	bool operator()(const std::type_info& InterfaceType)
	{
		if(typeid(head_t) == InterfaceType)
			return true;

		return implements_interface<tail_t>()(InterfaceType);
	}
};

/// Generates code at compile-time that returns a list of interface types
template<class interface_type>
struct get_interfaces;

/// Generates code at compile-time that returns a list of interface types
template<>
struct get_interfaces<null_interface>
{
	void operator()(iplugin_factory::interfaces_t& Interfaces)
	{
	}
};

/// Generates code at compile-time that returns a list of interface types
template<typename head_t, typename tail_t>
struct get_interfaces<interface_list<head_t, tail_t> >
{
	void operator()(iplugin_factory::interfaces_t& Interfaces)
	{
		Interfaces.push_back(&typeid(head_t));
		get_interfaces<tail_t>()(Interfaces);
	}
};


} // namespace detail

/// Context policy class for use with plugin_factory - creates an "application context" plugin that is not associated with any particular document
template<typename plugin_t>
class application_plugin :
	public iapplication_plugin_factory
{
public:
	iunknown* create_plugin()
	{
		return new plugin_t();
	}
};

/// Context policy class for use with plugin_factory - creates a "document context" plugin that is associated with a specific document
template<typename plugin_t>
class document_plugin :
	public idocument_plugin_factory
{
public:
	inode* create_plugin(k3d::idocument& Document)
	{
		return new plugin_t(Document);
	}
};

/////////////////////////////////////////////////////////////////////////////
// factory

template<typename context_policy, typename interface_list = null_interface>
class plugin_factory :
	public detail::plugin_factory,
	public context_policy
{
public:
	plugin_factory(const uuid& ClassID, const std::string& Name, const std::string& ShortDescription, const std::string& Categories, const quality_t Quality = STABLE) :
		detail::plugin_factory(ClassID, Name, ShortDescription, Categories, Quality)
	{
	}

	bool implements(const std::type_info& InterfaceType)
	{
		return detail::implements_interface<interface_list>()(InterfaceType);
	}

	const interfaces_t interfaces()
	{
		interfaces_t results;
		detail::get_interfaces<interface_list>()(results);
		return results;
	}
};

} // namespace k3d

#endif // !K3DSDK_MODULE_H

