/* This file is part of the KDE project
   Copyright (C) 2001, 2002, 2003 The Karbon Developers

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#ifndef __VSEGMENT_H__
#define __VSEGMENT_H__

#include <qptrlist.h>
#include <qvaluelist.h>

#include <koPoint.h>
#include <koRect.h>

#include "vglobal.h"
#include <koffice_export.h>

class QDomElement;
class VPainter;

/**
 * A class representing lines and beziers. We waste some KoPoints, if we
 * would use only lines, but this makes it easy to convert the segment types
 * into each other. Make sure yourself, that you pass values to functions within
 * proper ranges.
 */

class KARBONBASE_EXPORT VSegment
{
	friend class VSubpath;
	friend class VSubpathIterator;

public:
	/**
	 * Tells which control point is "fixed" i.e. located at the
	 * corresponding knot and invisible. This flag makes no sense for
	 * line segments.
	 */
	enum VCtrlPointFixing
	{
		none = 0,
		first = 1,
		second = 2
	};

	enum VState
	{
		normal,
		deleted
	};


	VSegment( unsigned short deg = 3 );

	VSegment( const VSegment& segment );

	~VSegment();

	/**
	 * Returns the segment's degree, which is identical to the number of nodes.
	 * For cubic beziers it is "three" and "one" for lines.
	 */
	unsigned short degree() const
	{
		return m_degree;
	}

	/**
	 * Sets the segment's degree and thus resizes the array of node data.
	 * The node data is copied from the old knot "backwards".
	 */
	void setDegree( unsigned short deg );

	/**
	 * Tests for the segment type ("begin", "line" or "curve").
	 */
	bool isBegin() const { return (degree() == 1) && !prev(); }
	bool isLine() const { return (degree() == 1) && prev(); }
	bool isCurve() const { return degree() > 1; }

	/**
	 * Returns the segment state.
	 */
	VState state() const
	{
		return m_state;
	}

	/**
	 * Sets the segment state.
	 */
	void setState( VState state )
	{
		m_state = state;
	}


	/**
	 * Returns the segment's point with index 0 <= i < degree().
	 */
	const KoPoint& point( int i ) const
	{
		return m_nodes[ i ].m_vector;
	}

	/**
	 * This is a convenience function. It returns the point with index
	 * 0 <= i <= degree() while p( 0 ) is the knot of the previous
	 * segment.
	 */
	const KoPoint& p( int i ) const
	{
		return i == 0
			   ? prev()->knot()
			   : m_nodes[ --i ].m_vector;
	}

	/**
	 * Returns the knot. This is a convenience function using point().
	 */
	const KoPoint& knot() const
	{
		return point( degree() - 1 );
	}

	/**
	 * Sets the segment's point with index 0 <= i < degree() to "p".
	 */
	void setPoint( int i, const KoPoint& p )
	{
		m_nodes[ i ].m_vector = p;
	}

	/**
	 * This is a convenience function. It sets the point with index
	 * 0 <= i <= degree() to "p" while setP( 0 ) sets the knot of the
	 * previous segment.
	 */
	void setP( int i, const KoPoint& p )
	{
		if( i == 0 )
			prev()->setKnot( p );
		else
			m_nodes[ --i ].m_vector = p;
	}

	/**
	 * Sets the knot. This is a convenience function.
	 */
	void setKnot( const KoPoint& p )
	{
		m_nodes[ degree() - 1 ].m_vector = p;
	}


	/**
	 * Returns true if the point with index 0 <= i < degree() is selected.
	 */
	bool pointIsSelected( int i ) const
	{
		return m_nodes[ i ].m_isSelected;
	}

	/**
	 * Returns true if the knot is selected. This is a convenience function.
	 */
	bool knotIsSelected() const
	{
		return m_nodes[ degree() - 1 ].m_isSelected;
	}

	/**
	 * Selects the point with index 0 <= i < degree().
	 */
	void selectPoint( int i, bool select = true )
	{
		m_nodes[ i ].m_isSelected = select;
	}

	/**
	 * Selects/deselects the knot of this segment.
	 */
	void selectKnot( bool select = true )
	{
		m_nodes[ degree() - 1 ].m_isSelected = select;
	}


	/**
	 * Returns index of the node at point p. Returns 0 if no
	 * segment point matches point p.
	 */
	// TODO: Move this function into "userland"
	uint nodeNear( const KoPoint& p,
				   double isNearRange = VGlobal::isNearRange ) const;


	/**
	 * Returns a pointer to the previous not deleted segment, if
	 * stored in a VSubpath.
	 */
	VSegment* prev() const;

	/**
	 * Returns a pointer to the next not deleted segment, if
	 * stored in a VSubpath.
	 */
	VSegment* next() const;

	/**
	 * Returns true if the segment is flat. That means it's height
	 * is smaller than flatness.
	 */
	bool isFlat( double flatness = VGlobal::flatnessTolerance ) const;


	/**
	 * Calculates the point on this segment at parameter 0 <= t <= 1.
	 * This is a convenience wrapper for pointDerivativesAt().
	 */
	KoPoint pointAt( double t ) const;

	/**
	 * Calculates the point and the derivatives of first and
	 * second order for 0 <= t <= 1.
	 */
	void pointDerivativesAt( double t, KoPoint* p = 0L,
							 KoPoint* d1 = 0L, KoPoint* d2 = 0L ) const;


	/**
	 * Calculates the normalized tangent vector (length=1) at the point
	 * parameterized by 0 <= t <= 1. This is a convenience wrapper
	 * for pointTangentNormalAt(). Use the latter function directly if you
	 * need to calculate the point and normal vector or tangent vector
	 * at once.
	 */
	KoPoint tangentAt( double t ) const;

	/**
	 * Calculates the point, the tangent vector and the normal vector for
	 * 0 <= t <= 1. The tangent vector and the normal vector are
	 * normalized (length=1).
	 */
	void pointTangentNormalAt( double t, KoPoint* p = 0L,
							   KoPoint* tn = 0L, KoPoint* n = 0L ) const;


	/**
	 * Calculates the arclength from p0 to the point parametrized
	 * by 0 <= t <= 1. For beziers this function is a bit expensive.
	 */
	double length( double t = 1.0 ) const;

	/**
	 * Calculates the chord length (the distance from the previous
	 * knot to the current knot).
	 */
	double chordLength() const;

	/**
	 * Calculates the length of the control polygon.
	 */
	double polyLength() const;


	/**
	 * Calculates the parameter of a point located at arclength len.
	 * This is the exact inverse operation of length( t ).
	 */
	double lengthParam( double len ) const;


	/**
	 * Calculates the parameter of the nearest point on this segment
	 * to the point p. This function is pretty expensive.
	 */
	double nearestPointParam( const KoPoint& p ) const;


	/**
	 * Calculates wether the tangent at the knot is exactly parallel to
	 * the tangent at p0 of the next segment. Returns false if the
	 * current segment is a "begin".
	 */
	bool isSmooth( const VSegment& next ) const;

	bool isSmooth() const
	{
		return next()
			   ? isSmooth( *next() )
			   : false;
	}


	/**
	 * Creates a reverted version of this segment. For example:
	 * if this segment is a line from A to B, the result is a
	 * line from B to A.
	 */
	VSegment* revert() const;


	/**
	 *  Splits the segment at parameter 0 <= t <= 1. Returns a pointer
	 *  to the first segment and modifies the current one to
	 *  be the second segment.
	 */
	VSegment* splitAt( double t );


	/**
	 * Calculates height of point p above line AB.
	 */
	static double height(
		const KoPoint& a,
		const KoPoint& p,
		const KoPoint& b );


	/**
	 * Calculates whether lines A0A1 and B0B1 intersect.
	 */
	static bool linesIntersect(
		const KoPoint& a0,
		const KoPoint& a1,
		const KoPoint& b0,
		const KoPoint& b1 );

	/**
	 * Returns true, if this segment intersects the other segment.
	 */
	bool intersects( const VSegment& segment ) const;


	/**
	 * Returns a number > 0 if the point p is left, 0 if it's on and
	 * a number < 0 if it's right of the infinite line through the
	 * previous segment's knot and the current knot.
	 */
	double pointIsLeft( const KoPoint& p ) const
	{
		return
			( knot().x() - prev()->knot().x() ) *
			( p.y() - prev()->knot().y() )
			-
			( p.x() - prev()->knot().x() ) *
			( knot().y() - prev()->knot().y() );
	}

	/**
	 * Calculates the bounding box.
	 */
	KoRect boundingBox() const;


	void draw( VPainter* painter ) const;

	// TODO: remove this backward compatibility function after koffice 1.3.x.
	void load( const QDomElement& element );


	/**
	 * Returns a pointer to a copy of this segment.
	 */
	VSegment* clone() const;

private:
	/**
	 * Calculates the solutions of y(x) = 0 where 0 <= x <= 1. The
	 * returned parameters are not ordered.
	 */
	void rootParams( QValueList<double>& params ) const;

	/**
	 * Calculates how often the control polygon crosses the x-axis.
	 */
	int controlPolygonZeros() const;


	/**
	 * The segment degree. For (cubic) beziers "three", "one" for lines.
	 */
	unsigned short m_degree : 6;

	/**
	 * The segment state.
	 */
	VState m_state : 2;

	/**
	 * Node data.
	 */
	struct VNodeData
	{
		KoPoint m_vector;
		bool m_isSelected;
	};

	/**
	 * A pointer to an array of node data.
	 */
	VNodeData* m_nodes;


	/**
	 * Pointer to the previous segment.
	 */
	VSegment* m_prev;

	/**
	 * Pointer to the next segment.
	 */
	VSegment* m_next;
};

#endif

