/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file blender/freestyle/intern/stroke/ChainingIterators.cpp
 *  \ingroup freestyle
 *  \brief Chaining iterators
 *  \author Stephane Grabli
 *  \date 01/07/2003
 */

#include "../python/Director.h"

#include "ChainingIterators.h"

#include "../system/TimeStamp.h"

namespace Freestyle {

ViewEdge *AdjacencyIterator::operator*()
{
	return (*_internalIterator).first;
}

bool AdjacencyIterator::isIncoming() const
{
	return (*_internalIterator).second;
}

int AdjacencyIterator::increment()
{
	++_internalIterator;
	while ((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first)))
		++_internalIterator;
	return 0;
}

bool AdjacencyIterator::isValid(ViewEdge *edge)
{
	if (_restrictToSelection) {
		if (edge->getTimeStamp() != TimeStamp::instance()->getTimeStamp())
			return false;
	}
	if (_restrictToUnvisited) {
		if (edge->getChainingTimeStamp() > TimeStamp::instance()->getTimeStamp())
			return false;
	}
	return true;
}

int ChainingIterator::init()
{
	return Director_BPy_ChainingIterator_init(this);
}

int ChainingIterator::traverse(const AdjacencyIterator &it)
{
	return Director_BPy_ChainingIterator_traverse(this, const_cast<AdjacencyIterator &>(it));
}

int ChainingIterator::increment()
{
	_increment = true;
	ViewVertex *vertex = getVertex();
	if (!vertex) {
		_edge = 0;
		return 0;
	}
	AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited);
	if (it.isEnd()) {
		_edge = 0;
		return 0;
	}
	if (traverse(it) < 0)
		return -1;
	_edge = result;
	if (_edge == 0)
		return 0;
	if (_edge->A() == vertex)
		_orientation = true;
	else
		_orientation = false;
	return 0;
}

int ChainingIterator::decrement()
{
	_increment = false;
	ViewVertex *vertex = getVertex();
	if (!vertex) {
		_edge = 0;
		return 0;
	}
	AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited);
	if (it.isEnd()) {
		_edge = 0;
		return 0;
	}
	if (traverse(it) < 0)
		return -1;
	_edge = result;
	if (_edge == 0)
		return 0;
	if (_edge->B() == vertex)
		_orientation = true;
	else
		_orientation = false;
	return 0;
}

//
// ChainSilhouetteIterators
//
///////////////////////////////////////////////////////////

int ChainSilhouetteIterator::traverse(const AdjacencyIterator& ait)
{
	AdjacencyIterator it(ait);
	ViewVertex *nextVertex = getVertex();
	// we can't get a NULL nextVertex here, it was intercepted before
	if (nextVertex->getNature() & Nature::T_VERTEX) {
		TVertex *tvertex = (TVertex *)nextVertex;
		ViewEdge *mate = (tvertex)->mate(getCurrentEdge());
		while (!it.isEnd()) {
			ViewEdge *ve = *it;
			if (ve == mate) {
				result = ve;
				return 0;
			}
			++it;
		}
		result = 0;
		return 0;
	}
	if (nextVertex->getNature() & Nature::NON_T_VERTEX) {
		//soc NonTVertex *nontvertex = (NonTVertex*)nextVertex;
		ViewEdge *newEdge(0);
		// we'll try to chain the edges by keeping the same nature...
		// the preseance order is : SILHOUETTE, BORDER, CREASE, MATERIAL_BOUNDARY, EDGE_MARK, SUGGESTIVE, VALLEY, RIDGE
		Nature::EdgeNature natures[8] = {
			Nature::SILHOUETTE,
			Nature::BORDER,
			Nature::CREASE,
			Nature::MATERIAL_BOUNDARY,
			Nature::EDGE_MARK,
			Nature::SUGGESTIVE_CONTOUR,
			Nature::VALLEY,
			Nature::RIDGE
		};
		int numNatures = sizeof(natures) / sizeof(Nature::EdgeNature);
		for (int i = 0; i < numNatures; ++i) {
			if (getCurrentEdge()->getNature() & natures[i]) {
				int n = 0;
				while (!it.isEnd()) {
					ViewEdge *ve = *it;
					if (ve->getNature() & natures[i]) {
						++n;
						newEdge = ve;
					}
					++it;
				}
				if (n == 1) {
					result = newEdge;
				}
				else {
					result = 0;
				}
				return 0;
			}
		}
	}
	result = 0;
	return 0;
}

int ChainPredicateIterator::traverse(const AdjacencyIterator& ait)
{
	AdjacencyIterator it(ait);
	// Iterates over next edges to see if one of them respects the predicate:
	while (!it.isEnd()) {
		ViewEdge *ve = *it;
		if (_unary_predicate->operator()(*ve) < 0)
			return -1;
		if (_unary_predicate->result) {
			if (_binary_predicate->operator()(*(getCurrentEdge()), *(ve)) < 0)
				return -1;
			if (_binary_predicate->result) {
				result = ve;
				return 0;
			}
		}
		++it;
	}
	result = 0;
	return 0;
}

} /* namespace Freestyle */
