/*
 * ***** 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 *****
 */

#ifndef __FREESTYLE_PREDICATES_1D_H__
#define __FREESTYLE_PREDICATES_1D_H__

/** \file blender/freestyle/intern/stroke/Predicates1D.h
 *  \ingroup freestyle
 *  \brief Class gathering stroke creation algorithms
 *  \author Stephane Grabli
 *  \author Emmanuel Turquin
 *  \date 01/07/2003
 */

#include <string>

#include "AdvancedFunctions1D.h"

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

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

#include "../view_map/Interface1D.h"
#include "../view_map/Functions1D.h"

#ifdef WITH_CXX_GUARDEDALLOC
#include "MEM_guardedalloc.h"
#endif

namespace Freestyle {

//
// UnaryPredicate1D (base class for predicates in 1D)
//
///////////////////////////////////////////////////////////

/*! Base class for Unary Predicates that work on Interface1D.
 *  A UnaryPredicate1D is a functor that evaluates a condition on a Interface1D and returns
 *  true or false depending on whether this condition is satisfied or not.
 *  The UnaryPredicate1D is used by calling its () operator.
 *  Any inherited class must overload the () operator.
 */
class UnaryPredicate1D
{
public:
	bool result;
	PyObject *py_up1D;

	/*! Default constructor. */
	UnaryPredicate1D()
	{
		py_up1D = NULL;
	}

	/*! Destructor. */
	virtual ~UnaryPredicate1D() {}

	/*! Returns the string of the name of the UnaryPredicate1D. */
	virtual string getName() const
	{
		return "UnaryPredicate1D";
	}

	/*! The () operator. Must be overload by inherited classes.
	 *  \param inter
	 *    The Interface1D on  which we wish to evaluate the predicate.
	 *  \return true if the condition is satisfied, false otherwise.
	 */
	virtual int operator()(Interface1D& inter)
	{
		return Director_BPy_UnaryPredicate1D___call__(this, inter);
	}

#ifdef WITH_CXX_GUARDEDALLOC
	MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:UnaryPredicate1D")
#endif
};


//
// BinaryPredicate1D (base class for predicates in 1D)
//
///////////////////////////////////////////////////////////

/*! Base class for Binary Predicates working on Interface1D.
 *  A BinaryPredicate1D is typically an ordering relation between two Interface1D.
 *  It evaluates a relation between 2 Interface1D and returns true or false.
 *  It is used by calling the () operator.
 */
class BinaryPredicate1D
{
public:
	bool result;
	PyObject *py_bp1D;

	/*! Default constructor. */
	BinaryPredicate1D()
	{
		py_bp1D = NULL;
	}

	/*! Destructor. */
	virtual ~BinaryPredicate1D() {}

	/*! Returns the string of the name of the binary predicate. */
	virtual string getName() const
	{
		return "BinaryPredicate1D";
	}

	/*! The () operator. Must be overload by inherited classes.
	 *  It evaluates a relation between 2 Interface1D.
	 *  \param inter1
	 *    The first Interface1D.
	 *  \param inter2
	 *    The second Interface1D.
	 *  \return true or false.
	 */
	virtual int operator()(Interface1D& inter1, Interface1D& inter2)
	{
		return Director_BPy_BinaryPredicate1D___call__(this, inter1, inter2);
	}

#ifdef WITH_CXX_GUARDEDALLOC
	MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BinaryPredicate1D")
#endif
};


//
// Predicates definitions
//
///////////////////////////////////////////////////////////

namespace Predicates1D {

// TrueUP1D
/*! Returns true */
class TrueUP1D : public UnaryPredicate1D
{
public:
	/*! Constructor */
	TrueUP1D() {}

	/*! Returns the string "TrueUP1D"*/
	string getName() const
	{
		return "TrueUP1D";
	}

	/*! the () operator */
	int operator()(Interface1D&)
	{
		result = true;
		return 0;
	}
};

// FalseUP1D
/*! Returns false */
class FalseUP1D : public UnaryPredicate1D
{
public:
	/*! Constructor */
	FalseUP1D() {}

	/*! Returns the string "FalseUP1D"*/
	string getName() const
	{
		return "FalseUP1D";
	}

	/*! the () operator */
	int operator()(Interface1D&)
	{
		result = false;
		return 0;
	}
};

// QuantitativeInvisibilityUP1D
/*! Returns true if the Quantitative Invisibility evaluated at an Interface1D, using the QuantitativeInvisibilityF1D
 *  functor, equals a certain user-defined value.
 */
class QuantitativeInvisibilityUP1D : public UnaryPredicate1D
{
public:
	/*! Builds the Predicate.
	 *  \param qi
	 *    The Quantitative Invisibility you want the Interface1D to have
	 */
	QuantitativeInvisibilityUP1D(unsigned qi = 0) : _qi(qi) {}

	/*! Returns the string "QuantitativeInvisibilityUP1D" */
	string getName() const
	{
		return "QuantitativeInvisibilityUP1D";
	}

	/*! the () operator */
	int operator()(Interface1D& inter)
	{
		Functions1D::QuantitativeInvisibilityF1D func;
		if (func(inter) < 0)
			return -1;
		result = (func.result == _qi);
		return 0;
	}

private:
	unsigned _qi;
};

// ContourUP1D
/*! Returns true if the Interface1D is a contour.
 *  An Interface1D is a contour if it is borded by a different shape on each of its sides.
 */
class ContourUP1D : public UnaryPredicate1D
{
private:
	Functions1D::CurveNatureF1D _getNature;

public:
	/*! Returns the string "ContourUP1D"*/
	string getName() const
	{
		return "ContourUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		if (_getNature(inter) < 0)
			return -1;
		if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) {
			Interface0DIterator it = inter.verticesBegin();
			for (; !it.isEnd(); ++it) {
				if (Functions0D::getOccludeeF0D(it) != Functions0D::getShapeF0D(it)) {
					result = true;
					return 0;
				}
			}
		}
		result = false;
		return 0;
	}
};

// ExternalContourUP1D
/*! Returns true if the Interface1D is an external contour.
 *  An Interface1D is an external contour if it is borded by no shape on one of its sides.
 */
class ExternalContourUP1D : public UnaryPredicate1D
{
private:
	Functions1D::CurveNatureF1D _getNature;

public:
	/*! Returns the string "ExternalContourUP1D" */
	string getName() const
	{
		return "ExternalContourUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		if (_getNature(inter) < 0)
			return -1;
		if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) {
			set<ViewShape*> occluded;
			Functions1D::getOccludeeF1D(inter, occluded);
			for (set<ViewShape*>::iterator os = occluded.begin(), osend = occluded.end(); os != osend; ++os) {
				if ((*os) == 0) {
					result = true;
					return 0;
				}
			}
		}
		result = false;
		return 0;
	}
};

// EqualToTimeStampUP1D
/*! Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */
class EqualToTimeStampUP1D : public UnaryPredicate1D
{
protected:
	unsigned _timeStamp;

public:
	EqualToTimeStampUP1D(unsigned ts) : UnaryPredicate1D()
	{
		_timeStamp = ts;
	}

	/*! Returns the string "EqualToTimeStampUP1D"*/
	string getName() const
	{
		return "EqualToTimeStampUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		result = (inter.getTimeStamp() == _timeStamp);
		return 0;
	}
};

// EqualToChainingTimeStampUP1D
/*! Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */
class EqualToChainingTimeStampUP1D : public UnaryPredicate1D
{
protected:
	unsigned _timeStamp;

public:
	EqualToChainingTimeStampUP1D(unsigned ts) : UnaryPredicate1D()
	{
		_timeStamp = ts;
	}

	/*! Returns the string "EqualToChainingTimeStampUP1D"*/
	string getName() const
	{
		return "EqualToChainingTimeStampUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		ViewEdge *edge = dynamic_cast<ViewEdge*>(&inter);
		if (!edge) {
			result = false;
			return 0;
		}
		result = (edge->getChainingTimeStamp() >= _timeStamp);
		return 0;
	}
};

// ShapeUP1D
/*! Returns true if the shape to which the Interface1D belongs to has the same Id as the one specified by the user. */
class ShapeUP1D: public UnaryPredicate1D
{
private:
	Id _id;

public:
	/*! Builds the Predicate.
	 *  \param idFirst
	 *    The first Id component.
	 *  \param idSecond
	 *    The second Id component.
	 */
	ShapeUP1D(unsigned idFirst, unsigned idSecond = 0) : UnaryPredicate1D()
	{
		_id = Id(idFirst, idSecond);
	}

	/*! Returns the string "ShapeUP1D"*/
	string getName() const
	{
		return "ShapeUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		set<ViewShape*> shapes;
		Functions1D::getShapeF1D(inter, shapes);
		for (set<ViewShape*>::iterator s = shapes.begin(), send = shapes.end(); s != send; ++s) {
			if ((*s)->getId() == _id) {
				result = true;
				return 0;
			}
		}
		result = false;
		return 0;
	}
};

// WithinImageBoundaryUP1D
/*! Returns true if the Interface1D is (partly) within the image boundary. */
class WithinImageBoundaryUP1D: public UnaryPredicate1D
{
private:
	real _xmin, _ymin, _xmax, _ymax;

public:
	/*! Builds the Predicate.
	 *  \param xmin
	 *    The X lower bound of the image boundary.
	 *  \param ymin
	 *    The Y lower bound of the image boundary.
	 *  \param xmax
	 *    The X upper bound of the image boundary.
	 *  \param ymax
	 *    The Y upper bound of the image boundary.
	 */
	WithinImageBoundaryUP1D(const real xmin, const real ymin, const real xmax, const real ymax)
	: _xmin(xmin), _ymin(ymin), _xmax(xmax), _ymax(ymax)
	{
	}

	/*! Returns the string "WithinImageBoundaryUP1D" */
	string getName() const
	{
		return "WithinImageBoundaryUP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& inter)
	{
		// 1st pass: check if a point is within the image boundary.
		Interface0DIterator it = inter.verticesBegin(), itend = inter.verticesEnd();
		for (; it != itend; ++it) {
			real x = (*it).getProjectedX();
			real y = (*it).getProjectedY();
			if (_xmin <= x && x <= _xmax && _ymin <= y && y <= _ymax) {
				result = true;
				return 0;
			}
		}
		// 2nd pass: check if a line segment intersects with the image boundary.
		it = inter.verticesBegin();
		if (it != itend) {
			Vec2r pmin(_xmin, _ymin);
			Vec2r pmax(_xmax, _ymax);
			Vec2r prev((*it).getPoint2D());
			++it;
			for (; it != itend; ++it) {
				Vec2r p((*it).getPoint2D());
				if (GeomUtils::intersect2dSeg2dArea (pmin, pmax, prev, p)) {
					result = true;
					return 0;
				}
				prev = p;
			}
		}
		result = false;
		return 0;
	}
};

//
//   Binary Predicates definitions
//
///////////////////////////////////////////////////////////

// TrueBP1D
/*! Returns true. */
class TrueBP1D : public BinaryPredicate1D
{
public:
	/*! Returns the string "TrueBP1D" */
	string getName() const
	{
		return "TrueBP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& i1, Interface1D& i2)
	{
		result = true;
		return 0;
	}
};

// FalseBP1D
/*! Returns false. */
class FalseBP1D : public BinaryPredicate1D
{
public:
	/*! Returns the string "FalseBP1D" */
	string getName() const
	{
		return "FalseBP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& i1, Interface1D& i2)
	{
		result = false;
		return 0;
	}
};

// Length2DBP1D
/*! Returns true if the 2D length of the Interface1D i1 is less than the 2D length of the Interface1D i2. */
class Length2DBP1D : public BinaryPredicate1D
{
public:
	/*! Returns the string "Length2DBP1D" */
	string getName() const
	{
		return "Length2DBP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& i1, Interface1D& i2)
	{
		result = (i1.getLength2D() > i2.getLength2D());
		return 0;
	}
};

// SameShapeIdBP1D
/*! Returns true if the Interface1D i1 and i2 belong to the same shape. */
class SameShapeIdBP1D : public BinaryPredicate1D
{
public:
	/*! Returns the string "SameShapeIdBP1D" */
	string getName() const
	{
		return "SameShapeIdBP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& i1, Interface1D& i2)
	{
		set<ViewShape*> shapes1;
		Functions1D::getShapeF1D(i1, shapes1);
		set<ViewShape*> shapes2;
		Functions1D::getShapeF1D(i2, shapes2);
		// FIXME:// n2 algo, can do better...
		for (set<ViewShape*>::iterator s = shapes1.begin(), send = shapes1.end(); s != send; ++s) {
			Id current = (*s)->getId();
			for (set<ViewShape*>::iterator s2 = shapes2.begin(), s2end = shapes2.end(); s2 != s2end; ++s2) {
				if ((*s2)->getId() == current) {
					result = true;
					return 0;
				}
			}
		}
		result = false;
		return 0;
	}
};

// ViewMapGradientNormBP1D
/*! Returns true if the evaluation of the Gradient norm Function is higher for Interface1D i1 than for i2. */
class ViewMapGradientNormBP1D : public BinaryPredicate1D
{
private:
	Functions1D::GetViewMapGradientNormF1D _func;

public:
	ViewMapGradientNormBP1D(int level, IntegrationType iType = MEAN, float sampling = 2.0)
	: BinaryPredicate1D(), _func(level, iType, sampling)
	{
	}

	/*! Returns the string "ViewMapGradientNormBP1D" */
	string getName() const
	{
		return "ViewMapGradientNormBP1D";
	}

	/*! The () operator. */
	int operator()(Interface1D& i1, Interface1D& i2)
	{
		if (_func(i1) < 0)
			return -1;
		real n1 = _func.result;
		if (_func(i2) < 0)
			return -1;
		real n2 = _func.result;
		result = (n1 > n2);
		return 0;
	}
};

} // end of namespace Predicates1D

} /* namespace Freestyle */

#endif // __FREESTYLE_PREDICATES_1D_H__
