/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
#include <osl/diagnose.h>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <rtl/instance.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <boost/scoped_ptr.hpp>
#include <vector>
#include <algorithm>

//////////////////////////////////////////////////////////////////////////////

struct CoordinateData2D : public basegfx::B2DPoint
{
public:
    CoordinateData2D() {}

	explicit CoordinateData2D(const basegfx::B2DPoint& rData) 
    :	B2DPoint(rData) 
	{}

    CoordinateData2D& operator=(const basegfx::B2DPoint& rData)
    {
        B2DPoint::operator=(rData);
        return *this;
	}

	void transform(const basegfx::B2DHomMatrix& rMatrix) 
	{ 
        *this *= rMatrix; 
	}
};

//////////////////////////////////////////////////////////////////////////////

class CoordinateDataArray2D
{
	typedef ::std::vector< CoordinateData2D > CoordinateData2DVector;

	CoordinateData2DVector							maVector;

public:
	explicit CoordinateDataArray2D(sal_uInt32 nCount)
	:	maVector(nCount)
	{
	}

	explicit CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal)
	:	maVector(rOriginal.maVector)
	{
	}

	CoordinateDataArray2D(const CoordinateDataArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
	:	maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
	{
	}

	sal_uInt32 count() const
	{
		return maVector.size();
	}

	bool operator==(const CoordinateDataArray2D& rCandidate) const
	{
		return (maVector == rCandidate.maVector);
	}

	const basegfx::B2DPoint& getCoordinate(sal_uInt32 nIndex) const
	{
        return maVector[nIndex];
	}

	void setCoordinate(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
	{
        maVector[nIndex] = rValue;
	}

	void reserve(sal_uInt32 nCount)
	{
		maVector.reserve(nCount);
	}

	void append(const CoordinateData2D& rValue)
	{
		maVector.push_back(rValue);
	}

	void insert(sal_uInt32 nIndex, const CoordinateData2D& rValue, sal_uInt32 nCount)
	{
		if(nCount)
		{
			// add nCount copies of rValue
			CoordinateData2DVector::iterator aIndex(maVector.begin());
			aIndex += nIndex;
			maVector.insert(aIndex, nCount, rValue);
		}
	}

	void insert(sal_uInt32 nIndex, const CoordinateDataArray2D& rSource)
	{
		const sal_uInt32 nCount(rSource.maVector.size());

		if(nCount)
		{
			// insert data
			CoordinateData2DVector::iterator aIndex(maVector.begin());
			aIndex += nIndex;
			CoordinateData2DVector::const_iterator aStart(rSource.maVector.begin());
			CoordinateData2DVector::const_iterator aEnd(rSource.maVector.end());
			maVector.insert(aIndex, aStart, aEnd);
		}
	}

	void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
	{
		if(nCount)
		{
			// remove point data
			CoordinateData2DVector::iterator aStart(maVector.begin());
			aStart += nIndex;
			const CoordinateData2DVector::iterator aEnd(aStart + nCount);
			maVector.erase(aStart, aEnd);
		}
	}

	void flip(bool bIsClosed)
	{
		if(maVector.size() > 1)
		{
			// to keep the same point at index 0, just flip all points except the
			// first one when closed
			const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
			CoordinateData2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
			CoordinateData2DVector::iterator aEnd(maVector.end() - 1);
			
			for(sal_uInt32 a(0); a < nHalfSize; a++)
			{
				::std::swap(*aStart, *aEnd);
				aStart++;
				aEnd--;
			}
		}
	}

	void removeDoublePointsAtBeginEnd()
	{
		// remove from end as long as there are at least two points
		// and begin/end are equal
		while((maVector.size() > 1) && (maVector[0] == maVector[maVector.size() - 1]))
		{
			maVector.pop_back();
		}
	}

	void removeDoublePointsWholeTrack()
	{
		sal_uInt32 nIndex(0);

		// test as long as there are at least two points and as long as the index
		// is smaller or equal second last point
		while((maVector.size() > 1) && (nIndex <= maVector.size() - 2))
		{
			if(maVector[nIndex] == maVector[nIndex + 1])
			{
				// if next is same as index, delete next
				maVector.erase(maVector.begin() + (nIndex + 1));
			}
			else
			{
				// if different, step forward
				nIndex++;
			}
		}
	}

	void transform(const basegfx::B2DHomMatrix& rMatrix)
	{
		CoordinateData2DVector::iterator aStart(maVector.begin());
		CoordinateData2DVector::iterator aEnd(maVector.end());

		for(; aStart != aEnd; aStart++)
		{
			aStart->transform(rMatrix);
		}
	}

    const basegfx::B2DPoint* begin() const
    {
        if(maVector.empty())
            return 0;
        else
            return &maVector.front();
    }

    const basegfx::B2DPoint* end() const
    {
        if(maVector.empty())
            return 0;
        else
            return (&maVector.back())+1;
    }

    basegfx::B2DPoint* begin()
    {
        if(maVector.empty())
            return 0;
        else
            return &maVector.front();
    }

    basegfx::B2DPoint* end()
    {
        if(maVector.empty())
            return 0;
        else
            return (&maVector.back())+1;
    }
};

//////////////////////////////////////////////////////////////////////////////

class ControlVectorPair2D
{
	basegfx::B2DVector							maPrevVector;
	basegfx::B2DVector							maNextVector;

public:
    explicit ControlVectorPair2D () { }

	const basegfx::B2DVector& getPrevVector() const 
	{ 
		return maPrevVector; 
	}
	
	void setPrevVector(const basegfx::B2DVector& rValue) 
	{ 
		if(rValue != maPrevVector) 
			maPrevVector = rValue; 
	}

	const basegfx::B2DVector& getNextVector() const 
	{ 
		return maNextVector; 
	}

	void setNextVector(const basegfx::B2DVector& rValue) 
	{ 
		if(rValue != maNextVector) 
			maNextVector = rValue; 
	}

	bool operator==(const ControlVectorPair2D& rData) const 
	{ 
		return (maPrevVector == rData.getPrevVector() && maNextVector == rData.getNextVector()); 
	}

	void flip()
	{
		::std::swap(maPrevVector, maNextVector);
	}
};

//////////////////////////////////////////////////////////////////////////////

class ControlVectorArray2D
{
	typedef ::std::vector< ControlVectorPair2D > ControlVectorPair2DVector;

	ControlVectorPair2DVector							maVector;
	sal_uInt32											mnUsedVectors;

public:
	explicit ControlVectorArray2D(sal_uInt32 nCount)
	:	maVector(nCount),
		mnUsedVectors(0)
	{}

	ControlVectorArray2D(const ControlVectorArray2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
	:	maVector(),
		mnUsedVectors(0)
	{
		ControlVectorPair2DVector::const_iterator aStart(rOriginal.maVector.begin());
		aStart += nIndex;
		ControlVectorPair2DVector::const_iterator aEnd(aStart);
		aEnd += nCount;
		maVector.reserve(nCount);

		for(; aStart != aEnd; aStart++)
		{
			if(!aStart->getPrevVector().equalZero())
				mnUsedVectors++;

			if(!aStart->getNextVector().equalZero())
				mnUsedVectors++;

			maVector.push_back(*aStart);
		}
	}

	sal_uInt32 count() const
	{
		return maVector.size();
	}

	bool operator==(const ControlVectorArray2D& rCandidate) const
	{
		return (maVector == rCandidate.maVector);
	}

	bool isUsed() const 
	{ 
		return (0 != mnUsedVectors);
	}

	const basegfx::B2DVector& getPrevVector(sal_uInt32 nIndex) const
	{
		return maVector[nIndex].getPrevVector();
	}

	void setPrevVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
	{
		bool bWasUsed(mnUsedVectors && !maVector[nIndex].getPrevVector().equalZero());
		bool bIsUsed(!rValue.equalZero());

		if(bWasUsed)
		{
			if(bIsUsed)
			{
				maVector[nIndex].setPrevVector(rValue);
			}
			else
			{
				maVector[nIndex].setPrevVector(basegfx::B2DVector::getEmptyVector());
				mnUsedVectors--;
			}
		}
		else
		{
			if(bIsUsed)
			{
				maVector[nIndex].setPrevVector(rValue);
				mnUsedVectors++;
			}
		}
	}

	const basegfx::B2DVector& getNextVector(sal_uInt32 nIndex) const
	{
		return maVector[nIndex].getNextVector();
	}

	void setNextVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
	{
		bool bWasUsed(mnUsedVectors && !maVector[nIndex].getNextVector().equalZero());
		bool bIsUsed(!rValue.equalZero());

		if(bWasUsed)
		{
			if(bIsUsed)
			{
				maVector[nIndex].setNextVector(rValue);
			}
			else
			{
				maVector[nIndex].setNextVector(basegfx::B2DVector::getEmptyVector());
				mnUsedVectors--;
			}
		}
		else
		{
			if(bIsUsed)
			{
				maVector[nIndex].setNextVector(rValue);
				mnUsedVectors++;
			}
		}
	}

	void append(const ControlVectorPair2D& rValue)
	{
		maVector.push_back(rValue);

		if(!rValue.getPrevVector().equalZero())
			mnUsedVectors += 1;

		if(!rValue.getNextVector().equalZero())
			mnUsedVectors += 1;
	}

	void insert(sal_uInt32 nIndex, const ControlVectorPair2D& rValue, sal_uInt32 nCount)
	{
		if(nCount)
		{
			// add nCount copies of rValue
			ControlVectorPair2DVector::iterator aIndex(maVector.begin());
			aIndex += nIndex;
			maVector.insert(aIndex, nCount, rValue);

			if(!rValue.getPrevVector().equalZero())
				mnUsedVectors += nCount;

			if(!rValue.getNextVector().equalZero())
				mnUsedVectors += nCount;
		}
	}

	void insert(sal_uInt32 nIndex, const ControlVectorArray2D& rSource)
	{
		const sal_uInt32 nCount(rSource.maVector.size());

		if(nCount)
		{
			// insert data
			ControlVectorPair2DVector::iterator aIndex(maVector.begin());
			aIndex += nIndex;
			ControlVectorPair2DVector::const_iterator aStart(rSource.maVector.begin());
			ControlVectorPair2DVector::const_iterator aEnd(rSource.maVector.end());
			maVector.insert(aIndex, aStart, aEnd);

			for(; aStart != aEnd; aStart++)
			{
				if(!aStart->getPrevVector().equalZero())
					mnUsedVectors++;

				if(!aStart->getNextVector().equalZero())
					mnUsedVectors++;
			}
		}
	}

	void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
	{
		if(nCount)
		{
			const ControlVectorPair2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
			const ControlVectorPair2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
			ControlVectorPair2DVector::const_iterator aStart(aDeleteStart);

			for(; mnUsedVectors && aStart != aDeleteEnd; aStart++)
			{
				if(!aStart->getPrevVector().equalZero())
					mnUsedVectors--;

				if(mnUsedVectors && !aStart->getNextVector().equalZero())
					mnUsedVectors--;
			}

			// remove point data
			maVector.erase(aDeleteStart, aDeleteEnd);
		}
	}

	void flip(bool bIsClosed)
	{
		if(maVector.size() > 1)
		{
			// to keep the same point at index 0, just flip all points except the
			// first one when closed
			const sal_uInt32 nHalfSize(bIsClosed ? (maVector.size() - 1) >> 1 : maVector.size() >> 1);
			ControlVectorPair2DVector::iterator aStart(bIsClosed ? maVector.begin() + 1 : maVector.begin());
			ControlVectorPair2DVector::iterator aEnd(maVector.end() - 1);
			
			for(sal_uInt32 a(0); a < nHalfSize; a++)
			{
				// swap Prev and Next
				aStart->flip();
				aEnd->flip();

				// swap entries
				::std::swap(*aStart, *aEnd);

				aStart++;
				aEnd--;
			}

			if(aStart == aEnd)
			{
				// swap Prev and Next at middle element (if exists)
				aStart->flip();
			}

			if(bIsClosed)
			{
				// swap Prev and Next at start element
				maVector.begin()->flip();
			}
		}
	}
};

//////////////////////////////////////////////////////////////////////////////

class ImplBufferedData
{
private:
	// Possibility to hold the last subdivision
	boost::scoped_ptr< basegfx::B2DPolygon >		mpDefaultSubdivision;

    // Possibility to hold the last B2DRange calculation
	boost::scoped_ptr< basegfx::B2DRange >		    mpB2DRange;

public:
    ImplBufferedData()
    :   mpDefaultSubdivision(),
        mpB2DRange()
    {}

    const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
	{
		if(!mpDefaultSubdivision)
		{
			const_cast< ImplBufferedData* >(this)->mpDefaultSubdivision.reset(new basegfx::B2DPolygon(basegfx::tools::adaptiveSubdivideByCount(rSource, 9)));
		}

        return *mpDefaultSubdivision;
	}
	
    const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
    {
		if(!mpB2DRange)
		{
			basegfx::B2DRange aNewRange;
			const sal_uInt32 nPointCount(rSource.count());

			if(nPointCount)
			{
				for(sal_uInt32 a(0); a < nPointCount; a++)
				{
					aNewRange.expand(rSource.getB2DPoint(a));
				}

				if(rSource.areControlPointsUsed())
				{
					const sal_uInt32 nEdgeCount(rSource.isClosed() ? nPointCount : nPointCount - 1);

					if(nEdgeCount)
					{
						basegfx::B2DCubicBezier aEdge;
						aEdge.setStartPoint(rSource.getB2DPoint(0));

						for(sal_uInt32 b(0); b < nEdgeCount; b++)
						{
							const sal_uInt32 nNextIndex((b + 1) % nPointCount);
							aEdge.setControlPointA(rSource.getNextControlPoint(b));
							aEdge.setControlPointB(rSource.getPrevControlPoint(nNextIndex));
							aEdge.setEndPoint(rSource.getB2DPoint(nNextIndex));

							if(aEdge.isBezier())
							{
								const basegfx::B2DRange aBezierRangeWithControlPoints(aEdge.getRange());

								if(!aNewRange.isInside(aBezierRangeWithControlPoints))
								{
									// the range with control points of the current edge is not completely
									// inside the current range without control points. Expand current range by
									// subdividing the bezier segment.
									// Ideal here is a subdivision at the extreme values, so use 
									// getAllExtremumPositions to get all extremas in one run
									::std::vector< double > aExtremas;
									
									aExtremas.reserve(4);
									aEdge.getAllExtremumPositions(aExtremas);

									const sal_uInt32 nExtremaCount(aExtremas.size());

									for(sal_uInt32 c(0); c < nExtremaCount; c++)
									{
										aNewRange.expand(aEdge.interpolatePoint(aExtremas[c]));
									}
								}
							}

							// prepare next edge
							aEdge.setStartPoint(aEdge.getEndPoint());
						}
					}
				}
			}

			const_cast< ImplBufferedData* >(this)->mpB2DRange.reset(new basegfx::B2DRange(aNewRange));
		}

        return *mpB2DRange;
    }
};

//////////////////////////////////////////////////////////////////////////////

class ImplB2DPolygon
{
private:
	// The point vector. This vector exists always and defines the
	// count of members.
	CoordinateDataArray2D							maPoints;

	// The control point vectors. This vectors are created on demand
	// and may be zero.
	boost::scoped_ptr< ControlVectorArray2D >		mpControlVector;

    // buffered data for e.g. default subdivision and range
    boost::scoped_ptr< ImplBufferedData >           mpBufferedData;

	// flag which decides if this polygon is opened or closed
	bool                                            mbIsClosed;

public:
	const basegfx::B2DPolygon& getDefaultAdaptiveSubdivision(const basegfx::B2DPolygon& rSource) const
	{
		if(!mpControlVector || !mpControlVector->isUsed())
        {
    		return rSource;
        }

        if(!mpBufferedData)
        {
			const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
        }

        return mpBufferedData->getDefaultAdaptiveSubdivision(rSource);
	}

	const basegfx::B2DRange& getB2DRange(const basegfx::B2DPolygon& rSource) const
    {
        if(!mpBufferedData)
        {
			const_cast< ImplB2DPolygon* >(this)->mpBufferedData.reset(new ImplBufferedData);
        }

        return mpBufferedData->getB2DRange(rSource);
    }

	ImplB2DPolygon()
	:	maPoints(0),
		mpControlVector(),
		mpBufferedData(),
        mbIsClosed(false)
	{}

	ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied)
	:	maPoints(rToBeCopied.maPoints),
		mpControlVector(),
		mpBufferedData(),
		mbIsClosed(rToBeCopied.mbIsClosed)
	{
		// complete initialization using copy
		if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
        {
			mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );
        }
	}

	ImplB2DPolygon(const ImplB2DPolygon& rToBeCopied, sal_uInt32 nIndex, sal_uInt32 nCount)
	:	maPoints(rToBeCopied.maPoints, nIndex, nCount),
		mpControlVector(),
		mpBufferedData(),
		mbIsClosed(rToBeCopied.mbIsClosed)
	{
		// complete initialization using partly copy
		if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
		{
			mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector, nIndex, nCount) );

			if(!mpControlVector->isUsed())
                mpControlVector.reset();
		}
	}

    ImplB2DPolygon& operator=( const ImplB2DPolygon& rToBeCopied )
    {
		maPoints = rToBeCopied.maPoints;
		mpControlVector.reset();
		mpBufferedData.reset();
		mbIsClosed = rToBeCopied.mbIsClosed;

		// complete initialization using copy
		if(rToBeCopied.mpControlVector && rToBeCopied.mpControlVector->isUsed())
			mpControlVector.reset( new ControlVectorArray2D(*rToBeCopied.mpControlVector) );        

        return *this;
    }

	sal_uInt32 count() const
	{
		return maPoints.count();
	}

	bool isClosed() const
	{
		return mbIsClosed;
	}

	void setClosed(bool bNew)
	{
		if(bNew != mbIsClosed)
		{
			mpBufferedData.reset();
			mbIsClosed = bNew;
		}
	}

	bool operator==(const ImplB2DPolygon& rCandidate) const
	{
		if(mbIsClosed == rCandidate.mbIsClosed)
		{
			if(maPoints == rCandidate.maPoints)
			{
				bool bControlVectorsAreEqual(true);

				if(mpControlVector)
				{
					if(rCandidate.mpControlVector)
					{
						bControlVectorsAreEqual = ((*mpControlVector) == (*rCandidate.mpControlVector));
					}
					else
					{
						// candidate has no control vector, so it's assumed all unused.
						bControlVectorsAreEqual = !mpControlVector->isUsed();
					}
				}
				else
				{
					if(rCandidate.mpControlVector)
					{
						// we have no control vector, so it's assumed all unused.
						bControlVectorsAreEqual = !rCandidate.mpControlVector->isUsed();
					}
				}

				if(bControlVectorsAreEqual)
				{
					return true;
				}
			}
		}

		return false;
	}

	const basegfx::B2DPoint& getPoint(sal_uInt32 nIndex) const
	{
		return maPoints.getCoordinate(nIndex);
	}

	void setPoint(sal_uInt32 nIndex, const basegfx::B2DPoint& rValue)
	{
		mpBufferedData.reset();
		maPoints.setCoordinate(nIndex, rValue);
	}

	void reserve(sal_uInt32 nCount)
	{
		maPoints.reserve(nCount);
	}

	void append(const basegfx::B2DPoint& rPoint)
	{
		mpBufferedData.reset(); // TODO: is this needed?
		const CoordinateData2D aCoordinate(rPoint);
		maPoints.append(aCoordinate);

		if(mpControlVector)
		{
			const ControlVectorPair2D aVectorPair;
			mpControlVector->append(aVectorPair);
		}
	}

	void insert(sal_uInt32 nIndex, const basegfx::B2DPoint& rPoint, sal_uInt32 nCount)
	{
		if(nCount)
		{
			mpBufferedData.reset();
			CoordinateData2D aCoordinate(rPoint);
			maPoints.insert(nIndex, aCoordinate, nCount);

			if(mpControlVector)
			{
				ControlVectorPair2D aVectorPair;
				mpControlVector->insert(nIndex, aVectorPair, nCount);
			}
		}
	}

	const basegfx::B2DVector& getPrevControlVector(sal_uInt32 nIndex) const
	{
		if(mpControlVector)
		{
			return mpControlVector->getPrevVector(nIndex);
		}
		else
		{
			return basegfx::B2DVector::getEmptyVector();
		}
	}

	void setPrevControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
	{
		if(!mpControlVector)
		{
			if(!rValue.equalZero())
			{
				mpBufferedData.reset();
				mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
				mpControlVector->setPrevVector(nIndex, rValue);
			}
		}
		else
		{
			mpBufferedData.reset();
			mpControlVector->setPrevVector(nIndex, rValue);

			if(!mpControlVector->isUsed())
                mpControlVector.reset();
		}
	}

	const basegfx::B2DVector& getNextControlVector(sal_uInt32 nIndex) const
	{
		if(mpControlVector)
		{
			return mpControlVector->getNextVector(nIndex);
		}
		else
		{
			return basegfx::B2DVector::getEmptyVector();
		}
	}

	void setNextControlVector(sal_uInt32 nIndex, const basegfx::B2DVector& rValue)
	{
		if(!mpControlVector)
		{
			if(!rValue.equalZero())
			{
				mpBufferedData.reset();
				mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
				mpControlVector->setNextVector(nIndex, rValue);
			}
		}
		else
		{
			mpBufferedData.reset();
			mpControlVector->setNextVector(nIndex, rValue);

			if(!mpControlVector->isUsed())
                mpControlVector.reset();
		}
	}

	bool areControlPointsUsed() const
	{
		return (mpControlVector && mpControlVector->isUsed());
	}

	void resetControlVectors(sal_uInt32 nIndex)
	{
		setPrevControlVector(nIndex, basegfx::B2DVector::getEmptyVector());
		setNextControlVector(nIndex, basegfx::B2DVector::getEmptyVector());
	}

	void resetControlVectors()
	{
		mpBufferedData.reset();
		mpControlVector.reset();
	}

	void setControlVectors(sal_uInt32 nIndex, const basegfx::B2DVector& rPrev, const basegfx::B2DVector& rNext)
	{
		setPrevControlVector(nIndex, rPrev);
		setNextControlVector(nIndex, rNext);
	}

	void appendBezierSegment(const basegfx::B2DVector& rNext, const basegfx::B2DVector& rPrev, const basegfx::B2DPoint& rPoint)
	{
		mpBufferedData.reset();
		const sal_uInt32 nCount(maPoints.count());

        if(nCount)
		{
			setNextControlVector(nCount - 1, rNext);
		}

		insert(nCount, rPoint, 1);
		setPrevControlVector(nCount, rPrev);
	}

	void insert(sal_uInt32 nIndex, const ImplB2DPolygon& rSource)
	{
		const sal_uInt32 nCount(rSource.maPoints.count());

		if(nCount)
		{
			mpBufferedData.reset();

			if(rSource.mpControlVector && rSource.mpControlVector->isUsed() && !mpControlVector)
			{
				mpControlVector.reset( new ControlVectorArray2D(maPoints.count()) );
			}

			maPoints.insert(nIndex, rSource.maPoints);

			if(rSource.mpControlVector)
			{
				mpControlVector->insert(nIndex, *rSource.mpControlVector);

				if(!mpControlVector->isUsed())
                    mpControlVector.reset();
			}
			else if(mpControlVector)
			{
				ControlVectorPair2D aVectorPair;
				mpControlVector->insert(nIndex, aVectorPair, nCount);
			}
		}
	}

	void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
	{
		if(nCount)
		{
			mpBufferedData.reset();
			maPoints.remove(nIndex, nCount);

			if(mpControlVector)
			{
				mpControlVector->remove(nIndex, nCount);

				if(!mpControlVector->isUsed())
                    mpControlVector.reset();
			}
		}
	}

	void flip()
	{
		if(maPoints.count() > 1)
		{
			mpBufferedData.reset();

			// flip points
			maPoints.flip(mbIsClosed);

			if(mpControlVector)
			{
				// flip control vector
				mpControlVector->flip(mbIsClosed);
			}
		}
	}

	bool hasDoublePoints() const
	{
		if(mbIsClosed)
		{
			// check for same start and end point
			const sal_uInt32 nIndex(maPoints.count() - 1);

			if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
			{
				if(mpControlVector)
				{
					if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
					{
						return true;
					}
				}
				else
				{
					return true;
				}
			}
		}

		// test for range
		for(sal_uInt32 a(0); a < maPoints.count() - 1; a++)
		{
			if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1))
			{
				if(mpControlVector)
				{
					if(mpControlVector->getNextVector(a).equalZero() && mpControlVector->getPrevVector(a + 1).equalZero())
					{
						return true;
					}
				}
				else
				{
					return true;
				}
			}
		}

		return false;
	}

	void removeDoublePointsAtBeginEnd()
	{
		// Only remove DoublePoints at Begin and End when poly is closed
		if(mbIsClosed)
		{
			mpBufferedData.reset();

            if(mpControlVector)
			{
				bool bRemove;

				do
				{
					bRemove = false;

					if(maPoints.count() > 1)
					{
						const sal_uInt32 nIndex(maPoints.count() - 1);

						if(maPoints.getCoordinate(0) == maPoints.getCoordinate(nIndex))
						{
							if(mpControlVector)
							{
								if(mpControlVector->getNextVector(nIndex).equalZero() && mpControlVector->getPrevVector(0).equalZero())
								{
									bRemove = true;
								}
							}
							else
							{
								bRemove = true;
							}
						}
					}

					if(bRemove)
					{
						const sal_uInt32 nIndex(maPoints.count() - 1);

						if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
						{
							mpControlVector->setPrevVector(0, mpControlVector->getPrevVector(nIndex));
						}

						remove(nIndex, 1);
					}
				} 
				while(bRemove);
			}
			else
			{
				maPoints.removeDoublePointsAtBeginEnd();
			}
		}
	}

	void removeDoublePointsWholeTrack()
	{
		mpBufferedData.reset();

        if(mpControlVector)
		{
			sal_uInt32 nIndex(0);

			// test as long as there are at least two points and as long as the index
			// is smaller or equal second last point
			while((maPoints.count() > 1) && (nIndex <= maPoints.count() - 2))
			{
				bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nIndex + 1));

				if(bRemove)
				{
					if(mpControlVector)
					{
						if(!mpControlVector->getNextVector(nIndex).equalZero() || !mpControlVector->getPrevVector(nIndex + 1).equalZero())
						{
							bRemove = false;
						}
					}
				}

				if(bRemove)
				{
					if(mpControlVector && !mpControlVector->getPrevVector(nIndex).equalZero())
					{
						mpControlVector->setPrevVector(nIndex + 1, mpControlVector->getPrevVector(nIndex));
					}

					// if next is same as index and the control vectors are unused, delete index
					remove(nIndex, 1);
				}
				else
				{
					// if different, step forward
					nIndex++;
				}
			}
		}
		else
		{
			maPoints.removeDoublePointsWholeTrack();
		}
	}

	void transform(const basegfx::B2DHomMatrix& rMatrix)
	{
		mpBufferedData.reset();

        if(mpControlVector)
		{
			for(sal_uInt32 a(0); a < maPoints.count(); a++)
			{
				basegfx::B2DPoint aCandidate = maPoints.getCoordinate(a);

				if(mpControlVector->isUsed())
				{
					const basegfx::B2DVector& rPrevVector(mpControlVector->getPrevVector(a));
					const basegfx::B2DVector& rNextVector(mpControlVector->getNextVector(a));
					
					if(!rPrevVector.equalZero())
					{
						basegfx::B2DVector aPrevVector(rMatrix * rPrevVector);
						mpControlVector->setPrevVector(a, aPrevVector);
					}

					if(!rNextVector.equalZero())
					{
						basegfx::B2DVector aNextVector(rMatrix * rNextVector);
						mpControlVector->setNextVector(a, aNextVector);
					}
				}

				aCandidate *= rMatrix;
				maPoints.setCoordinate(a, aCandidate);
			}

			if(!mpControlVector->isUsed())
                mpControlVector.reset();
		}
		else
		{
			maPoints.transform(rMatrix);
		}
	}

    const basegfx::B2DPoint* begin() const
    {
        return maPoints.begin();
    }

    const basegfx::B2DPoint* end() const
    {
        return maPoints.end();
    }

    basegfx::B2DPoint* begin()
    {
       mpBufferedData.reset();
       return maPoints.begin();
    }

    basegfx::B2DPoint* end()
    {
        mpBufferedData.reset();
        return maPoints.end();
    }
};

//////////////////////////////////////////////////////////////////////////////

namespace basegfx
{
    namespace 
	{ 
		struct DefaultPolygon: public rtl::Static<B2DPolygon::ImplType, DefaultPolygon> {}; 
	}

	B2DPolygon::B2DPolygon() 
	:	mpPolygon(DefaultPolygon::get())
	{}

	B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon)
	:	mpPolygon(rPolygon.mpPolygon)
	{}

	B2DPolygon::B2DPolygon(const B2DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount)
	:	mpPolygon(ImplB2DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
	{
        // TODO(P2): one extra temporary here (cow_wrapper copies
        // given ImplB2DPolygon into its internal impl_t wrapper type)
		OSL_ENSURE(nIndex + nCount <= rPolygon.mpPolygon->count(), "B2DPolygon constructor outside range (!)");
	}

	B2DPolygon::~B2DPolygon()
	{
	}

	B2DPolygon& B2DPolygon::operator=(const B2DPolygon& rPolygon)
	{
		mpPolygon = rPolygon.mpPolygon;
		return *this;
	}

    void B2DPolygon::makeUnique()
    {
        mpPolygon.make_unique();
    }

	bool B2DPolygon::operator==(const B2DPolygon& rPolygon) const
	{
		if(mpPolygon.same_object(rPolygon.mpPolygon))
			return true;

		return ((*mpPolygon) == (*rPolygon.mpPolygon));
	}

	bool B2DPolygon::operator!=(const B2DPolygon& rPolygon) const
	{
        return !(*this == rPolygon);
	}

	sal_uInt32 B2DPolygon::count() const
	{
		return mpPolygon->count();
	}

    B2DPoint B2DPolygon::getB2DPoint(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		
		return mpPolygon->getPoint(nIndex);
	}

	void B2DPolygon::setB2DPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		if(mpPolygon->getPoint(nIndex) != rValue)
		{
			mpPolygon->setPoint(nIndex, rValue);
		}
	}

	void B2DPolygon::reserve(sal_uInt32 nCount)
	{
		mpPolygon->reserve(nCount);
	}

	void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPoint& rPoint, sal_uInt32 nCount)
	{
		OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");
		
		if(nCount)
		{
			mpPolygon->insert(nIndex, rPoint, nCount);
		}
	}

	void B2DPolygon::append(const B2DPoint& rPoint, sal_uInt32 nCount)
	{
		if(nCount)
		{
			mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
		}
	}

	void B2DPolygon::append(const B2DPoint& rPoint)
	{
		mpPolygon->append(rPoint);
	}

	B2DPoint B2DPolygon::getPrevControlPoint(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		
		if(mpPolygon->areControlPointsUsed())
		{
			return mpPolygon->getPoint(nIndex) + mpPolygon->getPrevControlVector(nIndex);
		}
		else
		{
			return mpPolygon->getPoint(nIndex);
		}
	}

	B2DPoint B2DPolygon::getNextControlPoint(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		
		if(mpPolygon->areControlPointsUsed())
		{
			return mpPolygon->getPoint(nIndex) + mpPolygon->getNextControlVector(nIndex);
		}
		else
		{
			return mpPolygon->getPoint(nIndex);
		}
	}

	void B2DPolygon::setPrevControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));

		if(mpPolygon->getPrevControlVector(nIndex) != aNewVector)
		{
			mpPolygon->setPrevControlVector(nIndex, aNewVector);
		}
	}

	void B2DPolygon::setNextControlPoint(sal_uInt32 nIndex, const B2DPoint& rValue)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		const basegfx::B2DVector aNewVector(rValue - mpPolygon->getPoint(nIndex));

		if(mpPolygon->getNextControlVector(nIndex) != aNewVector)
		{
			mpPolygon->setNextControlVector(nIndex, aNewVector);
		}
	}

	void B2DPolygon::setControlPoints(sal_uInt32 nIndex, const basegfx::B2DPoint& rPrev, const basegfx::B2DPoint& rNext)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
		const basegfx::B2DVector aNewPrev(rPrev - aPoint);
		const basegfx::B2DVector aNewNext(rNext - aPoint);

		if(mpPolygon->getPrevControlVector(nIndex) != aNewPrev || mpPolygon->getNextControlVector(nIndex) != aNewNext)
		{
			mpPolygon->setControlVectors(nIndex, aNewPrev, aNewNext);
		}
	}

	void B2DPolygon::resetPrevControlPoint(sal_uInt32 nIndex)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		if(mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero())
		{
			mpPolygon->setPrevControlVector(nIndex, B2DVector::getEmptyVector());
		}
	}

	void B2DPolygon::resetNextControlPoint(sal_uInt32 nIndex)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		if(mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero())
		{
			mpPolygon->setNextControlVector(nIndex, B2DVector::getEmptyVector());
		}
	}

	void B2DPolygon::resetControlPoints(sal_uInt32 nIndex)
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		if(mpPolygon->areControlPointsUsed() && 
			(!mpPolygon->getPrevControlVector(nIndex).equalZero() || !mpPolygon->getNextControlVector(nIndex).equalZero()))
		{
			mpPolygon->resetControlVectors(nIndex);
		}
	}

	void B2DPolygon::resetControlPoints()
	{
		if(mpPolygon->areControlPointsUsed())
		{
			mpPolygon->resetControlVectors();
		}
	}

	void B2DPolygon::appendBezierSegment(
		const B2DPoint& rNextControlPoint, 
		const B2DPoint& rPrevControlPoint, 
		const B2DPoint& rPoint)
	{
		const B2DVector aNewNextVector(mpPolygon->count() ? B2DVector(rNextControlPoint - mpPolygon->getPoint(mpPolygon->count() - 1)) : B2DVector::getEmptyVector());
		const B2DVector aNewPrevVector(rPrevControlPoint - rPoint);

		if(aNewNextVector.equalZero() && aNewPrevVector.equalZero())
		{
			mpPolygon->insert(mpPolygon->count(), rPoint, 1);
		}
		else
		{
			mpPolygon->appendBezierSegment(aNewNextVector, aNewPrevVector, rPoint);
		}
	}

	bool B2DPolygon::areControlPointsUsed() const
	{
		return mpPolygon->areControlPointsUsed();
	}

	bool B2DPolygon::isPrevControlPointUsed(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		return (mpPolygon->areControlPointsUsed() && !mpPolygon->getPrevControlVector(nIndex).equalZero());
	}

	bool B2DPolygon::isNextControlPointUsed(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		return (mpPolygon->areControlPointsUsed() && !mpPolygon->getNextControlVector(nIndex).equalZero());
	}

	B2VectorContinuity B2DPolygon::getContinuityInPoint(sal_uInt32 nIndex) const
	{
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");

		if(mpPolygon->areControlPointsUsed())
		{
			const B2DVector& rPrev(mpPolygon->getPrevControlVector(nIndex));
			const B2DVector& rNext(mpPolygon->getNextControlVector(nIndex));

			return getContinuity(rPrev, rNext);
		}
		else
		{
			return CONTINUITY_NONE;
		}
	}

    bool B2DPolygon::isBezierSegment(sal_uInt32 nIndex) const
    {
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
		
        if(mpPolygon->areControlPointsUsed())
		{
            // Check if the edge exists
            const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());

            if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
            {
                const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
                return (!mpPolygon->getPrevControlVector(nNextIndex).equalZero() 
                    || !mpPolygon->getNextControlVector(nIndex).equalZero());
            }
            else
            {
                // no valid edge -> no bezier segment, even when local next
                // vector may be used
                return false;
            }
        }
        else
        {
            // no control points -> no bezier segment
            return false;
        }
    }

    void B2DPolygon::getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier& rTarget) const
    {
		OSL_ENSURE(nIndex < mpPolygon->count(), "B2DPolygon access outside range (!)");
        const bool bNextIndexValidWithoutClose(nIndex + 1 < mpPolygon->count());

        if(bNextIndexValidWithoutClose || mpPolygon->isClosed())
        {
            const sal_uInt32 nNextIndex(bNextIndexValidWithoutClose ? nIndex + 1 : 0);
            rTarget.setStartPoint(mpPolygon->getPoint(nIndex));
            rTarget.setEndPoint(mpPolygon->getPoint(nNextIndex));

            if(mpPolygon->areControlPointsUsed())
            {
                rTarget.setControlPointA(rTarget.getStartPoint() + mpPolygon->getNextControlVector(nIndex));
                rTarget.setControlPointB(rTarget.getEndPoint() + mpPolygon->getPrevControlVector(nNextIndex));
            }
            else
            {
                // no bezier, reset control poins at rTarget
                rTarget.setControlPointA(rTarget.getStartPoint());
                rTarget.setControlPointB(rTarget.getEndPoint());
            }
        }
        else
        {
            // no valid edge at all, reset rTarget to current point
    		const B2DPoint aPoint(mpPolygon->getPoint(nIndex));
            rTarget.setStartPoint(aPoint);
            rTarget.setEndPoint(aPoint);
            rTarget.setControlPointA(aPoint);
            rTarget.setControlPointB(aPoint);
        }
    }

    B2DPolygon B2DPolygon::getDefaultAdaptiveSubdivision() const
	{
		return mpPolygon->getDefaultAdaptiveSubdivision(*this);
	}

    B2DRange B2DPolygon::getB2DRange() const
	{
		return mpPolygon->getB2DRange(*this);
	}

	void B2DPolygon::insert(sal_uInt32 nIndex, const B2DPolygon& rPoly, sal_uInt32 nIndex2, sal_uInt32 nCount)
	{
		OSL_ENSURE(nIndex <= mpPolygon->count(), "B2DPolygon Insert outside range (!)");

		if(rPoly.count())
		{
			if(!nCount)
			{
				nCount = rPoly.count();
			}

			if(0 == nIndex2 && nCount == rPoly.count())
			{
				mpPolygon->insert(nIndex, *rPoly.mpPolygon);
			}
			else
			{
				OSL_ENSURE(nIndex2 + nCount <= rPoly.mpPolygon->count(), "B2DPolygon Insert outside range (!)");
				ImplB2DPolygon aTempPoly(*rPoly.mpPolygon, nIndex2, nCount);
				mpPolygon->insert(nIndex, aTempPoly);
			}
		}
	}

	void B2DPolygon::append(const B2DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
	{
		if(rPoly.count())
		{
			if(!nCount)
			{
				nCount = rPoly.count();
			}

			if(0 == nIndex && nCount == rPoly.count())
			{
				mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
			}
			else
			{
				OSL_ENSURE(nIndex + nCount <= rPoly.mpPolygon->count(), "B2DPolygon Append outside range (!)");
				ImplB2DPolygon aTempPoly(*rPoly.mpPolygon, nIndex, nCount);
				mpPolygon->insert(mpPolygon->count(), aTempPoly);
			}
		}
	}

	void B2DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
	{
		OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B2DPolygon Remove outside range (!)");
		
		if(nCount)
		{
			mpPolygon->remove(nIndex, nCount);
		}
	}
	
	void B2DPolygon::clear()
	{
		mpPolygon = DefaultPolygon::get();
	}

	bool B2DPolygon::isClosed() const
	{
		return mpPolygon->isClosed();
	}

	void B2DPolygon::setClosed(bool bNew)
	{
		if(isClosed() != bNew)
		{
			mpPolygon->setClosed(bNew);
		}
	}

	void B2DPolygon::flip()
	{
		if(count() > 1)
		{
			mpPolygon->flip();
		}
	}

	bool B2DPolygon::hasDoublePoints() const
	{
		return (mpPolygon->count() > 1 && mpPolygon->hasDoublePoints());
	}

	void B2DPolygon::removeDoublePoints()
	{
		if(hasDoublePoints())
		{
			mpPolygon->removeDoublePointsAtBeginEnd();
			mpPolygon->removeDoublePointsWholeTrack();
		}
	}

	void B2DPolygon::transform(const B2DHomMatrix& rMatrix)
	{
		if(mpPolygon->count() && !rMatrix.isIdentity())
		{
			mpPolygon->transform(rMatrix);
		}
	}

    const B2DPoint* B2DPolygon::begin() const
    {
        return mpPolygon->begin();
    }

    const B2DPoint* B2DPolygon::end() const
    {
        return mpPolygon->end();
    }

    B2DPoint* B2DPolygon::begin()
    {
        return mpPolygon->begin();
    }

    B2DPoint* B2DPolygon::end()
    {
        return mpPolygon->end();
    }
} // end of namespace basegfx

//////////////////////////////////////////////////////////////////////////////
// eof
