/* $Id$ */

/**
 * \file 
 * $Revision$
 * $Date$
 * 
 * Copyright (C) 2011 The University of Sydney, Australia
 *
 * This file is part of GPlates.
 *
 * GPlates is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2, as published by
 * the Free Software Foundation.
 *
 * GPlates 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 General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "global/CompilerWarnings.h"
#include <algorithm>
#include <functional>
#include <map>
#include <queue>
#include <vector>
#include <boost/cast.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/optional.hpp>
#include <boost/pool/object_pool.hpp>
#include <CGAL/Cartesian.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Triangulation_vertex_base_2.h>
#include <QDebug>

#include "PolygonMesh.h"

#include "AngularDistance.h"
#include "Centroid.h"
#include "ConstGeometryOnSphereVisitor.h"
#include "GnomonicProjection.h"
#include "MathsUtils.h"

#include "global/AssertionFailureException.h"
#include "global/GPlatesAssert.h"

#include "utils/Earth.h"
#include "utils/Profile.h"


PUSH_GCC_WARNINGS
DISABLE_GCC_WARNING("-Wshadow")


namespace GPlatesMaths
{
	//
	// CGAL typedefs.
	//
	// NOTE: Not putting these in an anonymous namespace because some compilers complain
	// about lack of external linkage when compiling CGAL.
	//



	/**
	 * Vertex info used by @a PolygonMeshRefinement.
	 */
	struct PolygonMeshRefinementVertexInfo
	{
		explicit
		PolygonMeshRefinementVertexInfo(
					unsigned int vertex_index_) :
			vertex_index(vertex_index_)
		{  }

		unsigned int vertex_index; // Index/reference to vertex associated with this vertex.
	};

	/**
	 * This class holds the extra info for each constrained delaunay triangulation vertex.
	 *
	 * This is based on the rebind mechanism described in the 'flexibility' section of
	 * 2D Triangulation Data Structure in the CGAL user manual.
	 *
	 * We could have instead used the simpler CGAL::Triangulation_vertex_base_with_info_2
	 * since we don't need any information based on the triangulation data structure type,
	 * but it does make dereferencing a little more direct and can provide our own member functions.
	 */
	template < typename GT, typename Vb = CGAL::Triangulation_vertex_base_2<GT> >
	class PolygonMeshConstrainedDelaunayVertex_2 :
			public Vb
	{
	public:
		typedef typename Vb::Face_handle                   Face_handle;
		typedef typename Vb::Point                         Point;

		template < typename TDS2 >
		struct Rebind_TDS
		{
			typedef typename Vb::template Rebind_TDS<TDS2>::Other Vb2;
			typedef PolygonMeshConstrainedDelaunayVertex_2<GT, Vb2>  Other;
		};

		PolygonMeshConstrainedDelaunayVertex_2() :
			Vb()
		{ }

		PolygonMeshConstrainedDelaunayVertex_2(
				const Point &p) :
			Vb(p)
		{ }

		PolygonMeshConstrainedDelaunayVertex_2(
				const Point &p,
				Face_handle c) :
			Vb(p, c)
		{ }

		PolygonMeshConstrainedDelaunayVertex_2(
				Face_handle c) :
			Vb(c)
		{ }


		/**
		 * The original 3D point on the globe.
		 *
		 * This is initialised from the original point of the globe when this vertex is inserted into
		 * the constrained delaunay triangulation, or unprojected from the 2D projection plane for
		 * constraint intersection vertices generated by CGAL (we unproject them afterwards).
		 */
		boost::optional<UnitVector3D> point3d;

		// Vertex info used by @a PolygonMeshRefinement.
		boost::optional<PolygonMeshRefinementVertexInfo> mesh_refinement_info;
	};


	/**
	 * Triangle info used by @a PolygonMeshRefinement.
	 */
	struct PolygonMeshRefinementTriangleInfo
	{
		/**
		 * Index/reference to triangle associated with this face.
		 *
		 * Is none if triangle is not inside polygon.
		 */
		boost::optional<unsigned int> triangle_index;
	};

	/**
	 * This class holds the extra info for each constrained delaunay triangulation face.
	 *
	 * This is based on the rebind mechanism described in the 'flexibility' section of
	 * 2D Triangulation Data Structure in the CGAL user manual.
	 *
	 * We could have instead used the simpler CGAL::Triangulation_face_base_with_info_2
	 * since we don't need any information based on the triangulation data structure type,
	 * but it does make dereferencing a little more direct and can provide our own member functions.
	 */
	template < typename GT, typename Fb = CGAL::Constrained_triangulation_face_base_2<GT> >
	class PolygonMeshConstrainedDelaunayFace_2 :
			public Fb
	{
	public:
		typedef typename Fb::Vertex_handle                   Vertex_handle;
		typedef typename Fb::Face_handle                     Face_handle;

		template < typename TDS2 >
		struct Rebind_TDS
		{
			typedef typename Fb::template Rebind_TDS<TDS2>::Other Fb2;
			typedef PolygonMeshConstrainedDelaunayFace_2<GT, Fb2>  Other;
		};

		PolygonMeshConstrainedDelaunayFace_2() :
			Fb()
		{ }

		PolygonMeshConstrainedDelaunayFace_2(
				Vertex_handle v0, 
				Vertex_handle v1,
				Vertex_handle v2) :
			Fb(v0, v1, v2)
		{ }

		PolygonMeshConstrainedDelaunayFace_2(
				Vertex_handle v0, 
				Vertex_handle v1,
				Vertex_handle v2, 
				Face_handle n0, 
				Face_handle n1,
				Face_handle n2) :
			Fb(v0, v1, v2, n0, n1, n2)
		{ }

		// Triangle info used by @a PolygonMeshRefinement.
		boost::optional<PolygonMeshRefinementTriangleInfo> mesh_refinement_info;
	};


	//
	// UPDATE: The following comment no longer applies because we no longer use CGAL for mesh
	// refinement (we only use CGAL to create constrained delaunay triangulation) - we use our
	// own mesh refinement based on edge splitting.
	// So it appears that 'CGAL::Exact_predicates_inexact_constructions_kernel' is fine for
	// generating constrained delaunay triangulations for self-intersecting polygons.
	//
	// We can't use CGAL::Exact_predicates_exact_constructions_kernel because mesh refinement
	// requires CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt (ie, added support for'sqrt')...
	// See http://cgal-discuss.949826.n4.nabble.com/Working-kernels-for-make-conforming-2-and-refine-Delaynay-mesh-2-tt4655362.html#a4655363
	//
	// And using CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt requires CORE
	// (which uses GMP) but so slow that is it unusable and also seems to crash on data that
	// inexact constructions has no problem with. So there must be something I'm overlooking there
	// because a relatively simple polygon with 31 vertices takes 30 seconds to refine and that is
	// way too slow. The paper "Efficient Exact Geometric Predicates For Delaunay Triangulations"
	// has timings for MP_float with interval arithmetic (roughly equivalent to
	// CGAL::Exact_predicates_exact_constructions_kernel) and timings for CORE Expr (roughly
	// equivalent to CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt).
	// The latter is about ten times slower than the former at about 1 second per 1,000 points
	// which is much better than what we're getting - but then again those timings are for the
	// triangulation whereas our timing includes the meshing (which is where all the time is spent).
	//
	// And unfortunately cannot adapt another number type (like in GMP) to add support for 'sqrt'...
	// See http://cgal-discuss.949826.n4.nabble.com/Working-kernels-for-make-conforming-2-and-refine-Delaynay-mesh-2-tt4655362.html#a4655420
	//

	typedef CGAL::Exact_predicates_inexact_constructions_kernel polygon_mesh_kernel_type;
	typedef PolygonMeshConstrainedDelaunayVertex_2<polygon_mesh_kernel_type> polygon_mesh_vertex_type;
	typedef PolygonMeshConstrainedDelaunayFace_2<polygon_mesh_kernel_type> polygon_mesh_face_type;
	typedef CGAL::Triangulation_data_structure_2<polygon_mesh_vertex_type, polygon_mesh_face_type>
			polygon_mesh_triangulation_data_structure_type;

	// NOTE: The CGAL::Exact_predicates_tag enables meshing of *self-intersecting* polygons since
	// it enables triangulation constraints to intersect each other.
	// 
	// NOTE: Avoid compiler warning 4503 'decorated name length exceeded' in Visual Studio 2008.
	// Seems we get the warning, which gets (correctly) treated as error due to /WX switch,
	// even if we disable the 4503 warning. So prevent warning by reducing name length of
	// identifier - which we do by inheritance instead of using a typedef.
	struct polygon_mesh_constrained_triangulation_type :
			public CGAL::Constrained_Delaunay_triangulation_2<
					polygon_mesh_kernel_type,
					polygon_mesh_triangulation_data_structure_type,
					CGAL::Exact_predicates_tag>
	{  };


	typedef CGAL::Cartesian<double> polygon_mesh_polygon_ring_2_traits_type;
	typedef polygon_mesh_polygon_ring_2_traits_type::Point_2 polygon_mesh_polygon_ring_2_point_type;
	typedef std::vector<polygon_mesh_polygon_ring_2_point_type> polygon_mesh_polygon_ring_2_type;


	namespace
	{
		/**
		 * Creates a @a PolygonMesh from a @a GeometryOnSphere.
		 */
		class CreatePolygonMeshFromGeometryOnSphere :
				public ConstGeometryOnSphereVisitor
		{
		public:

			explicit
			CreatePolygonMeshFromGeometryOnSphere(
					const double &mesh_refinement_threshold_radians) :
				d_mesh_refinement_threshold_radians(mesh_refinement_threshold_radians)
			{  }


			/**
			 * Returns the optionally created @a PolygonMesh after visiting a @a GeometryOnSphere.
			 */
			boost::optional<PolygonMesh::non_null_ptr_to_const_type>
			get_polygon_mesh() const
			{
				return d_polygon_mesh;
			}


			virtual
			void
			visit_multi_point_on_sphere(
					MultiPointOnSphere::non_null_ptr_to_const_type multi_point_on_sphere)
			{
				d_polygon_mesh = PolygonMesh::create(multi_point_on_sphere, d_mesh_refinement_threshold_radians);
			}

			virtual
			void
			visit_point_on_sphere(
					PointGeometryOnSphere::non_null_ptr_to_const_type /*point_on_sphere*/)
			{
				// Do nothing - can't create a polygon mesh from a single point.
			}

			virtual
			void
			visit_polygon_on_sphere(
					PolygonOnSphere::non_null_ptr_to_const_type polygon_on_sphere)
			{
				d_polygon_mesh = PolygonMesh::create(polygon_on_sphere, d_mesh_refinement_threshold_radians);
			}

			virtual
			void
			visit_polyline_on_sphere(
					PolylineOnSphere::non_null_ptr_to_const_type polyline_on_sphere)
			{
				d_polygon_mesh = PolygonMesh::create(polyline_on_sphere, d_mesh_refinement_threshold_radians);
			}

		private:
			boost::optional<PolygonMesh::non_null_ptr_to_const_type> d_polygon_mesh;
			double d_mesh_refinement_threshold_radians;
		};


		/**
		 * Project an exterior or interior ring of a polygon onto the specified projection plane.
		 *
		 * Returns false if unable to project polygon.
		 */
		bool
		project_polygon_ring(
				polygon_mesh_polygon_ring_2_type &polygon_ring_2,
				const GnomonicProjection &gnomonic_projection,
				PolygonOnSphere::ring_vertex_const_iterator ring_vertex_begin,
				PolygonOnSphere::ring_vertex_const_iterator ring_vertex_end)
		{
			// If the first vertex of the ring is the same as (or extremely close to) the
			// last vertex then CGAL will complain that the ring is not a simple polygon.
			// We avoid this by skipping the last vertex in this case.
			//
			// UPDATE: Since we now support *self-intersecting* polygons (due to use of CGAL::Exact_predicates_tag)
			// we probably don't need this any longer, but we'll keep it anyway.
			PolygonOnSphere::ring_vertex_const_iterator last_ring_points_iter = ring_vertex_end;
			--last_ring_points_iter;
			if (*ring_vertex_begin == *last_ring_points_iter)
			{
				--ring_vertex_end;
			}

			// Iterate through the ring's points again and project onto the plane.
			PolygonOnSphere::ring_vertex_const_iterator ring_points_iter = ring_vertex_begin;
			for ( ; ring_points_iter != ring_vertex_end; ++ring_points_iter)
			{
				const PointOnSphere &point_3 = *ring_points_iter;

				boost::optional<polygon_mesh_polygon_ring_2_point_type> point_2 =
						gnomonic_projection.project_from_point_on_sphere<
								polygon_mesh_polygon_ring_2_point_type>(point_3);
				if (!point_2)
				{
					qWarning() << "PolygonMesh: Unable to project polygon - it's too big.";
					return false;
				}

				polygon_ring_2.push_back(point_2.get());
			}

			return true;
		}


		/**
		 * Insert a polygon's exterior or interior ring into the constrained delaunay triangulation.
		 */
		void
		insert_polygon_ring_into_constrained_delaunay_triangulation(
				polygon_mesh_constrained_triangulation_type &cdt,
				std::map<polygon_mesh_constrained_triangulation_type::Vertex_handle, unsigned int/*vertex index*/> &
						cdt_unique_vertex_handles_map,
				std::vector<polygon_mesh_constrained_triangulation_type::Vertex_handle> &cdt_unique_vertex_handles,
				const PolygonOnSphere::ring_vertex_const_iterator &ring_vertex_begin,
				const PolygonOnSphere::ring_vertex_const_iterator &ring_vertex_end,
				const polygon_mesh_polygon_ring_2_type &ring_2)
		{
			std::vector<polygon_mesh_constrained_triangulation_type::Vertex_handle> cdt_ring_vertex_handles;
			cdt_ring_vertex_handles.reserve(ring_2.size());

			// Add the ring vertices into the constrained delaunay triangulation.
			PolygonOnSphere::ring_vertex_const_iterator ring_vertex_iter = ring_vertex_begin;
			polygon_mesh_polygon_ring_2_type::const_iterator ring_vert_2_iter = ring_2.begin();
			for ( ; ring_vert_2_iter != ring_2.end(); ++ring_vert_2_iter, ++ring_vertex_iter)
			{
				polygon_mesh_constrained_triangulation_type::Vertex_handle vertex_handle = cdt.insert(
						polygon_mesh_constrained_triangulation_type::Point(
								ring_vert_2_iter->x(),
								ring_vert_2_iter->y()));
				if (cdt_unique_vertex_handles_map.insert(
						std::map<polygon_mesh_constrained_triangulation_type::Vertex_handle, unsigned int>::value_type(
							vertex_handle, boost::numeric_cast<unsigned int>(cdt_unique_vertex_handles.size()))).second)
				{
					cdt_unique_vertex_handles.push_back(vertex_handle);

					// Record the original unprojected position on the sphere so we don't have to unproject
					// which takes a little extra time and also introduces numerical error (we want the
					// mesh positions, on the sphere, of adjacent polygon meshes to match up exactly if
					// both polygons have exactly the same point locations on their shared boundary).
					const PointOnSphere &ring_point_on_sphere = *ring_vertex_iter;
					vertex_handle->point3d = ring_point_on_sphere.position_vector();
				}

				cdt_ring_vertex_handles.push_back(vertex_handle);
			}

			unsigned int num_constraints_inserted = 0;

			// Add the ring boundary constraints into the constrained delaunay triangulation.
			for (std::size_t vert_index = 1; vert_index < cdt_ring_vertex_handles.size(); ++vert_index)
			{
				// Add edge constraint unless its endpoints are coincident.
				if (cdt_ring_vertex_handles[vert_index - 1] != cdt_ring_vertex_handles[vert_index])
				{
					cdt.insert_constraint(cdt_ring_vertex_handles[vert_index - 1], cdt_ring_vertex_handles[vert_index]);
					++num_constraints_inserted;
				}
			}

			// Add final edge constraint from ring's end point to its start point unless they are coincident.
			//
			// If only one constraint added so far then don't add since it'll be the same constraint
			// already added (ie, the ring is a line with no area). We do this in case CGAL has
			// trouble when identical constraints are added more than once.
			if (num_constraints_inserted > 1 &&
				cdt_ring_vertex_handles[cdt_ring_vertex_handles.size() - 1] != cdt_ring_vertex_handles[0])
			{
				cdt.insert_constraint(cdt_ring_vertex_handles[cdt_ring_vertex_handles.size() - 1], cdt_ring_vertex_handles[0]);
				++num_constraints_inserted;
			}
		}


		/**
		 * Create a constrained delaunay triangulation from the specified polygon.
		 *
		 * Returns false if there are not enough vertices in constrained delaunay triangulation.
		 */
		bool
		create_constrained_delaunay_triangulation(
				polygon_mesh_constrained_triangulation_type &cdt,
				const PolygonOnSphere::non_null_ptr_to_const_type &polygon,
				const polygon_mesh_polygon_ring_2_type &exterior_ring_2,
				const std::vector<polygon_mesh_polygon_ring_2_type> &interior_rings_2)
		{
			// Use a map in case CGAL merges any vertices.
			std::map<
					polygon_mesh_constrained_triangulation_type::Vertex_handle,
					unsigned int/*vertex index*/> cdt_unique_vertex_handles_map;
			std::vector<polygon_mesh_constrained_triangulation_type::Vertex_handle> cdt_unique_vertex_handles;

			// Insert polygon's exterior ring into constrained delaunay triangulation.
			insert_polygon_ring_into_constrained_delaunay_triangulation(
					cdt,
					cdt_unique_vertex_handles_map,
					cdt_unique_vertex_handles,
					polygon->exterior_ring_vertex_begin(),
					polygon->exterior_ring_vertex_end(),
					exterior_ring_2);

			// Insert polygon's interior rings into constrained delaunay triangulation.
			const unsigned int num_interior_rings = polygon->number_of_interior_rings();
			for (unsigned int interior_ring_index = 0; interior_ring_index < num_interior_rings; ++interior_ring_index)
			{
				insert_polygon_ring_into_constrained_delaunay_triangulation(
						cdt,
						cdt_unique_vertex_handles_map,
						cdt_unique_vertex_handles,
						polygon->interior_ring_vertex_begin(interior_ring_index),
						polygon->interior_ring_vertex_end(interior_ring_index),
						interior_rings_2[interior_ring_index]);
			}

			// For now, if the constrained delaunay triangulation has less than three unique vertices then discard it.
			// This can happen if CGAL determines enough points are close enough to be merged.
			if (cdt_unique_vertex_handles.size() < 3)
			{
				return false;
			}

			return true;
		}


		/**
		 * We do our own mesh refinement based on splitting edges that exceed a threshold length.
		 *
		 * Previously we used 'CGAL::refine_Delaunay_mesh_2()' from the CGAL library which produces
		 * a nicer triangulation in that, in addition to inserting new vertices, it moves
		 * original vertices such that the refined triangulation has no long, thin triangles
		 * (produced from the original constrained delaunay triangulation).
		 *
		 * However we avoid using CGAL's mesh refinement because it hangs/crashes on some polygons.
		 * Also splitting based on edge length ensures that two adjacent polygons, that have the same
		 * vertex positions along their shared boundary, will get newly refined vertices that match
		 * up with each other thus avoiding thin cracks between polygons (due to differing refinements).
		 * We do get long, thin triangles in some places though (they are retained from the
		 * constrained delaunay triangulation) but it doesn't matter because we don't want anything
		 * from the refined mesh other than covering the interior of the polygon.
		 * You can control the shape (thinness) of triangles with CGAL and allow it to create fewer
		 * triangles by allowing thin ones but if there are two vertices close to each other then,
		 * while naturally the triangles around them will be small, it appears that triangles
		 * neighbouring those small triangles cannot be vastly different in size - so you get these
		 * areas where the triangles start out small and then spread outwards in linearly increasing
		 * size. Whereas just splitting edges doesn't have this limitation.
		 */
		class PolygonMeshRefinement
		{
		public:

			struct Vertex
			{
				explicit
				Vertex(
						const UnitVector3D &point_) :
					point(point_)
				{  }

				UnitVector3D point;
			};

			struct Edge; // Forward declaration.

			struct Triangle
			{
				static
				unsigned int
				ccw(
						unsigned int vertex_offset)
				{
					vertex_offset += 1;
					if (vertex_offset >= 3)
					{
						vertex_offset -= 3;
					}
					return vertex_offset;
				}

				static
				unsigned int
				cw(
						unsigned int vertex_offset)
				{
					vertex_offset += 2;
					if (vertex_offset >= 3)
					{
						vertex_offset -= 3;
					}
					return vertex_offset;
				}

				Triangle(
						unsigned int vertex_index_0,
						unsigned int vertex_index_1,
						unsigned int vertex_index_2)
				{
					vertex_indices[0] = vertex_index_0;
					vertex_indices[1] = vertex_index_1;
					vertex_indices[2] = vertex_index_2;

					edges[0] = edges[1] = edges[2] = NULL;
				}

				unsigned int
				get_vertex_offset_opposite_edge(
						unsigned int edge_vertex_index_0,
						unsigned int edge_vertex_index_1) const
				{
					// Find the triangle's vertex opposite the edge.
					for (unsigned int v = 0; v < 3; ++v)
					{
						if (vertex_indices[v] != edge_vertex_index_0 &&
							vertex_indices[v] != edge_vertex_index_1)
						{
							return v;
						}
					}

					GPlatesGlobal::Abort(GPLATES_ASSERTION_SOURCE);
					return 0; // To keep compiler happy.
				}

				void
				set_edge(
						Edge *edge)
				{
					const unsigned int edge_index = get_vertex_offset_opposite_edge(
							edge->vertex_indices[0],
							edge->vertex_indices[1]);
					edges[edge_index] = edge;
				}


				unsigned int vertex_indices[3];
				// Each edge is opposite the associated vertex in @a vertex_indices.
				Edge *edges[3];
			};

			struct Edge
			{
				static
				AngularDistance
				calc_edge_length(
						const Vertex &vertex_0,
						const Vertex &vertex_1)
				{
					return AngularDistance::create_from_cosine(
							dot(vertex_0.point, vertex_1.point));
				}

				explicit
				Edge(
						Triangle *triangle_0,
						Triangle *triangle_1,
						unsigned int vertex_index_0,
						unsigned int vertex_index_1,
						const std::vector<Vertex *> &vertices) :
					edge_length(
							calc_edge_length(
									*vertices[vertex_index_0],
									*vertices[vertex_index_1]))
				{
					triangles[0] = triangle_0;
					triangles[1] = triangle_1;

					vertex_indices[0] = vertex_index_0;
					vertex_indices[1] = vertex_index_1;
				}

				void
				set_triangles_edge()
				{
					for (unsigned int t = 0; t < 2; ++t)
					{
						if (triangles[t])
						{
							triangles[t]->set_edge(this);
						}
					}
				}

				void
				replace_triangle(
						Triangle *old_triangle,
						Triangle *new_triangle)
				{
					for (unsigned int t = 0; t < 2; ++t)
					{
						if (triangles[t] == old_triangle)
						{
							triangles[t] = new_triangle;
							return;
						}
					}

					GPlatesGlobal::Abort(GPLATES_ASSERTION_SOURCE);
				}


				/**
				 * The two adjoining triangles.
				 *
				 * One of the triangles can be NULL (if this is a boundary edge).
				 */
				Triangle *triangles[2];

				unsigned int vertex_indices[2]; // Two edge vertices.

				AngularDistance edge_length;
			};


			/**
			 * Construct a triangle mesh suitable for refinement from the constrained delaunay triangulation.
			 */
			explicit
			PolygonMeshRefinement(
					const PolygonOnSphere::non_null_ptr_to_const_type &polygon,
					const polygon_mesh_constrained_triangulation_type &cdt,
					const GnomonicProjection &gnomonic_projection)
			{
				// Iterate over the edges of the triangulation and collect the triangles that are inside the polygon.
				for (polygon_mesh_constrained_triangulation_type::Finite_edges_iterator edges_iter = cdt.finite_edges_begin();
					edges_iter != cdt.finite_edges_end();
					++edges_iter)
				{
					// Get the two faces adjoining the current edge.
					const polygon_mesh_constrained_triangulation_type::Face_handle face_handles[2] =
					{
						edges_iter->first,
						edges_iter->first->neighbor(edges_iter->second)
					};

					Triangle *triangles[2] = { NULL, NULL };

					// Iterate over both faces adjoining the current edge.
					for (unsigned int t = 0; t < 2; ++t)
					{
						// Skip infinite faces. These occur on boundary edges.
						if (cdt.is_infinite(face_handles[t]))
						{
							continue;
						}

						// If we've not looked at the current face yet then do so now.
						if (!face_handles[t]->mesh_refinement_info)
						{
							initialise_face(face_handles[t], gnomonic_projection, polygon);
						}

						// If the triangle is not inside the polygon then skip it.
						if (!face_handles[t]->mesh_refinement_info->triangle_index)
						{
							continue;
						}

						triangles[t] = d_triangles[face_handles[t]->mesh_refinement_info->triangle_index.get()];
					}

					// If both triangles adjoining current edge are not inside the polygon or an
					// infinite face the skip the current edge.
					if (triangles[0] == NULL &&
						triangles[1] == NULL)
					{
						continue;
					}

					// Get the edge vertices.
					const polygon_mesh_constrained_triangulation_type::Vertex_handle edge_vertex_handles[2] =
					{
						edges_iter->first->vertex(cdt.cw(edges_iter->second)),
						edges_iter->first->vertex(cdt.ccw(edges_iter->second))
					};

					// The vertex indices (and vertices) should already have been initialised by
					// one of the visited adjacent triangles.
					const unsigned int edge_vertex_indices[2] =
					{
						edge_vertex_handles[0]->mesh_refinement_info->vertex_index,
						edge_vertex_handles[1]->mesh_refinement_info->vertex_index,
					};

					Edge *edge = d_edge_pool.construct(
							// Use Edge copy constructor since boost::object_pool::construct() only accepts 3 args...
							Edge(
									triangles[0], triangles[1],
									edge_vertex_indices[0], edge_vertex_indices[1],
									d_vertices));
					edge->set_triangles_edge();
					d_edges.push_back(edge);
				}
			}


			/**
			 * Keep spitting edges in half until all edge lengths are below the specified angular threshold.
			 */
			void
			refine_mesh(
					double edge_length_threshold_radians)
			{
				if (d_edges.empty())
				{
					return;
				}

				// Avoid a zero threshold (or threshold greater than 180 degrees).
				if (edge_length_threshold_radians < MINIMUM_EDGE_LENGTH_THRESHOLD_RADIANS)
				{
					edge_length_threshold_radians = MINIMUM_EDGE_LENGTH_THRESHOLD_RADIANS;
				}
				if (edge_length_threshold_radians > PI)
				{
					edge_length_threshold_radians = PI;
				}

				const AngularDistance edge_length_threshold =
						AngularDistance::create_from_angle(edge_length_threshold_radians);

				// Create a heap based on the edge lengths of our edges.
				// Only add those edges with edge lengths exceeding the threshold (since they will get split).
				std::priority_queue<Edge *, std::vector<Edge *>, EdgeLengthCompare> edges_to_refine;
				for (std::vector<Edge *>::const_iterator edges_iter = d_edges.begin();
					edges_iter != d_edges.end();
					++edges_iter)
				{
					Edge *edge = *edges_iter;
					if (edge->edge_length.is_precisely_greater_than(edge_length_threshold))
					{
						edges_to_refine.push(edge);
					}
				}

				// Keep splitting edges until all edges are below the threshold length.
				while (!edges_to_refine.empty())
				{
					// Split the longest edge (which is at the front of the heap).
					split_edge(edges_to_refine, edge_length_threshold);
				}
			}


			/**
			 * Get the @a PolygonMesh triangles/vertices from the current mesh refinement.
			 */
			void
			get_polygon_mesh(
					std::vector<PolygonMesh::Triangle> &polygon_mesh_triangles,
					std::vector<PolygonMesh::Vertex> &polygon_mesh_vertices) const
			{
				const unsigned int num_vertices = d_vertices.size();
				polygon_mesh_vertices.reserve(num_vertices);
				for (unsigned int v = 0; v < num_vertices; ++v)
				{
					polygon_mesh_vertices.push_back(
							PolygonMesh::Vertex(d_vertices[v]->point));
				}

				const unsigned int num_triangles = d_triangles.size();
				polygon_mesh_triangles.reserve(num_triangles);
				for (unsigned int t = 0; t < num_triangles; ++t)
				{
					const Triangle &tri = *d_triangles[t];

					polygon_mesh_triangles.push_back(
							PolygonMesh::Triangle(
									tri.vertex_indices[0],
									tri.vertex_indices[1],
									tri.vertex_indices[2]));
				}
			}

		private:

			/**
			 * Compares the lengths of two edges.
			 */
			struct EdgeLengthCompare
			{
				bool
				operator()(
						const Edge *edge1,
						const Edge *edge2) const
				{
					return edge1->edge_length.is_precisely_less_than(edge2->edge_length);
				}
			};


			static const double MINIMUM_EDGE_LENGTH_THRESHOLD_RADIANS;


			boost::object_pool<Vertex> d_vertex_pool;
			boost::object_pool<Triangle> d_triangle_pool;
			boost::object_pool<Edge> d_edge_pool;

			std::vector<Vertex *> d_vertices;
			std::vector<Triangle *> d_triangles;
			std::vector<Edge *> d_edges;


			/**
			 * Visit a face of the constrained delaunay triangulation and if it's inside the polygon
			 * then create a triangle (and associated vertices) for it, otherwise mark it as outside
			 * the polygon so we don't visit it again.
			 */
			void
			initialise_face(
					polygon_mesh_constrained_triangulation_type::Face_handle face_handle,
					const GnomonicProjection &gnomonic_projection,
					const PolygonOnSphere::non_null_ptr_to_const_type &polygon)
			{
				// Register that we've visited the face.
				// By default it will be considered outside the polygon and have no triangle index.
				face_handle->mesh_refinement_info = PolygonMeshRefinementTriangleInfo();

				// Iterate over the current face's vertices to determine the triangle centroid.
				// We need the centroid to determine if the triangle is part of the mesh (inside polygon).
				Vector3D triangle_vertices_sum;
				polygon_mesh_constrained_triangulation_type::Vertex_handle triangle_vertex_handles[3];
				for (unsigned int v = 0; v < 3; ++v)
				{
					polygon_mesh_constrained_triangulation_type::Vertex_handle vertex_handle = face_handle->vertex(v);

					// If the vertex does not have a 3D point then unproject the 2D point to get it.
					// This happens when the constrained triangulation creates new vertices (that we didn't insert)
					// which happens when constraint edges intersect (a new vertex is generated at intersection).
					if (!vertex_handle->point3d)
					{
						// Unproject the mesh point back onto the sphere.
						vertex_handle->point3d = gnomonic_projection.unproject_to_point_on_sphere(
									vertex_handle->point()).position_vector();
					}

					triangle_vertex_handles[v] = vertex_handle;
					triangle_vertices_sum = triangle_vertices_sum + Vector3D(vertex_handle->point3d.get());
				}

				// If the magnitude of the summed triangle vertices is zero then all the vertices averaged
				// to zero and hence we cannot determine the triangle's centroid.
				// This shouldn't happen because it would mean the triangle's vertices are all equally
				// spaced on a great circle which is extremely unlikely.
				// If this happens we'll just skip the triangle.
				if (triangle_vertices_sum.magSqrd() <= 0.0)
				{
					return;
				}

				// Skip the current triangle if its centroid is outside the polygon.
				const PointOnSphere triangle_centroid(triangle_vertices_sum.get_normalisation());
				if (!polygon->is_point_in_polygon(triangle_centroid))
				{
					return;
				}

				// Create any vertices that have not been visited yet.
				for (unsigned int v = 0; v < 3; ++v)
				{
					polygon_mesh_constrained_triangulation_type::Vertex_handle vertex_handle =
							triangle_vertex_handles[v];

					// If we've not visited the vertex yet then do so now.
					if (!vertex_handle->mesh_refinement_info)
					{
						// Mark face as been visited and record index into 'd_vertices'.
						vertex_handle->mesh_refinement_info =
								PolygonMeshRefinementVertexInfo(d_vertices.size());

						Vertex *vertex = d_vertex_pool.construct(vertex_handle->point3d.get());
						d_vertices.push_back(vertex);
					}
				}

				// Mark face as belonging to the polygon and also record index into 'd_triangles'.
				face_handle->mesh_refinement_info->triangle_index = d_triangles.size();

				Triangle *triangle = d_triangle_pool.construct(
						triangle_vertex_handles[0]->mesh_refinement_info->vertex_index,
						triangle_vertex_handles[1]->mesh_refinement_info->vertex_index,
						triangle_vertex_handles[2]->mesh_refinement_info->vertex_index);
				d_triangles.push_back(triangle);
			}

			/**
			 * Split the longest edge (which is at the front of the heap).
			 */
			void
			split_edge(
					std::priority_queue<Edge *, std::vector<Edge *>, EdgeLengthCompare> &edges_to_refine,
					const AngularDistance &edge_length_threshold)
			{
				// Pop the front edge off the heap.
				Edge *edge = edges_to_refine.top();
				edges_to_refine.pop();

				// Get the edge midpoint.
				// The edge endpoints won't be antipodal because we know that the entire polygon projected
				// onto a 2D plane, so we don't have to worry about 'get_normalisation()' throwing.
				const UnitVector3D edge_mid_point = (
							Vector3D(d_vertices[edge->vertex_indices[0]]->point) +
									Vector3D(d_vertices[edge->vertex_indices[1]]->point)
						).get_normalisation();

				// Create a vertex for the edge midpoint.
				const unsigned int edge_mid_vertex_index = d_vertices.size();
				Vertex *edge_mid_vertex = d_vertex_pool.construct(edge_mid_point);
				d_vertices.push_back(edge_mid_vertex);

				// When the edge is split there will either be two new triangles or four
				// depending on whether edge is on boundary or not (ie, edge originally has
				// one or two adjoining triangles respectively).
				// Half of these triangles will overwrite/re-use the original triangles and the other
				// half will be newly created triangles.
				// These will be the newly created triangle(s).
				Triangle *split_triangles_1[2] = { NULL, NULL };

				// Iterate over both triangles adjoining the current edge.
				for (unsigned int t = 0; t < 2; ++t)
				{
					// Might be a boundary edge.
					if (edge->triangles[t] == NULL)
					{
						continue;
					}

					// Copy the original triangle since the first split triangle will overwrite it.
					const Triangle triangle = *edge->triangles[t];

					// Find the triangle's vertex offset (0, 1 or 2) opposite the edge.
					const unsigned int vertex_offset_opposite_edge =
							triangle.get_vertex_offset_opposite_edge(
									edge->vertex_indices[0],
									edge->vertex_indices[1]);


					//
					// The first split triangle.
					//

					// Split the current triangle into two (due to edge split).
					// Re-use the original triangle for the first split triangle.
					Triangle *split_triangle_0 = edge->triangles[t];

					// Set the next counter-clockwise/clockwise vertex in first split triangle to the edge midpoint.
					//
					// Later we'll be contracting the vertex at 'edge->vertex_indices[1]' to the mid-point for the
					// first split edge 'split_edge_0'. So we need to find that vertex in the first split triangle
					// and set it to the edge midpoint.
					if (triangle.vertex_indices[Triangle::ccw(vertex_offset_opposite_edge)] == edge->vertex_indices[1])
					{
						split_triangle_0->vertex_indices[Triangle::ccw(vertex_offset_opposite_edge)] =
								edge_mid_vertex_index;
					}
					else
					{
						split_triangle_0->vertex_indices[Triangle::cw(vertex_offset_opposite_edge)] =
								edge_mid_vertex_index;
					}


					//
					// The second split triangle.
					//

					unsigned int split_triangle_1_vertex_index_0 = edge_mid_vertex_index;
					unsigned int split_triangle_1_vertex_index_1;
					unsigned int split_triangle_1_vertex_index_2;
					// The new split triangle will have two of edges be new half-edges and a third
					// edge will be one of the original (un-split) edges.
					Edge *split_triangle_1_original_edge;
					// Similar to above with the first split triangle, we need to find which vertices
					// of the original (un-split) triangle contribute to the split triangle.
					// One of these vertices will be the edge midpoint.
					if (triangle.vertex_indices[Triangle::ccw(vertex_offset_opposite_edge)] == edge->vertex_indices[1])
					{
						split_triangle_1_vertex_index_1 = triangle.vertex_indices[vertex_offset_opposite_edge];
						split_triangle_1_vertex_index_2 = triangle.vertex_indices[Triangle::ccw(vertex_offset_opposite_edge)];
						split_triangle_1_original_edge = triangle.edges[Triangle::cw(vertex_offset_opposite_edge)];
					}
					else
					{
						split_triangle_1_vertex_index_1 = triangle.vertex_indices[Triangle::cw(vertex_offset_opposite_edge)];
						split_triangle_1_vertex_index_2 = triangle.vertex_indices[vertex_offset_opposite_edge];
						split_triangle_1_original_edge = triangle.edges[Triangle::ccw(vertex_offset_opposite_edge)];
					}

					Triangle *split_triangle_1 = d_triangle_pool.construct(
							split_triangle_1_vertex_index_0,
							split_triangle_1_vertex_index_1,
							split_triangle_1_vertex_index_2);
					d_triangles.push_back(split_triangle_1);

					// Set the original edge in the split triangle and vice versa.
					// The split triangle must point to the original edge which must in turn point
					// back to the the original edge.
					split_triangle_1->set_edge(split_triangle_1_original_edge);
					split_triangle_1_original_edge->replace_triangle(edge->triangles[t], split_triangle_1);

					split_triangles_1[t] = split_triangle_1;


					//
					// The edge between the split triangles.
					//

					Edge *triangle_split_edge = d_edge_pool.construct(
							// Use Edge copy constructor since boost::object_pool::construct() only accepts 3 args...
							Edge(
									split_triangle_0, split_triangle_1,
									edge_mid_vertex_index, triangle.vertex_indices[vertex_offset_opposite_edge],
									d_vertices));
					triangle_split_edge->set_triangles_edge();
					d_edges.push_back(triangle_split_edge);

					// Add the edge that splits the current triangle to the heap if it's above the
					// threshold (so that it can get split later on).
					if (triangle_split_edge->edge_length.is_precisely_greater_than(edge_length_threshold))
					{
						edges_to_refine.push(triangle_split_edge);
					}
				}

				// Copy the original edge vertex indices since the first split edge will overwrite them.
				const unsigned int edge_vertex_indices[2] =
				{
					edge->vertex_indices[0],
					edge->vertex_indices[1]
				};

				// Split the edge into two (due to edge split).
				// Re-use the original edge for one split edge.
				// And create a new edge for the second split edge.
				Edge *split_edge_0 = edge;

				// Make the split edge a half-edge by setting one of its vertices to the
				// edge mid point and re-calculating its edge length.
				//
				// As noted above when splitting triangles, we are contracting the vertex at
				// 'edge->vertex_indices[1]' to the mid-point for the first split edge 'split_edge_0'.
				split_edge_0->vertex_indices[1] = edge_mid_vertex_index;
				split_edge_0->edge_length = Edge::calc_edge_length(
						*d_vertices[edge_vertex_indices[0]],
						*edge_mid_vertex);

				// The second split edge.
				Edge *split_edge_1 = d_edge_pool.construct(
						// Use Edge copy constructor since boost::object_pool::construct() only accepts 3 args...
						Edge(
								split_triangles_1[0], split_triangles_1[1],
								edge_mid_vertex_index, edge_vertex_indices[1],
								d_vertices));
				split_edge_1->set_triangles_edge();
				d_edges.push_back(split_edge_1);

				// Add the split edges to the heap (with their new edge lengths).
				if (split_edge_0->edge_length.is_precisely_greater_than(edge_length_threshold))
				{
					edges_to_refine.push(split_edge_0);
				}
				if (split_edge_1->edge_length.is_precisely_greater_than(edge_length_threshold))
				{
					edges_to_refine.push(split_edge_1);
				}
			}
		};

		// Set the minimum edge length to 0.01km (10 metres).
		const double PolygonMeshRefinement::MINIMUM_EDGE_LENGTH_THRESHOLD_RADIANS =
				0.01 / GPlatesUtils::Earth::EQUATORIAL_RADIUS_KMS;
	}
}


bool
GPlatesMaths::PolygonMesh::initialise(
		const PolygonOnSphere::non_null_ptr_to_const_type &polygon,
		const double &mesh_refinement_threshold_radians)
{
	//PROFILE_FUNC();

	// Project the polygon onto a tangent plane using a gnomonic projection.
	//
	// NOTE: We use a gnomonic projection to ensure that great circle arcs (the polygon edges)
	// project onto straight lines in the 2D projection - this ensures that the re-projection
	// of the resulting triangulation (with tessellated 2D lines) will have the extra triangulation
	// vertices lie on the great circle arcs. With a non-planar projection such as azimuthal
	// equal area projection this is not the case.
	//
	// Get the centroid of the polygon to use as our tangent point.
	const PointOnSphere tangent_point(polygon->get_boundary_centroid());
	const GnomonicProjection gnomonic_projection(
			tangent_point,
			// If any 3D point (to be projected) is further than this angle from the tangent point
			// then the projection fails (a cosine of 0.15 is about 81 degrees)...
			AngularDistance::create_from_cosine(0.15));

	// Project polygon's exterior ring.
	polygon_mesh_polygon_ring_2_type exterior_ring_2;
	if (!project_polygon_ring(
			exterior_ring_2,
			gnomonic_projection,
			polygon->exterior_ring_vertex_begin(),
			polygon->exterior_ring_vertex_end()))
	{
		// For now, if any point isn't localised on the plane then discard polygon.
		return false;
	}

	// Project polygon's interior rings.
	const unsigned int num_interior_rings = polygon->number_of_interior_rings();
	std::vector<polygon_mesh_polygon_ring_2_type> interior_rings_2(num_interior_rings);
	for (unsigned int interior_ring_index = 0; interior_ring_index < num_interior_rings; ++interior_ring_index)
	{
		if (!project_polygon_ring(
				interior_rings_2[interior_ring_index],
				gnomonic_projection,
				polygon->interior_ring_vertex_begin(interior_ring_index),
				polygon->interior_ring_vertex_end(interior_ring_index)))
		{
			// For now, if any point isn't localised on the plane then discard polygon.
			return false;
		}
	}

	// Generate the constrained delaunay triangulation.
	polygon_mesh_constrained_triangulation_type cdt;
	if (!create_constrained_delaunay_triangulation(cdt, polygon, exterior_ring_2, interior_rings_2))
	{
		return false;
	}

	// Create a mesh refinement from the constrained delaunay triangulation.
	//
	// Note that regions outside the polygon are not meshed.
	// This means interior rings are not meshed (unless they intersect the exterior ring in which
	// case part of interior ring is considered inside and part outside the polygon - basically the
	// centroid of each triangle in mesh must pass the point-in-polygon test).
	PolygonMeshRefinement polygon_mesh_refinement(polygon, cdt, gnomonic_projection);

	// Refine the mesh by splitting edges until all edge lengths are below the specified threshold.
	polygon_mesh_refinement.refine_mesh(mesh_refinement_threshold_radians);

	// Get the PolygonMesh triangles/vertices from the current mesh refinement.
	polygon_mesh_refinement.get_polygon_mesh(d_triangles, d_vertices);

	return true;
}

// See above.
POP_GCC_WARNINGS


boost::optional<GPlatesMaths::PolygonMesh::non_null_ptr_to_const_type>
GPlatesMaths::PolygonMesh::create(
		const PolygonOnSphere::non_null_ptr_to_const_type &polygon,
		const double &mesh_refinement_threshold_radians)
{
	non_null_ptr_type polygon_mesh(new PolygonMesh());

	// Attempt to create the polygon mesh from the polygon vertices.
	if (!polygon_mesh->initialise(polygon, mesh_refinement_threshold_radians))
	{
		return boost::none;
	}

	return non_null_ptr_to_const_type(polygon_mesh);
}


boost::optional<GPlatesMaths::PolygonMesh::non_null_ptr_to_const_type>
GPlatesMaths::PolygonMesh::create(
		const PolylineOnSphere::non_null_ptr_to_const_type &polyline,
		const double &mesh_refinement_threshold_radians)
{
	if (polyline->number_of_vertices() < 3)
	{
		return boost::none;
	}

	PolygonOnSphere::non_null_ptr_to_const_type polygon =
			PolygonOnSphere::create(
					polyline->vertex_begin(),
					polyline->vertex_end());

	non_null_ptr_type polygon_mesh(new PolygonMesh());

	// Attempt to create the polygon mesh from the polyline vertices.
	// The first and last vertices will close off to form a polygon.
	if (!polygon_mesh->initialise(polygon, mesh_refinement_threshold_radians))
	{
		return boost::none;
	}

	return non_null_ptr_to_const_type(polygon_mesh);
}


boost::optional<GPlatesMaths::PolygonMesh::non_null_ptr_to_const_type>
GPlatesMaths::PolygonMesh::create(
		const MultiPointOnSphere::non_null_ptr_to_const_type &multi_point,
		const double &mesh_refinement_threshold_radians)
{
	if (multi_point->number_of_points() < 3)
	{
		return boost::none;
	}

	PolygonOnSphere::non_null_ptr_to_const_type polygon =
			PolygonOnSphere::create(
					multi_point->begin(),
					multi_point->end());

	non_null_ptr_type polygon_mesh(new PolygonMesh());

	// Attempt to create the polygon mesh from the multi-point vertices.
	// A polygon is formed from the multipoint by treating the order of points in the multipoint
	// as the vertices of a polygon.
	if (!polygon_mesh->initialise(polygon, mesh_refinement_threshold_radians))
	{
		return boost::none;
	}

	return non_null_ptr_to_const_type(polygon_mesh);
}


boost::optional<GPlatesMaths::PolygonMesh::non_null_ptr_to_const_type>
GPlatesMaths::PolygonMesh::create(
		const GeometryOnSphere::non_null_ptr_to_const_type &geometry_on_sphere,
		const double &mesh_refinement_threshold_radians)
{
	CreatePolygonMeshFromGeometryOnSphere visitor(mesh_refinement_threshold_radians);

	geometry_on_sphere->accept_visitor(visitor);

	return visitor.get_polygon_mesh();
}


// NOTE: Do not remove this.
// This is here to avoid CGAL related compile errors on MacOS.
// It seems we only need this at the end of the file - perhaps it's something to do with
// template instantiations happening at the end of the translation unit.
DISABLE_GCC_WARNING("-Wshadow")
