/******************************************************************************  		
 * Company:         SINED GmbH 
 * Author:          Andriy Kurhanevych
 * Created:         26.04.2007
 * Last modified:   $Modtime: 21.03.13 14:40 $
 * Purpose:         Declaration of the PolygonLayer class
 ******************************************************************************/

#ifndef POLYGONLIBRARY_POLYGONLAYER_H
#define POLYGONLIBRARY_POLYGONLAYER_H

#include <vector>

namespace PolygonLibrary
{
	class Polygon;
	class PolygonEnumerator;
	class Topology;
	
	/// Defines identifiers of spatial relations. 
	enum SpatialRelation
	{
		SR_INTERSECTS_BOUNDS,
		SR_INTERSECTS
	};

	//////////////////////////////////////////////////////////////////////////
	/// A container of Polygon objects of a Topology.
	//////////////////////////////////////////////////////////////////////////
	class PolygonLayer
	{

	// ----------------------------- Interface ------------------------------
	public:
		
		// --- Attributes ---

		/// Returns pointer to the Topology this PolygonLayer belongs to. 
		Topology* Topology() const;

		/// Returns the count of Polygons in this PolygonLayer.
		unsigned int      PolygonCount() const;
		
		/// Returns an enumerator of all Polygons of this PolygonLayer.
		PolygonEnumerator Polygons() const;

		/// Creates an empty Polygon object and adds to this PolygonLayer. 
		/// Returns pointer to the created Polygon.
		Polygon* CreatePolygon();

		/// Creates and adds (to this PolygonLayer) a Polygon object representing the same polygon 
		/// as the specified Polygon object.
		/// @return    Pointer to the created Polygon object
		/// @exception Exceptions::ArgumentException - if the input parameter is a NULL pointer
		Polygon* CreatePolygon(const Polygon* pPolygon);
		
		/// Creates and adds (to this PolygonLayer) a Polygon object representing the result of inflating/deflating 
		/// the specified polygon by the specified distance. If the specified distance is negative, 
		/// the buffering is performed inside the polygon (deflation).  
		/// @return    Pointer to the created Polygon object.
		/// @exception Exceptions::ArgumentException       - if pPolygon is NULL or dBufferDistance is zero
		/// @exception Exceptions::InvalidPolygonException - if the specified Polygon is not valid (IsValid() == false)
		Polygon* CreateBufferedPolygon(Polygon* pPolygon, double dBufferDistance);

		/// Creates and adds (to this PolygonLayer) a Polygon object representing the set-theoretic union 
		/// of the specified polygons. 
		/// The third parameter specifies if the Parents attribute property of the created Polygon has to be set.  
		/// @return    Pointer to the created Polygon object.
		/// @exception Exceptions::ArgumentException       - if an input parameter is a NULL pointer
		/// @exception Exceptions::InvalidPolygonException - if a specified Polygon is not valid (IsValid() == false)
		Polygon* CreatePolygonsUnion(Polygon* pFirstPolygon, Polygon* pSecondPolygon, bool bSetParents = false);

		/// Creates and adds (to this PolygonLayer) a Polygon object representing the set-theoretic union 
		/// of the specified polygons. 
		/// The third parameter specifies if the Parents attribute of the created Polygon has to be set.  
		/// @return    Pointer to the created Polygon object.
		/// @exception Exceptions::ArgumentException       - if an input parameter is a NULL pointer
		/// @exception Exceptions::InvalidPolygonException - if a specified Polygon is not valid (IsValid() == false)
		Polygon* CreatePolygonsUnion(PolygonEnumerator polygonEnumerator, bool bSetParents = false);
		
		/// Creates and adds (to this PolygonLayer) a Polygon object representing the set-theoretic intersection 
		/// of the specified polygons. 
		/// The third parameter specifies if the Parents attribute of the created Polygon has to be set.  
		/// @return    Pointer to the created Polygon object.
		/// @exception Exceptions::ArgumentException       - if an input parameter is a NULL pointer
		/// @exception Exceptions::InvalidPolygonException - if a specified Polygon is not valid (IsValid() == false)
		Polygon* CreatePolygonsIntersection(Polygon* pFirstPolygon, Polygon* pSecondPolygon, bool bSetParents = false);

		/// Creates and adds (to this PolygonLayer) a Polygon object representing the set-theoretic difference 
		/// of the specified polygons. 
		/// The third parameter specifies if the Parents attribute of the created Polygon has to be set.  
		/// @return    Pointer to the created Polygon object.
		/// @exception Exceptions::ArgumentException       - if an input parameter is a NULL pointer
		/// @exception Exceptions::InvalidPolygonException - if a specified Polygon is not valid (IsValid() == false)
		Polygon* CreatePolygonsDifference(Polygon* pFirstPolygon, Polygon* pSecondPolygon, bool bSetParents = false);
		
		/// Splits the specified polygon with the specified polyline and adds the result polygons to this PolygonLayer.
		/// Does nothing if the polyline does not split the polygon.
		/// The third parameter specifies if the Parents attribute of the created Polygons has to be set.  
		/// @return    Collection of pointers to the created Polygon objects.
		/// @exception Exceptions::ArgumentException       - if an input parameter is a NULL pointer
		/// @exception Exceptions::InvalidPolygonException - if the specified Polygon is not valid (IsValid() == false)
		std::vector<Polygon*> SplitPolygon(Polygon* pPolygon, Polyline* pPolyline, bool bSetParents = false);

		/// Deletes the specified Polygon of this PolygonLayer.
		/// @exception Exceptions::ArgumentException         - if the Polygon is not contained in this PolygonLayer
		/// @exception Exceptions::InvalidOperationException - if the Polygon is a parent of an other Polygon 
		/// (is contained it its Parents collection).
	   	void DeletePolygon(Polygon* pPolygon);

		/// Returns the bounding box of all polygons of this polygon layer.
		Rectangle Bounds() const;
		
		/// Returns a value indicating whether polygons of this layer can overlap.
		bool AllowsOverlap() const; 
		
		// --- Operations ---
				
		/// Locates the polygon closest to the specified point 
		/// and containing the point or being within the specified tolerance distance from the point.
		/// @return Pointer to the located Polygon 
		/// or NULL pointer if no polygon intersects the specified circular area.
		/// @exception Exceptions::InvalidArgumentException - if dToleranceDistance is negative
		Polygon* PolygonAtLocation(const Point& point, double dToleranceDistance) const;

		/// Returns an enumerator of Polygons that have the specified spatial relation  
		/// to the specified rectangle.
		PolygonEnumerator LocatePolygons(const Rectangle& rectangle, SpatialRelation spatialRelation) const;

		/// Commits all changes made since last successful call of this method: 
		/// - calls AcceptChanges() of changed Polygons,
		/// - checks polygons overlap if no overlap is allowed (AllowsOverlap() = false).
		/// @exception InvalidPolygonException -  if a Polygon of this PolygonLayer is not valid 
		/// @exception PolygonsOverlapException - if some polygons overlap and this PolygonLayer does not allow overlap  
		void AcceptChanges();
		
		/// Clear the Parents collection in all polygons.
		/// The clear is required to allow change of the parent polygons, e.q. when the PolygonLayer
		/// was created as a result of a Boolean operations on other polygon layers and the bSetParents
		/// input parameter of the corresponding method of the Topology class was true. 
		void ClearParents();	
	
	// --------------------------- Implementation ------------------------------
	private:
		friend class RingImpl;
		friend class PolygonImpl;
		friend class PolygonLayerImpl;
		friend class TopologyImpl;
		friend class PolygonOperations;

		PolygonLayer(PolygonLibrary::Topology* pTopology, bool bAllowsOverlap);
		~PolygonLayer();
		
		// disable copying and assignment
		PolygonLayer(const PolygonLayer&);
		PolygonLayer& operator=(const PolygonLayer&);
		
		// pointer to the implementation part
		const PolygonLayerImpl* impl() const; 
		PolygonLayerImpl*       impl();
		
		PolygonLayerImpl*       m_pimpl;
	};
}
#endif