/* === S Y N F I G ========================================================= */
/*!	\file token.cpp
**	\brief Token File
**
**	$Id$
**
**	\legal
**	......... ... 2018 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 <synfig/localization.h>
#include <synfig/general.h>

#include "token.h"

#endif

/* === U S I N G =========================================================== */

using namespace std;
using namespace synfig;

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

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

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

bool Token::ready_ = false;
bool Token::root_exists_ = false;
Token *Token::first_ = 0;
Token *Token::last_ = 0;

Token Token::token;

Token::Token(const Token&):
	previous_(), next_(), in_process_(), prepared_() { }

Token::Token():
	previous_(), next_(), in_process_(), prepared_()
{ root_exists_ = true; init(); }

Token::Token(const Handle &parent):
	previous_(),
	next_(),
	in_process_(),
	prepared_()
{
	parents_.insert(parent);
	init();
}

Token::Token(const Set &parents):
	previous_(),
	next_(),
	in_process_(),
	prepared_(),
	parents_(parents)
{ init(); }

void
Token::init()
{
	ready_ = false;
	previous_ = last_;
	(previous_ ? previous_->next_ : first_) = this;
	last_ = this;
}

Token::~Token()
{
	ready_ = false;
	(previous_ ? previous_->next_ : first_) = next_;
	(next_     ? next_->previous_ : last_ ) = previous_;

	// remove all references
	for(Token* token = first_; token; token = token->next_)
		token->parents_.erase(handle());
}

void
Token::fill_all_parents()
{
	if (parents_.empty() || !all_parents_.empty())
		return;

	if (in_process_)
	{
		error(String(_("Loop in hierarchy of tokens detected")));
		assert(false);
		return;
	}

	in_process_ = true;
	for(Set::iterator i = parents_.begin(); i != parents_.end(); ++i)
	{
		Token &t = (*i)->cast_const();
		all_parents_.insert( t.handle() );
		t.fill_all_parents();
		all_parents_.insert( t.all_parents_.begin(), t.all_parents_.end() );
	}
	in_process_ = false;
}

void
Token::prepare()
{
	if (prepared_) return;
	if (in_process_)
	{
		error(String(_("Loop detected while tokens preparing")));
		assert(false);
		return;
	}

	prepare_vfunc();

	in_process_ = true;
	prepared_ = true;
	in_process_ = false;
}

void
Token::rebuild()
{
	if (ready_) return;
	assert(root_exists_);

	// clear
	for(Token* t = first_; t; t = t->next_)
	{
		t->unprepare_vfunc();
		t->prepared_ = false;
		t->children_.clear();
		t->all_parents_.clear();
		t->all_children_.clear();
	}

	// add root
	if (root_exists_)
		for(Token* t = first_; t; t = t->next_)
			if ( t->parents_.empty()
			  && t->handle() != token.handle() )
				t->parents_.insert( Token::token.handle() );

	// fill parents
	for(Token* t = first_; t; t = t->next_)
		t->fill_all_parents();

	// fill children
	for(Token* t = first_; t; t = t->next_)
	{
		for(Set::iterator i = t->parents_.begin(); i != t->parents_.end(); ++i)
			(*i)->cast_const().children_.insert( t->handle() );
		for(Set::iterator i = t->all_parents_.begin(); i != t->all_parents_.end(); ++i)
			(*i)->cast_const().all_children_.insert( t->handle() );
	}

	// prepare
	for(Token* t = first_; t; t = t->next_)
		t->prepare();

	ready_ = true;
}
