/* === S Y N F I G ========================================================= */
/*!	\file type.cpp
**	\brief Template Header
**
**	$Id$
**
**	\legal
**	......... ... 2014 Ivan Mahonin
**
**	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
*/
/* ========================================================================= */

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

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "type.h"

#include "general.h"
#include <synfig/localization.h>

#endif

using namespace synfig;
using namespace etl;

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

/* === G L O B A L S ======================================================= */

Type::OperationBookBase *Type::OperationBookBase::first = NULL;
Type::OperationBookBase *Type::OperationBookBase::last = NULL;

Type *Type::first = NULL;
Type *Type::last = NULL;
TypeId Type::last_identifier = 0;
Type::StaticData Type::staticData;


Type::OperationBookBase::OperationBookBase():
	previous(last), next(NULL), initialized(false)
{
	(previous == NULL ? first : previous->next) = last = this;
}

Type::OperationBookBase::~OperationBookBase()
{
	(previous == NULL ? first : previous->next) = next;
	(next     == NULL ? last  : next->previous) = previous;
}

void Type::OperationBookBase::remove_type_from_all_books(TypeId identifier)
{
	for(OperationBookBase *book = first; book != NULL; book = book->next)
		book->remove_type(identifier);
}

void Type::OperationBookBase::initialize()
{
	if (initialized) return;
	std::string type_name(typeid(*this).name());
	for(OperationBookBase *book = first; book != NULL && book != this; book = book->next)
	{
		book->initialize();
		if (typeid(*book).name() == type_name)
		{
			set_alias(book);
			break;
		}
	}
	initialized = true;
}

void Type::OperationBookBase::deinitialize()
{
	if (!initialized) return;
	set_alias(NULL);
}

void Type::OperationBookBase::initialize_all()
{
	for(OperationBookBase *book = first; book != NULL; book = book->next)
		book->initialize();
}

void Type::OperationBookBase::deinitialize_all() {
	for(OperationBookBase *book = first; book != NULL; book = book->next)
		book->deinitialize();
}


Type::Type(TypeId):
	previous(last), next(NULL),
	initialized(false),
	private_identifier(NIL),
	clone_prev(NULL),
	clone_next(NULL),
	identifier(private_identifier),
	description(private_description)
{
	(previous == NULL ? first : previous->next) = last = this;
}

Type::Type():
	previous(last), next(NULL),
	initialized(false),
	private_identifier(++last_identifier),
	clone_prev(NULL),
	clone_next(NULL),
	identifier(private_identifier),
	description(private_description)
{
	assert(last_identifier != NIL);
	(previous == NULL ? first : previous->next) = last = this;
}

Type::~Type()
{
	deinitialize();
	(previous == NULL ? first : previous->next) = next;
	(next     == NULL ? last  : next->previous) = previous;
}

void Type::register_type()
{
	// register id
	if (staticData.typesById.size() <= identifier) staticData.typesById.resize(identifier + 1, NULL);
	assert(staticData.typesById[identifier] == NULL);
	staticData.typesById[identifier] = this;

	// register names
	staticData.typesByName[description.name] = this;
	for(std::vector<String>::const_iterator i = description.aliases.begin(); i != description.aliases.end(); ++i)
	{
		assert(!staticData.typesByName.count(*i) || staticData.typesByName[*i] == this);
		staticData.typesByName[*i] = this;
	}
}

void Type::unregister_type()
{
	// unregister operations
	OperationBookBase::remove_type_from_all_books(identifier);

	// unregister id
	if (staticData.typesById.size() > identifier) staticData.typesById[identifier] = NULL;

	// unregister names
	staticData.typesByName.erase(description.name);
	for(std::vector<String>::const_iterator i = description.aliases.begin(); i != description.aliases.end(); ++i)
		staticData.typesByName.erase(*i);
}

String Type::local_n(const char *x)
{
	return N_(x);
}

void Type::initialize()
{
	if (initialized) return;
	if (clone_prev != NULL) { clone_prev->initialize(); return; }

	// find clones
	if (clone_next == NULL) {
		std::string type_name(typeid(*this).name());
		for(Type *i = first; i != NULL; i = i->next)
		{
			if (i != this && typeid(*i).name() == type_name)
			{
				clone_prev = i;
				clone_next = clone_prev->clone_next;
				if (clone_prev != NULL) clone_prev->clone_next = this;
				if (clone_next != NULL) clone_next->clone_prev = this;
				i->initialize();
				private_identifier = clone_prev->identifier;
				private_description = clone_prev->private_description;
				return;
			}
		}
	}

	initialize_vfunc(private_description);
	register_type();
	initialized = true;
}

void Type::deinitialize()
{
	if (!initialized && clone_prev == NULL) return;

	Type *initialize_next = NULL;
	if (clone_prev == NULL)
	{
		unregister_type();
		deinitialize_vfunc(private_description);
		initialized = false;
		initialize_next = clone_next;
	}

	// unassign clone
	if (clone_prev != NULL) clone_prev->clone_next = clone_next;
	if (clone_next != NULL) clone_next->clone_prev = clone_prev;
	clone_prev = NULL;
	clone_next = NULL;

	if (initialize_next != NULL) initialize_next->initialize();
}

void Type::initialize_all()
{
	OperationBookBase::initialize_all();
	for(Type *type = first; type != NULL; type = type->next)
		type->initialize();
}

void Type::deinitialize_all()
{
	for(Type *type = first; type != NULL; type = type->next)
		type->deinitialize();
	OperationBookBase::deinitialize_all();
}


namespace synfig {
namespace types_namespace {
	class TypeNil: public Type
	{
	protected:
		TypeNil(): Type(NIL) { }

		static String to_string(ConstInternalPointer) { return "Nil"; }

		virtual void initialize_vfunc(Description &description)
		{
			Type::initialize_vfunc(description);
			description.name = "nil";
			description.local_name = local_n("nil");
			description.aliases.push_back("null");
			register_to_string(to_string);
		}
	public:
		static TypeNil instance;
	};

	TypeNil TypeNil::instance;
}

Type &type_nil = types_namespace::TypeNil::instance;
}

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */
