// Robust version of Gilles Dumoulin's C++ port of Paul Bourke's
// triangulation code available from
// http://astronomy.swin.edu.au/~pbourke/papers/triangulate
// Used with permission of Paul Bourke.
// Segmentation fault and numerical robustness improvements by John C. Bowman

#include <cassert>
#include "Delaunay.h"
#include "predicates.h"

inline double max(double a, double b)
{
 return (a > b) ? a : b;
}

int XYZCompare(const void *v1, const void *v2)
{
 double x1=((XYZ*)v1)->p[0];
 double x2=((XYZ*)v2)->p[0];
 if(x1 < x2)
   return(-1);
 else if(x1 > x2)
   return(1);
 else
   return(0);
}

inline double hypot2(double *x)
{
 return x[0]*x[0]+x[1]*x[1];
}

///////////////////////////////////////////////////////////////////////////////
// Triangulate():
//   Triangulation subroutine
//   Takes as input NV vertices in array pxyz
//   Returned is a list of ntri triangular faces in the array v
//   These triangles are arranged in a consistent clockwise order.
//   The triangle array v should be allocated to 4 * nv
//   The vertex array pxyz must be big enough to hold 3 additional points.
//   By default, the array pxyz is automatically presorted and postsorted.
///////////////////////////////////////////////////////////////////////////////

Int Triangulate(Int nv, XYZ pxyz[], ITRIANGLE v[], Int &ntri,
               bool presort, bool postsort)
{
 Int emax = 200;

 if(presort) qsort(pxyz,nv,sizeof(XYZ),XYZCompare);
 else postsort=false;

/* Allocate memory for the completeness list, flag for each triangle */
 Int trimax = 4 * nv;
 Int *complete = new Int[trimax];
/* Allocate memory for the edge list */
 IEDGE *edges = new IEDGE[emax];
/*
 Find the maximum and minimum vertex bounds.
 This is to allow calculation of the bounding triangle
*/
 double xmin = pxyz[0].p[0];
 double ymin = pxyz[0].p[1];
 double xmax = xmin;
 double ymax = ymin;
 for(Int i = 1; i < nv; i++) {
   XYZ *pxyzi=pxyz+i;
   double x=pxyzi->p[0];
   double y=pxyzi->p[1];
   if (x < xmin) xmin = x;
   if (x > xmax) xmax = x;
   if (y < ymin) ymin = y;
   if (y > ymax) ymax = y;
 }
 double dx = xmax - xmin;
 double dy = ymax - ymin;
/*
 Set up the supertriangle.
 This is a triangle which encompasses all the sample points.
 The supertriangle coordinates are added to the end of the
 vertex list. The supertriangle is the first triangle in
 the triangle list.
*/
 static const double margin=0.01;
 double xmargin=margin*dx;
 double ymargin=margin*dy;
 pxyz[nv+0].p[0] = xmin-xmargin;
 pxyz[nv+0].p[1] = ymin-ymargin;
 pxyz[nv+1].p[0] = xmin-xmargin;
 pxyz[nv+1].p[1] = ymax+ymargin+dx;
 pxyz[nv+2].p[0] = xmax+xmargin+dy;
 pxyz[nv+2].p[1] = ymin-ymargin;
 v->p1 = nv;
 v->p2 = nv+1;
 v->p3 = nv+2;
 complete[0] = false;
 ntri = 1;
/*
 Include each point one at a time into the existing mesh
*/
 for(Int i = 0; i < nv; i++) {
   Int nedge = 0;
   double *d=pxyz[i].p;
/*
 Set up the edge buffer.
 If the point d lies inside the circumcircle then the
 three edges of that triangle are added to the edge buffer
 and that triangle is removed.
*/
   for(Int j = 0; j < ntri; j++) {
     if(complete[j])
       continue;
     ITRIANGLE *vj=v+j;

     double *a=pxyz[vj->p1].p;
     double *b=pxyz[vj->p2].p;
     double *c=pxyz[vj->p3].p;

     if(incircle(a,b,c,d) <= 0.0) { // Point d is inside or on circumcircle
/* Check that we haven't exceeded the edge list size */
       if(nedge + 3 >= emax) {
         emax += 100;
         IEDGE *p_EdgeTemp = new IEDGE[emax];
         for (Int i = 0; i < nedge; i++) {
           p_EdgeTemp[i] = edges[i];
         }
         delete[] edges;
         edges = p_EdgeTemp;
       }
       ITRIANGLE *vj=v+j;
       Int p1=vj->p1;
       Int p2=vj->p2;
       Int p3=vj->p3;
       edges[nedge].p1 = p1;
       edges[nedge].p2 = p2;
       edges[++nedge].p1 = p2;
       edges[nedge].p2 = p3;
       edges[++nedge].p1 = p3;
       edges[nedge].p2 = p1;
       ++nedge;
       ntri--;
       v[j] = v[ntri];
       complete[j] = complete[ntri];
       j--;
     } else {
       double A=hypot2(a);
       double B=hypot2(b);
       double C=hypot2(c);
       double a0=orient2d(a,b,c);
       // Is d[0] > xc+r for circumcircle abc of radius r about (xc,yc)?
       if(d[0]*a0 < 0.5*orient2d(A,a[1],B,b[1],C,c[1]))
         complete[j]=
           incircle(a[0]*a0,a[1]*a0,b[0]*a0,b[1]*a0,c[0]*a0,c[1]*a0,
                    d[0]*a0,0.5*orient2d(a[0],A,b[0],B,c[0],C)) > 0.0;
     }
   }
/*
 Tag multiple edges
 Note: if all triangles are specified anticlockwise then all
 interior edges are opposite pointing in direction.
*/
   for(Int j = 0; j < nedge - 1; j++) {
     for(Int k = j + 1; k < nedge; k++) {
       if((edges[j].p1 == edges[k].p2) && (edges[j].p2 == edges[k].p1)) {
         edges[j].p1 = -1;
         edges[j].p2 = -1;
         edges[k].p1 = -1;
         edges[k].p2 = -1;
       }
     }
   }
/*
 Form new triangles for the current point
 Skipping over any tagged edges.
 All edges are arranged in clockwise order.
*/
   for(Int j = 0; j < nedge; j++) {
     if(edges[j].p1 < 0 || edges[j].p2 < 0)
       continue;
     v[ntri].p1 = edges[j].p1;
     v[ntri].p2 = edges[j].p2;
     v[ntri].p3 = i;
     complete[ntri] = false;
     ntri++;
     assert(ntri < trimax);
   }
 }
/*
 Remove triangles with supertriangle vertices
 These are triangles which have a vertex number greater than nv
*/
 for(Int i = 0; i < ntri; i++) {
   ITRIANGLE *vi=v+i;
   if(vi->p1 >= nv || vi->p2 >= nv || vi->p3 >= nv) {
     ntri--;
     *vi = v[ntri];
     i--;
   }
 }
 delete[] edges;
 delete[] complete;

 if(postsort) {
   for(Int i = 0; i < ntri; i++) {
     ITRIANGLE *vi=v+i;
     vi->p1=pxyz[vi->p1].i;
     vi->p2=pxyz[vi->p2].i;
     vi->p3=pxyz[vi->p3].i;
   }
 }

 return 0;
}