//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#pragma once

namespace VWFC
{
	namespace Math
	{
		using namespace VectorWorks::Math;

		// ------------------------------------------------------------------------------------
		class VWPolygon2D
		{
		public:
							VWPolygon2D();
							VWPolygon2D(const TSTLPoints2DArray& src);
							VWPolygon2D(const TSTLPoints2DVWArray& src);
							VWPolygon2D(IPolyDefPtr pPolygonDef);
							VWPolygon2D(const VWPolygon2D& src);
			virtual			~VWPolygon2D();

			VWPolygon2D&	operator=(const TSTLPoints2DArray& src);
			VWPolygon2D&	operator=(const TSTLPoints2DVWArray& src);
			VWPolygon2D&	operator=(const VWPolygon2D& src);
			VWPolygon2D&	operator=(IPolyDefPtr pPolygonDef);

		// operations
		public:
			// setting polygon
			void			SetPolygon(const TSTLPoints2DArray& src);
			void			SetPolygon(const TSTLPoints2DVWArray& src);
			void			SetPolygon(IPolyDefPtr pPolygonDef);
			void			GetPolygon(TSTLPoints2DArray& out) const;
			void			GetPolygon(TSTLPoints2DVWArray& out) const;
			void			GetPolygon(IPolyDefPtr pPolygonDef) const;

			// vertex operations
			void			ClearVertices();
			void			ClearVertex(size_t at);
			void			AddVertex(double x, double y, EVertexType type=eVertexType_Corner, double dArcRadius=0);
			void			AddVertex(const VWPoint2D& pt, EVertexType type=eVertexType_Corner, double dArcRadius=0);
			void			InsertVertex(size_t at, double x, double y, EVertexType type=eVertexType_Corner, double dArcRadius=0);
			void			InsertVertex(size_t at, const VWPoint2D& pt, EVertexType type=eVertexType_Corner, double dArcRadius=0);

			size_t			GetVertexCount() const;
			bool			IsEmpty() const;

			VWPoint2D&		GetVertexAt(size_t at, bool bLoopVerts=false);
			const VWPoint2D& GetVertexAt(size_t at, bool bLoopVerts=false) const;
			EVertexType		GetVertexType(size_t at) const;
			void			SetVertexType(size_t at, EVertexType type);
			double			GetArcVertexRadius(size_t at) const;
			void			SetArcVertexRadius(size_t at, double dRadius);
			bool			GetVertexVisible(size_t at) const;
			void			SetVertexVisible(size_t at, bool bVisible);
			// return a line from (at, at+1) vertex (at the end the line is (len-1, 0)
			VWLine2D		GetLineAt(size_t at) const;

			// modify closed flag
			void			SetClosed(bool isClosed);
			bool			IsClosed() const;
			void			SetAllVertexVisible(bool bVisible);
			
			// get bounding box of this polygon
			void			GetPolygonBounds(double& outMinX, double& outMaxX, double& outMinY, double& outMaxY) const;
			VWRectangle2D	GetPolygonBounds() const;
			VWPoint2D		GetPolygonBoundsCenter() const;
			// get the bouding box which is parallel to given line
			// the result polygon contains 4 vertices of the bounding box
			void			GetPolygonBounds(const VWLine2D& line, VWPolygon2D& outBounds) const;

			// get the bounding box which is parallel to the given direction (a similar approach).
			// the result is 4 vertices (UL, UR, LL, LR) of the bounding box
			// XXX Bobi: in contrast to the previous, this one first polygonizes 
			// the polygon and then gets the bounding box. It works for arbitrary polylines.
			void			GetPolygonBounds(const VWPoint2D& vDirection, VWPoint2D& vUL, VWPoint2D& vUR, VWPoint2D& vLL, VWPoint2D& vLR) const;


			// reverse the order of the points (if CW to CCW or CCW to CW)
			void			ReversePoints(const bool& kbDoFullReversion = false);

			// determine if the polygon is Clock Wise oriented
			bool			IsClockWise() const;

			// return true if the passed polygon has more than 3 different vertices
			bool			IsValidPolygon(double dEpsilon) const;

			// removed all same points in the polygon
			void			CleanUpPolygon(double dEpsilon);

		// mathematics
		public:
			// transform the polygon with specified matrix
			void			Transform(const VWTransformMatrix& mat);
			// move the polygon by the given values
			void			MovePolygon(double cx, double cy);
			// rotate the polygon by the center of the bounding box
			void			Rotate(double degAngle);
			// rotate by specified center
			void			Rotate(const VWPoint2D& pt, double degAngle);

			// calculate the area of the polygon
			// positive if polygon is CCW -- negative if CW
			double			GetArea() const;
			// calculate the perimeter of the polygon
			double			GetPerimeter() const;

			// check if it is an equalish polygon
			bool			EqualishTo(const VWPolygon2D& poly) const;

			// is specified point on the edge of the specified polygon
			bool			IsPtOnPoly2D(double ptX, double ptY, double dEpsilon) const;
			bool			IsPtOnPoly2D(const VWPoint2D& pt, double dEpsilon) const;

			// determines if a point is in the area of a given polygon (the 2D projection)
			bool			IsPtInPoly2D(double ptX, double ptY, double dEpsilon) const;
			bool			IsPtInPoly2D(const VWPoint2D& pt, double dEpsilon) const;

			// combined operations
			bool			IsPtInOrOnPoly2D(double ptX, double ptY, double dEpsilon) const;
			bool			IsPtInOrOnPoly2D(const VWPoint2D& pt, double dEpsilon) const;

			// determines if a points is inside and if it is on the poly
			void			IsPtOnPoly2D(double ptX, double ptY, bool& outOnPoly, bool& outInPoly, double dEpsilon) const;
			void			IsPtOnPoly2D(const VWPoint2D& pt, bool& outOnPoly, bool& outInPoly, double dEpsilon) const;

			// return true if the specified line intersects the poly
			bool			IsIntersecting(const VWLine2D& line, double dEpsilon, VWPoint2D* /*out*/ pInsertPt=NULL ) const;
			bool			IsIntersecting(const VWLine2D& line, double dEpsilon, TSTLPoints2DArray& outArrIntersectPts ) const;
			bool			IsIntersecting(const VWPoint2D& a, const VWPoint2D& b, double dEpsilon, VWPoint2D* /*out*/ pInsertPt=NULL ) const;
			bool			IsIntersecting(const VWPoint2D& a, const VWPoint2D& b, double dEpsilon, TSTLPoints2DArray& outArrIntersectPts ) const;

			// return true if the given polygon and this polygon intersects each other
			// intersect 'true' means that they have intersection points or same points
			// 'outHaveFullyOutsideVerts' true when the specified 'poly' have fully outside vertices from this poly
			bool			IsIntersecting(const VWPolygon2D& poly, double dEpsilon) const;

			// returns true if the given polygon is fully within this polygon
			bool			IsFullyInside(const VWPolygon2D& poly, double dEpsilon) const;
			// returns true if the given polygon is fully outside this polygon
			bool			IsFullyOutside(const VWPolygon2D& poly, double dEpsilon) const;

			// returns true if the given polygon is within this polygon, including having common points
			bool			IsInside(const VWPolygon2D& poly, double dEpsilon) const;
			// returns true if the given polygon is fully outside this polygon, including having common points
			bool			IsOutside(const VWPolygon2D& poly, double dEpsilon) const;
			
			// get geometry center of the polygon
			VWPoint2D		GetCenter() const;
			// get random point that lies in the poly
			VWPoint2D		GetRandomPtInPoly(double dEpsilon) const;

			// get bounding points along line
			// the result points are projection on the line of the two points from the poly wich are bound points along the line
			void			GetBoundingProjOnLine(VWPoint2D&/*in-out*/ minProjPt, VWPoint2D&/*in-out*/ maxProjPt, double dEpsilon) const;

			// get an inside point near the vertex at
			VWPoint2D		GetInsidePtNearVertexAt(size_t at);
			// get set of inside points near polygon's vertices
			TSTLPoints2DArray	GetInsideNearPts();

			// transform polygon's points
			void			PointsTransform(const VWTransformMatrix& matrix);
			void			InversePointsTransform(const VWTransformMatrix& matrix);

		// Math tools
		public:
			// is pt (x0,y0) is inside or/and on the specified triangle (x1,y1):(x2,y2):(x3,y3)
			static void		IsPtInTriangle(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, bool& outInTri, bool& outOnTri);

		// internal funcs
		protected:
			void			RecalculateBoundingBox();

		protected:
			struct SVertex {
				EVertexType			type;
				VWPoint2D			pt;
				double				dArcRadius;
				bool				bVisible;
			};
			typedef	std::vector<SVertex>	TVertexArray;

			double									fdMinX;
			double									fdMaxX;
			double									fdMinY;
			double									fdMaxY;
			TVertexArray							farrVertices;
			bool									fbIsClosed;
		};

		// array of polygon defintion
		typedef		std::vector<VWPolygon2D>									TSTLPolygons2DArray;
		typedef		std::vector<VWPolygon2D, std::vwallocator<VWPolygon2D> >	TSTLPolygons2DVWArray;
	}
}