/* === S Y N F I G ========================================================= */
/*!	\file valuenode_registry.h
**	\brief Valuenode automatic registry
**
**	\legal
**	Copyright (c) 2016 caryoscelus
**
**	This package 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 package 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.
**	\endlegal
*/
/* ========================================================================= */

/* === S T A R T =========================================================== */

#ifndef __SYNFIG_VALUENODE_REGISTRY_H
#define __SYNFIG_VALUENODE_REGISTRY_H

/* === H E A D E R S ======================================================= */

#include "valuenode.h"

/* === M A C R O S ========================================================= */

/// This macro automatically registers valuenode and defines its
/// get_name & get_local_name functions.
/// NOTE: this relies on valuenode being either in synfig namespace
/// or in 'used' namespace.
/// TODO: move all nodes into synfig::valuenodes namespace
#define REGISTER_VALUENODE(klass, _version, _name, _local_name)				\
	namespace synfig 														\
	{																		\
		class Register_##klass:												\
			public RegisterValueNode<klass, Register_##klass>				\
		{																	\
			public:															\
				static const ReleaseVersion release_version;				\
				static const char* name;									\
				static const char* local_name;								\
		};																	\
																			\
		String																\
		klass::get_name() const												\
		{																	\
			return _name;													\
		}																	\
																			\
		String																\
		klass::get_local_name()const										\
		{																	\
			return _local_name;												\
		}																	\
																			\
		const ReleaseVersion Register_##klass::release_version = _version;	\
		const char* Register_##klass::name = _name;							\
		const char* Register_##klass::local_name = _local_name;				\
	}

/* === C L A S S E S & S T R U C T S ======================================= */

namespace synfig {

class ValueNodeRegistry {
public:
	//! Type that represents a pointer to a ValueNode's constructor
	typedef LinkableValueNode* (*Factory)(const ValueBase&, etl::loose_handle<Canvas>);
	//! Pointer to check_type method
	typedef bool (*CheckType)(Type &type);

	struct BookEntry
	{
		String local_name;
		Factory factory;
		CheckType check_type;
		ReleaseVersion release_version; // which version of synfig introduced this valuenode type
	};

	typedef std::map<String,BookEntry> Book;

public:
	static Book& book();
	static void register_node_type(const String &name, const String &local_name, ReleaseVersion version, Factory factory, CheckType check_type);
	static bool cleanup();
	static String localize_name(const String &local_name);

public:
	//! Creates a Linkable Value Node based on the name and the returned
	//! value type. Returns a valid Handle if both (name and type) match
	static LinkableValueNode::Handle create(const String &name, const ValueBase& x);
	//! Each derived Linkable Value Node has to implement this fucntion and
	//! should return true only if the type matches. \name is the name of
	//! the linked value node and \x is the returned value type
	static bool check_type(const String &name, Type &x);

private:
	static Book* book_;
}; // END of class ValueNodeRegistry

// Automatically register class
// See http://stackoverflow.com/questions/401621/best-way-to-for-c-types-to-self-register-in-a-list
template<class NodeT, class SelfT>
class RegisterValueNode {
private:
	struct do_register {
		do_register()
		{
			ValueNodeRegistry::register_node_type(
				SelfT::name,
				ValueNodeRegistry::localize_name(SelfT::local_name),
				SelfT::release_version,
				reinterpret_cast<ValueNodeRegistry::Factory>(&NodeT::create),
				&NodeT::check_type
			);
		}
	};
	// template magic to make sure do_register is instantiated
	template<do_register&> struct register_ref {};
	static do_register register_obj;
	static register_ref<register_obj> ref_obj;
}; // END of class RegisterValueNode

template<class NodeT, class SelfT>
typename RegisterValueNode<NodeT, SelfT>::do_register RegisterValueNode<NodeT, SelfT>::register_obj;

}; // END of namespace synfig

/* === E N D =============================================================== */

#endif
