/******************************************************************************  		
 * Company:         SINED GmbH
 * Author:          Andriy Kurhanevych
 * Created:         4.10.2004
 * Last modified:   $Modtime: 01.03.13 21:35 $
 * Purpose:         Declaration of the Polygon class
 ******************************************************************************/

#pragma once 
#ifndef POLYGONLIBRARY_POLYGON_H
#define POLYGONLIBRARY_POLYGON_H

namespace PolygonLibrary
{
	class Point;
	class Rectangle;
	class RingEnumerator;
	class Ring;
	class PolygonEnumerator;
	class PolygonLayer;

	//////////////////////////////////////////////////////////////////////////
	/// Represents a bounded closed set of points of a two-dimensional plane,
	/// enclosed by one or many exterior boundary loops (exterior rings) 
	/// and zero or many interior boundary loops (interior rings defining holes).
	/// 
	/// Exterior rings have clockwise orientation 
	/// while interior rings are oriented counterclockwise.
	///	
	/// The Polygon is valid if:
	/// - All its Rings are valid (enclose not zero areas and are simple (not self-intersecting))
	/// - Each interior ring is directly enclosed by an exterior ring (that there is no other ring 
	///   enclosing the interior ring and enclosed by the exterior ring).
	/// - The areas enclosed by exterior rings do not overlap, excepting when an exterior ring 
	///   is directly enclosed by an interior ring directly enclosed by an other exterior ring.
	/// - The areas enclosed by interior rings directly enclosed by an exterior ring do not overlap.
	//////////////////////////////////////////////////////////////////////////
	class Polygon
	{
		
	// ------------------------------ Interface -------------------------------
	public:
		
		/// Returns pointer to the PolygonLayer this Polygon belongs to.
		PolygonLayer* PolygonLayer() const;
		
		// --- Parents collection ---
		
		/// Returns an enumerator of the Polygons that this Polygon was created from 
		/// per a polygons operation and overlaps them. 
		/// The collection is not empty for results of operations between polygons 
		/// and can be cleared by ClearParents().
		PolygonEnumerator Parents() const;

		/// Clear the Parents collection (pointers to Polygons that this Polygon was created from 
		/// per a polygons operation and overlaps them).
		/// The clear is required to allow change of the parent polygons. 
		void ClearParents();
		
		// ---- Ring collection ----
		
		/// Returns an enumerator of all Rings of the Polygon.
		RingEnumerator Rings() const;

		/// Returns an enumerator of the exterior Rings of the Polygon.
		RingEnumerator ExteriorRings() const;

		/// Returns an enumerator of interior Rings, directly enclosed by the specified exterior Ring 
		/// of the Polygon (that there is no other ring enclosing an interior ring and enclosed by the exterior ring).
		/// @exception Exceptions::ArgumentException - if the Ring is not an exterior Ring of this Polygon  
		RingEnumerator InteriorRings(const Ring* pExteriorRing) const;
		
		/// Creates a new Ring and adds it to this Polygon. 
		/// Returns pointer to the created Ring.
		/// @exception Exceptions::InvalidOperationException - if the Polygon is a parent (IsParent() == true).
		Ring* CreateRing();

		/// Deletes the specified Ring of this Polygon. 
		/// @exception Exceptions::ArgumentException - if the Ring does not belong to this Polygon 
		/// @exception Exceptions::InvalidOperationException - if the Polygon is a parent (IsParent() == true).
		void  DeleteRing(Ring* pRing);

		// --- Other attributes ---

		/// Returns the bounding box of the polygon.  
		Rectangle Bounds() const;
		
		/// Returns the centroid of the polygon.
		/// @exception InvalidPolygonException - if the Polygon is not valid
		Point     Centroid() const;
		
		/// Returns the area of the polygon.   
		double    Area() const;

		/// Returns the perimeter of the polygon boundary (interior rings are considered too).   
		double    Perimeter() const;	
		
		/// Returns a value indicating if the polygon is empty (contains no point).
		bool      IsEmpty() const;

		/// Returns a value indicating if the polygon is convex.
		bool      IsConvex() const;

		/// Returns a value indicating if the polygon is valid.
		bool      IsValid() const;

		/// Indicates if this Polygon is contained in the Parents collection of an other Polygon.
		/// The Polygon cannot be changed if it is a parent.
		bool      IsParent() const;

		// --- Operations ---
		
		/// Determines whether the polygon contains the specified point.
		bool Contains(const Point& point) const;

		/// Determines whether the polygon overlaps the specified polygon 
		/// (their intersection is a polygon with not zero area).
		/// @exception Exceptions::ArgumentException - if the specified pointer is null 
		bool Overlaps(const Polygon* pPolygon) const;

		/// Determines whether the polygon is equal to the specified polygon with the specified 
		/// absolute accuracy of coordinates.
		/// @exception Exceptions::ArgumentException - if the specified pointer is null
		bool Equals(const Polygon* pPolygon, const double dAccuracy) const;

		/// Gets the polygon centroid, if it is contained in the polygon; 
		/// otherwise, gets the point of the polygon boundary that is closest to the centroid.
		/// @exception InvalidPolygonException - if the Polygon is not valid
		Point GetPointClosestToCentroid() const;

		/// Locates the edge closest to the specified point 
		/// and being within the specified tolerance distance from the point.
		/// Stores the search results at the specified addresses: 
		///  - ppRing     :	pointer to the Ring containing the located edge, or NULL if no edge intersects the circular area
		///  - pEdgeIndex : index of the located edge in the Ring, or -1 if no edge intersects the circular area
		///  - pDistance  : the distance between the located edge and the point, if the pointer is not NULL and an edge is found.
		/// @exception Exceptions::ArgumentException - if dToleranceDistance is negative or ppRing or pEdgeIndex is NULL 
		void LocateEdge(const Point& point, double dToleranceDistance, Ring** ppRing, int* pEdgeIndex, double* pDistance = NULL) const;

		/// Locates the node closest to the specified point 
		/// and being within the specified tolerance distance from the point.
		/// Stores the search results at the specified addresses: 
		///  - ppRing     :	pointer to the Ring containing the located node, or NULL if no node is contained in the circular area
		///  - pNodeIndex : index of the located node in the ring, or -1 if no node is contained in the circular area
		///  - pDistance  : the distance between the located node and the point, if the pointer is not NULL and a node is found
		/// @exception Exceptions::ArgumentException - if dToleranceDistance is negative or ppRing or pNodeIndex is NULL
		void LocateNode(const Point& point, double dToleranceDistance, Ring** ppRing, int* pNodeIndex, double* pDistance = NULL) const;
		
		/// Commits changes made to this Polygon since last call of this method:
		/// - simplifies the polygon: splits self-intersecting rings; removes empty edges 
		///   and simple rings with zero area;
		/// - checks if the Polygon is valid;
		/// - checks whether the Polygon does not overlap a committed Polygon of the PolygonLayer, 
		///   if the PolygonLayer does not allow overlap; 
		/// - adds the Polygon to the spatial index if the previous checks are successful.
		/// Is implicitly called by the AcceptChanges() function of the parent PolygonLayer.
		/// @exception InvalidPolygonException - if the Polygon is not valid
		/// @exception PolygonsOverlapException - if the Polygon overlaps a committed Polygon of the PolygonLayer
		/// and the PolygonLayer does not allow overlap          
		void AcceptChanges();
	
		// --- disable construction, destruction, copying, assignment ---
	private:	
		Polygon();
		Polygon(const Polygon&);
		~Polygon();
		Polygon& operator=(const Polygon&);
	
	// --------------------------- Implementation ------------------------------
	private:
		
		friend class RingImpl;
		friend class PolygonImpl;
		friend class PolygonLayerImpl;
		friend class TopologyImpl;
		friend class PolygonOperations;
		friend class PolygonOperationsImpl;
		

		const PolygonImpl* impl() const; 
		PolygonImpl*       impl(); 
	};

}
#endif