/* 
 * convexHull.cpp - Convex hull implementation wrapper
 * Copyright (C) 2018, D Haley
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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, see <http://www.gnu.org/licenses/>.
 */
#include "convexHull.h"

#include "common/assertion.h"

#include <set>
#include <random>

using std::vector;
using std::set;


//! Returns the shortest distance between a line segment and a given point
/* The inputs are the ends of the line segment and the point. Uses the formula that 
 * \f$ 
 * D = \abs{\vec{PE}}\f$
 * \f[ 
 * \mathrm{~if~} \vec{PA} \cdot \vec{AB} > 0 
 * \rightarrow \vec{PE} = \vec{A} 
 * \f]
 * \f[
 * \mathrm{~if~} \vec{AB} \cdot \vec{PB} > 0 ~\&~ \vec{PA} \cdot \vec{AB} < 0
 * \rightarrow \vec{PB} \cdot \frac{\vec{AB}}{\abs{\vec{AB}}} 
 * \f]
 * \f[
 * \mathrm{~if~} \vec{PB} \cdot \vec{AB} < 0 
 * \rightarrow \vec{B} 
 * \f]
 */
float distanceToSegment(const Point3D &fA, const Point3D &fB, const Point3D &p)
{

	//If the vectors a pointing "together" then use  point-line formula
	if(vectorPointDir(fA,fB,p,p) == POINTDIR_TOGETHER)
	{
		Point3D closestPt;
		Point3D vAB= fB-fA;

		//Use formula d^2 = |(B-A)(cross)(A-P)|^2/|B-A|^2
		return sqrtf( (vAB.crossProd(fA-p)).sqrMag()/(vAB.sqrMag()));
	}

	return sqrtf( std::min(fB.sqrDist(p), fA.sqrDist(p)) );
}


//!Find the distance between a point, and a triangular facet -- may be positive or negative
/* The inputs are the facet points (ABC) and the point P.
 * distance is shortest using standard plane version 
 * \f$ D = \vec{AB} \cdot \vec{n} \f$ 
 * iff dot products to each combination of \f$ \left( AP,BP,CP \right) \leq 0 \f$
 * otherwise closest point is on the boundary of the simplex.
 * tested by shortest distance to each line segment (E is shortest pt. AB is line segement)
 * \f$ \vec{E} = \frac{\vec{AB}}{|\vec{AB}|} 
 *  ( \vec{PB} \cdot \vec{AB})\f$
 */
float distanceToFacet(const Point3D &fA, const Point3D &fB, 
			const Point3D &fC, const Point3D &p, const Point3D &normal)
{

	//This will check the magnitude of the incoming normal
	ASSERT( fabs(sqrtf(normal.sqrMag()) - 1.0f) < 2.0* std::numeric_limits<float>::epsilon());
	unsigned int pointDir[3];
	pointDir[0] = vectorPointDir(fA,fB,p,p);
	pointDir[1] = vectorPointDir(fA,fC,p,p);
	pointDir[2] = vectorPointDir(fB,fC,p,p);

	//They can never be "APART" if the
	//vectors point to the same pt
	ASSERT(pointDir[0] != POINTDIR_APART);
	ASSERT(pointDir[1] != POINTDIR_APART);
	ASSERT(pointDir[2] != POINTDIR_APART);

	//Check to see if any of them are "in common"
	if(pointDir[0]  > 0 ||  pointDir[1] >0 || pointDir[2] > 0)
	{
		//if so, we have to check each edge for its closest point
		//then pick the best
		float bestDist[3];
		bestDist[0] = distanceToSegment(fA,fB,p);
		bestDist[1] = distanceToSegment(fA,fC,p);
		bestDist[2] = distanceToSegment(fB,fC,p);
	

		return std::min(bestDist[0],std::min(bestDist[1],bestDist[2]));
	}

	float temp;

	temp = fabs((p-fA).dotProd(normal));

	//check that the other points were not better than this!
	ASSERT(sqrtf(fA.sqrDist(p)) >= temp - std::numeric_limits<float>::epsilon());
	ASSERT(sqrtf(fB.sqrDist(p)) >= temp - std::numeric_limits<float>::epsilon());
	ASSERT(sqrtf(fC.sqrDist(p)) >= temp - std::numeric_limits<float>::epsilon());

	//Point lies above/below facet, use plane formula
	return temp; 
}

#ifdef HAVE_SWEEP_HULL

//S-Hull version

float getConvexVolume(const vector<Point3D> &pts, 
			const vector<HULLTRI> &facets)
{
	//Find midpoint
	Point3D midPt(0,0,0);

	for(unsigned int ui=0;ui<pts.size();ui++)
		midPt+=pts[ui];
	midPt*=1.0f/(float)pts.size();

	//Use double to limit accumulation error
	double totalVol = 0;
	//The convex hull consists of tetrahedra which are joined to the midpt.
	//V_tet = \frac(1)(3!) \abs(a\cdot(b \cross c))
	for(unsigned int ui=0;ui<facets.size();ui++)
	{
		Point3D edge[3];

		//Skip bad facets FIXME: This appears to be an upstream problem
		if(facets[ui].vertex[0] >= pts.size() ||
				facets[ui].vertex[1] >=pts.size() ||
				facets[ui].vertex[2] >=pts.size())
			continue;

		edge[0]  = pts[facets[ui].vertex[0]]-midPt;
		edge[1]  = pts[facets[ui].vertex[1]]-midPt;
		edge[2]  = pts[facets[ui].vertex[2]]-midPt;

		totalVol+=fabs(edge[0].dotProd(edge[1].crossProd(edge[2])));
	}

	totalVol*=1.0f/6.0f;
	return (float)totalVol;
}

unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsigned int *progress,
					std::vector<Point3D> &curHull,vector<HULLTRI> &facets)
{

	BoundCube bCube;


	vector<Point3D> pointsOrig;
	for(unsigned int ui=0;ui<data.size();ui++)
	{
		if(data[ui]->getStreamType() != STREAM_TYPE_IONS)
			continue;

		const IonStreamData *ionD;
		ionD = (IonStreamData*)data[ui];

		for(unsigned int ui=0;ui<ionD->data.size();ui++)
		{
			Point3D p;
			p = ionD->data[ui].getPos();
			pointsOrig.push_back(p); 
		}
	}

	bCube.setBounds(pointsOrig);

	//FIXME: Remove me
	//---
	//Due to a bug when points are in some particular (near-spherical) configurations
	// the points have to be joggled a little
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_real_distribution<> realV(-1,1);

	vector<R3> pts;
	pts.resize(pointsOrig.size());
	for(unsigned int ui=0;ui<pts.size();ui++)
	{
		//Noise the points to 1/10,000th of the bbox size
		pointsOrig[ui]+=Point3D(realV(gen),realV(gen),realV(gen))*bCube.getSize()*1.0/5e3;
		pts[ui] = R3(pointsOrig[ui][0],pointsOrig[ui][1],pointsOrig[ui][2]);
	}
	//---

	//Run the convex hull calculation
	vector<Tri> hull; 
	NewtonApple_hull_3D(pts,hull);


	//Find all unique points
	set<size_t> ptIdx;
	for(unsigned int ui=0;ui<hull.size();ui++)
	{
		ptIdx.insert(hull[ui].a);
		ptIdx.insert(hull[ui].b);
		ptIdx.insert(hull[ui].c);
	
		HULLTRI h;
		h.vertex[0] = hull[ui].a;
		h.vertex[1] = hull[ui].b;
		h.vertex[2] = hull[ui].c;
		facets.push_back(h);
	}

	curHull.reserve(ptIdx.size()/3); //This is a guess, depends on ajacency of each vertex
	for(std::set<size_t>::iterator it=ptIdx.begin(); it!=ptIdx.end();++it)	
	{
		curHull.push_back(
			Point3D(pts[*it].r,pts[*it].c,pts[*it].z));
	}

	return 0;
}

unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progress,
				const bool &abortPtr,std::vector<Point3D> &curHull)
{
	//Copy the points into the format required by shull
	vector<R3> pts;
	pts.resize(data.size());
	for(unsigned int ui=0;ui<data.size();ui++)
		pts[ui]=R3(data[ui][0],data[ui][1],data[ui][2]);

	//Run the convex hull calculation
	vector<Tri> hull; 
	NewtonApple_hull_3D(pts,hull);

	//Find all unique points
	set<size_t> ptIdx;
	for(unsigned int ui=0;ui<hull.size();ui++)
	{
		ptIdx.insert(hull[ui].a);
		ptIdx.insert(hull[ui].b);
		ptIdx.insert(hull[ui].c);
	}

	curHull.reserve(ptIdx.size()/3); //This is a guess, depends on ajacency of each vertex
	for(std::set<size_t>::iterator it=ptIdx.begin(); it!=ptIdx.end();++it)	
	{
		curHull.push_back(
			Point3D(pts[*it].r,pts[*it].c,pts[*it].z));
	}
	return 0;
}

unsigned int do3DDelaunay(vector<Point3D> &pts,
		vector<size_t> &ta, vector<size_t> &tb, vector<size_t> &tc)
{
	vector<R3> inPts;
	inPts.resize(pts.size());
#pragma omp parallel  shared(inPts)
	{
#pragma omp for
	for(unsigned int ui=0; ui<pts.size(); ui++)
		inPts[ui]= R3(pts[ui][0],pts[ui][1],pts[ui][2]);

	vector<Tri> triVec;
	if(NewtonApple_Delaunay( inPts, triVec) != 1)
		return 1;

	ta.resize(triVec.size());
	tb.resize(triVec.size());
	tc.resize(triVec.size());
#pragma omp for
	for(unsigned int ui=0;ui<triVec.size();ui++)
	{
		ta[ui]=triVec[ui].a;
		tb[ui]=triVec[ui].b;
		tc[ui]=triVec[ui].c;
	}
	}

	return 0;
}

unsigned int do2DDelaunay(double *bufXY, unsigned int n, 
		vector<size_t> &ta, vector<size_t> &tb, vector<size_t> &tc)
{
	vector<R3> inPts;
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_real_distribution<> realV(-0.001,0.001);

	inPts.resize(n);
	for(unsigned int ui=0; ui<n; ui++)
	{
		inPts[ui].r=bufXY[2*ui];
		inPts[ui].c=bufXY[2*ui+1];
		inPts[ui].z=realV(gen);
	}
	vector<Tri> triVec;
	if(NewtonApple_Delaunay( inPts, triVec) != 1)
		return 1;

	ta.resize(triVec.size());
	tb.resize(triVec.size());
	tc.resize(triVec.size());
	for(unsigned int ui=0;ui<triVec.size();ui++)
	{
		ta[ui]=triVec[ui].a;
		tb[ui]=triVec[ui].b;
		tc[ui]=triVec[ui].c;
	}

	return 0;
}

unsigned int GetReducedHullPts(const vector<Point3D> &points, float reductionDim,  
		unsigned int *progress, ATOMIC_BOOL &wantAbort, vector<Point3D> &pointResult)
{
	ASSERT(false); //IMPLEMENT ME
}
#else

//QHULL edition

//grab size when doing convex hull calculations
const unsigned int HULL_GRAB_SIZE=4096;

bool qhullInited=false;

//Wrapper for qhull single-pass run
unsigned int doHull(unsigned int bufferSize, double *buffer, 
			vector<Point3D> &resHull, Point3D &midPoint,	
			bool wantVolume, bool freeHullOnExit);
//FIXME: Abort pointer?
unsigned int computeConvexHull(const vector<const FilterStreamData*> &data, unsigned int *progress,
					std::vector<Point3D> &curHull, bool wantVolume,bool freeHull)
{
	//If you want volume, you have to not free the hull at this point
	// - can't have both!
	ASSERT(!(wantVolume && freeHull));

	size_t numPts;
	numPts=numElements(data,STREAM_TYPE_IONS);
	//Easy case of no data
	if(numPts < 4)
		return 0;

	double *buffer;
	double *tmp;
	//Use malloc so we can re-alloc
	buffer =(double*) malloc(HULL_GRAB_SIZE*3*sizeof(double));

	if(!buffer)
		return HULL_ERR_NO_MEM;

	size_t bufferOffset=0;

	//Do the convex hull in steps for two reasons
	// 1) qhull chokes on large data
	// 2) we need to check for abort every now and again, so we have to
	//   work in batches.
	Point3D midPoint;
	float maxSqrDist=-1;
	size_t n=0;
	for(size_t ui=0; ui<data.size(); ui++)
	{
		if(data[ui]->getStreamType() != STREAM_TYPE_IONS)
			continue;

		const IonStreamData* ions=(const IonStreamData*)data[ui];

		for(size_t uj=0; uj<ions->data.size(); uj++)
		{		//Do contained-in-sphere check
			if(!curHull.size() || midPoint.sqrDist(ions->data[uj].getPos())>= maxSqrDist)
			{
				//Copy point data into hull buffer
				buffer[3*bufferOffset]=ions->data[uj].getPos()[0];
				buffer[3*bufferOffset+1]=ions->data[uj].getPos()[1];
				buffer[3*bufferOffset+2]=ions->data[uj].getPos()[2];
				bufferOffset++;

				//If we have hit the hull grab size, perform a hull

				if(bufferOffset == HULL_GRAB_SIZE)
				{
					bufferOffset+=curHull.size();
					tmp=(double*)realloc(buffer,
							     3*bufferOffset*sizeof(double));
					if(!tmp)
					{
						free(buffer);

						return HULL_ERR_NO_MEM;
					}

					buffer=tmp;
					//Copy in the old hull
					for(size_t uk=0; uk<curHull.size(); uk++)
					{
						buffer[3*(HULL_GRAB_SIZE+uk)]=curHull[uk][0];
						buffer[3*(HULL_GRAB_SIZE+uk)+1]=curHull[uk][1];
						buffer[3*(HULL_GRAB_SIZE+uk)+2]=curHull[uk][2];
					}

					unsigned int errCode=0;
					
					errCode=doHull(bufferOffset,buffer,curHull,midPoint,wantVolume,freeHull);
					if(errCode)
					{
						free(buffer);
						return errCode;
					}


					//Now compute the min sqr distance
					//to the vertex, so we can fast-reject
					maxSqrDist=std::numeric_limits<float>::max();
					for(size_t ui=0; ui<curHull.size(); ui++)
						maxSqrDist=std::min(maxSqrDist,curHull[ui].sqrDist(midPoint));
					//reset buffer size
					bufferOffset=0;
				}

			}
			n++;

			//Update the progress information, and run abort check
			if(*Filter::wantAbort)
			{
				free(buffer);
				return HULL_ERR_USER_ABORT;
			}

			*progress= (unsigned int)((float)(n)/((float)numPts)*100.0f);

		}
	}

	//Need at least 4 objects to construct a sufficiently large buffer
	if(bufferOffset + curHull.size() >= 4)
	{
		//Re-allocate the buffer to determine the last hull size
		tmp=(double*)realloc(buffer,
		                     3*(bufferOffset+curHull.size())*sizeof(double));
		if(!tmp)
		{
			free(buffer);
			return HULL_ERR_NO_MEM;
		}
		buffer=tmp;

		#pragma omp parallel for
		for(unsigned int ui=0; ui<curHull.size(); ui++)
		{
			buffer[3*(bufferOffset+ui)]=curHull[ui][0];
			buffer[3*(bufferOffset+ui)+1]=curHull[ui][1];
			buffer[3*(bufferOffset+ui)+2]=curHull[ui][2];
		}

		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,
						curHull,midPoint,wantVolume,freeHull);

		if(errCode)
		{
			free(buffer);
			//FIXME: Free the last convex hull mem??
			return errCode;
		}
	}


	free(buffer);
	return 0;
}

unsigned int computeConvexHull(const vector<Point3D> &data, unsigned int *progress,
				const bool &abortPtr,std::vector<Point3D> &curHull, bool wantVolume, bool freeHull)
{

	//Easy case of no data
	if(data.size()< 4)
		return 0;

	double *buffer;
	double *tmp;
	//Use malloc so we can re-alloc
	buffer =(double*) malloc(HULL_GRAB_SIZE*3*sizeof(double));

	if(!buffer)
		return HULL_ERR_NO_MEM;

	size_t bufferOffset=0;

	//Do the convex hull in steps for two reasons
	// 1) qhull chokes on large data
	// 2) we need to run the callback every now and again, so we have to
	//   work in batches.
	Point3D midPoint;
	float maxSqrDist=-1;



	for(size_t uj=0; uj<data.size(); uj++)
	{
		//Do contained-in-sphere check
		if(!curHull.size() || midPoint.sqrDist(data[uj])>= maxSqrDist)
		{
		
			//Copy point data into hull buffer
			buffer[3*bufferOffset]=data[uj][0];
			buffer[3*bufferOffset+1]=data[uj][1];
			buffer[3*bufferOffset+2]=data[uj][2];
			bufferOffset++;

			//If we have hit the hull grab size, perform a hull

			if(bufferOffset == HULL_GRAB_SIZE)
			{
				bufferOffset+=curHull.size();
				tmp=(double*)realloc(buffer,
						     3*bufferOffset*sizeof(double));
				if(!tmp)
				{
					free(buffer);

					return HULL_ERR_NO_MEM;
				}

				buffer=tmp;
				//Copy in the old hull
				for(size_t uk=0; uk<curHull.size(); uk++)
				{
					buffer[3*(HULL_GRAB_SIZE+uk)]=curHull[uk][0];
					buffer[3*(HULL_GRAB_SIZE+uk)+1]=curHull[uk][1];
					buffer[3*(HULL_GRAB_SIZE+uk)+2]=curHull[uk][2];
				}

				unsigned int errCode=0;
				
				errCode=doHull(bufferOffset,buffer,curHull,midPoint,wantVolume,freeHull);
				if(errCode)
					return errCode;


				//Now compute the min sqr distance
				//to the vertex, so we can fast-reject
				maxSqrDist=std::numeric_limits<float>::max();
				for(size_t ui=0; ui<curHull.size(); ui++)
					maxSqrDist=std::min(maxSqrDist,curHull[ui].sqrDist(midPoint));
				//reset buffer size
				bufferOffset=0;
			}
		}

		if(*Filter::wantAbort)
		{
			free(buffer);
			return HULL_ERR_USER_ABORT;
		}

		*progress= (unsigned int)((float)(uj)/((float)data.size())*100.0f);

	}


	//Build the final hull, using the remaining points, and the
	// filtered hull points
	//Need at least 4 objects to construct a sufficiently large buffer
	if(bufferOffset + curHull.size() > 4)
	{
		//Re-allocate the buffer to determine the last hull size
		tmp=(double*)realloc(buffer,
		                     3*(bufferOffset+curHull.size())*sizeof(double));
		if(!tmp)
		{
			free(buffer);
			return HULL_ERR_NO_MEM;
		}
		buffer=tmp;

		#pragma omp parallel for
		for(unsigned int ui=0; ui<curHull.size(); ui++)
		{
			buffer[3*(bufferOffset+ui)]=curHull[ui][0];
			buffer[3*(bufferOffset+ui)+1]=curHull[ui][1];
			buffer[3*(bufferOffset+ui)+2]=curHull[ui][2];
		}

		unsigned int errCode=doHull(bufferOffset+curHull.size(),buffer,curHull,midPoint,wantVolume,freeHull);

		if(errCode)
		{
			free(buffer);
			//Free the last convex hull mem
			return errCode;
		}
	}


	free(buffer);
	return 0;
}

unsigned int doHull(unsigned int bufferSize, double *buffer, 
			vector<Point3D> &resHull, Point3D &midPoint, bool wantVolume ,
			bool freeHullOnExit)
{
	if(qhullInited)
	{
		qh_freeqhull(qh_ALL);
		int curlong,totlong;
		//This seems to be required? Cannot find any documentation on the difference
		// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
		qh_memfreeshort (&curlong, &totlong);    
		qhullInited=false;
	}


	const int dim=3;
	//Now compute the new hull
	//Generate the convex hull
	//(result is stored in qh's globals :(  )
	//note that the input is "joggled" to 
	//ensure simplicial facet generation

	//Qhull >=2012 has a "feature" where it won't accept null arguments for the output
	// there is no clear way to shut it up.
	FILE *outSquelch=0;
#if defined(__linux__) || defined(__APPLE__) || defined(__BSD__)
	outSquelch=fopen("/dev/null","w");
#elif defined(__win32__) || defined(__win64__)
	outSquelch=fopen("NUL","w");
#endif

	if(!outSquelch)
	{
		//Give up, just let qhull output random statistics to stderr
		outSquelch=stderr;
	}

	 //Joggle the output, such that only simplical facets are generated, Also compute area/volume
	const char *argsOptions[]= { "qhull QJ FA",
				   "qhull QJ"
					};

	const char *args;
	if(wantVolume)
		args = argsOptions[0];
	else
		args=argsOptions[1];
	

	if(qh_new_qhull(	dim,
			bufferSize,
			buffer,
			false,
			(char *)args ,
			outSquelch, //QHULL's interface is bizarre, no way to set null pointer in qhull 2012 - result is inf. loop in qhull_fprintf and error reporting func. 
			outSquelch))
	{
		return HULL_ERR_QHULL_NOINIT;
	}
	qhullInited=true;

	if(outSquelch !=stderr)
	{
		fclose(outSquelch);
	}

	unsigned int numPoints=0;
	//count points
	//--	
	//OKay, whilst this may look like invalid syntax,
	//qh is actually a macro from qhull
	//that creates qh. or qh-> as needed
	numPoints = qh num_vertices;	
	//--	
	midPoint=Point3D(0,0,0);	

	if(!numPoints)
		return 0;

	//store points in vector
	//--
	try
	{
		resHull.resize(numPoints);	
	}
	catch(std::bad_alloc)
	{
		return HULL_ERR_NO_MEM;
	}
	//--

	//Compute mean point
	//--
	vertexT *vertex;
	vertex= qh vertex_list;
	int curPt=0;
	while(vertex != qh vertex_tail)
	{
		resHull[curPt]=Point3D(vertex->point[0],
				vertex->point[1],
				vertex->point[2]);
		midPoint+=resHull[curPt];
		curPt++;
		vertex = vertex->next;
	}
	midPoint*=1.0f/(float)numPoints;
	//--
	if(freeHullOnExit)
	{
		qh_freeqhull(qh_ALL);
		int curlong,totlong;
		//This seems to be required? Cannot find any documentation on the difference
		// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
		qh_memfreeshort (&curlong, &totlong);    
		qhullInited=false;
	
	}

	return 0;
}


unsigned int do3DDelaunay(vector<Point3D> &pts,
		vector<size_t> &ta, vector<size_t> &tb, vector<size_t> &tc)
{

	//Delaunay tesslation cannot be done
	// piecewise, unlike hull.  Ensure that
	// we are not in the middle of a different
	// calculation
	ASSERT(!qhullInited);

	double *buffer = new double[pts.size()*3];
	unsigned int bufferSize = pts.size();
	for(unsigned int ui=0;ui<pts.size();ui++)
	{
		buffer[ui*3] = pts[ui][0];
		buffer[ui*3+1] = pts[ui][1];
		buffer[ui*3+2] = pts[ui][2];
	}

	const int dim=3;
	//Now compute delaunay
	//(result is stored in qh's globals :(  )
	//note that the input is "joggled" to 
	//ensure simplicial facet generation

	//Qhull >=2012 has a "feature" where it won't accept null arguments for the output
	// there is no clear way to shut it up.
	FILE *outSquelch=0;
#if defined(__linux__) || defined(__APPLE__) || defined(__BSD__)
	outSquelch=fopen("/dev/null","w");
#elif defined(__win32__) || defined(__win64__)
	outSquelch=fopen("NUL","w");
#endif

	if(!outSquelch)
	{
		//Give up, just let qhull output random statistics to stderr
		outSquelch=stderr;
	}

	 //Joggle the output, such that only simplical facets are generated, Also compute area/volume
	const char *opts = "qhull d QbB Qz Qt";
	unsigned int isMalloc=0;
	//FIXME: PVS complains that false here should be a 
	if(qh_new_qhull(dim, bufferSize, buffer,
		isMalloc,(char*)opts,outSquelch,outSquelch))
		return HULL_ERR_QHULL_NOINIT;
	
	if(outSquelch !=stderr)
		fclose(outSquelch);


	facetT *facet;
	vertexT *vertex, **vertexp;

	FORALLfacets
	{
		if(!facet->simplicial)
			continue;
//		if(facet->upperdelaunay)
//			continue;
			
		unsigned int ui=0;
		FOREACHvertex_(facet->vertices)
		{
			unsigned int id;
			id= qh_pointid(vertex->point);
			switch(ui)
			{
				case 0:
					ta.push_back(id);
					break;
				case 1:
					tb.push_back(id);
					break;
				case 2:
					tc.push_back(id);
					break;
			}
			ui++;
		}
	}


	qh_freeqhull(!qh_ALL);
	int curlong,totlong;
	//This seems to be required? Cannot find any documentation on the difference
	// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
	qh_memfreeshort (&curlong, &totlong);    

	delete[] buffer;

	return 0;
}

unsigned int do2DDelaunay(double *bufXY, unsigned int n, 
		vector<size_t> &ta, vector<size_t> &tb, vector<size_t> &tc)
{

	//Delaunay tesslation cannot be done
	// piecewise, unlike hull.  Ensure that
	// we are not in the middle of a different
	// calculation
	ASSERT(!qhullInited);

	const int dim=2;
	//Now compute delaunay
	//(result is stored in qh's globals :(  )
	//note that the input is "joggled" to 
	//ensure simplicial facet generation

	//Qhull >=2012 has a "feature" where it won't accept null arguments for the output
	// there is no clear way to shut it up.
	FILE *outSquelch=0;
#if defined(__linux__) || defined(__APPLE__) || defined(__BSD__)
	outSquelch=fopen("/dev/null","w");
#elif defined(__win32__) || defined(__win64__)
	outSquelch=fopen("NUL","w");
#endif
	
	if(!outSquelch)
	{
		//Give up, just let qhull output random statistics to stderr
		outSquelch=stderr;
	}

	 //Joggle the output, such that only simplical facets are generated, Also compute area/volume
	const char *opts = "qhull d Qt";
	if(qh_new_qhull(dim, n,bufXY,
		false,(char*)opts,outSquelch,outSquelch))
		return HULL_ERR_QHULL_NOINIT;
	
	if(outSquelch !=stderr)
		fclose(outSquelch);


	facetT *facet;
	vertexT *vertex, **vertexp;

	FORALLfacets
	{
		if(!facet->simplicial)
			continue;
		if(facet->upperdelaunay)
			continue;
			
		unsigned int ui;
		unsigned int id[3];

		ui=0;
		FOREACHvertex_(facet->vertices)
		{
			id[ui]= qh_pointid(vertex->point);
			ASSERT(id[ui] < n);
			ui++;
		}

		//Drop the facets formed by adding higher dimensional point
		if(id[0] == n || id[1] == n || id[2] == n)
			continue;

		ta.push_back(id[0]);
		tb.push_back(id[1]);
		tc.push_back(id[2]);
	}


	qh_freeqhull(!qh_ALL);
	int curlong,totlong;
	//This seems to be required? Cannot find any documentation on the difference
	// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
	qh_memfreeshort (&curlong, &totlong);    

	return 0;
}

void freeConvexHull()
{
	qh_freeqhull(qh_ALL);
	int curlong,totlong;
	//This seems to be required? Cannot find any documentation on the difference
	// between qh_freeqhull and qh_memfreeshort. qhull appears to leak when just using qh_freeqhull
	qh_memfreeshort (&curlong, &totlong);    
	qhullInited=false;
}

//obtains all the input points from ions that lie inside the convex hull after
//it has been shrunk such that the closest distance from the hull to the original data
//is reductionDim 
unsigned int GetReducedHullPts(const vector<Point3D> &points, float reductionDim,  
		unsigned int *progress, ATOMIC_BOOL &wantAbort, vector<Point3D> &pointResult)
{
	//TODO: This could be made to use a fixed amount of ram, by
	//partitioning the input points, and then
	//computing multiple hulls.
	//Take the resultant hull points, then hull again. This would be
	//much more space efficient, and more easily parallellised
	//Alternately, compute a for a randoms K set of points, and reject
	//points that lie in the hull from further computation

	//Need at least 4 points to define a hull in 3D
	if(points.size() < 4) 
		return 1;

	unsigned int dummyProg;
	vector<Point3D> theHull;
	if(computeConvexHull(points,progress,wantAbort,theHull,false,false))
		return 2;

	Point3D midPoint(0,0,0);
	for(size_t ui=0;ui<theHull.size();ui++)
		midPoint+=theHull[ui];
	midPoint *= 1.0f/(float)theHull.size();

	//Now we will find the mass/volume centroid of the hull
	//by constructing sets of pyramids.
	//We cannot use the simple midpoint, as point distribution
	//around the hull surface may be uneven
	
	//Here the faces of the hull are the bases of 
	//pyramids, and the midpoint is the apex
	Point3D hullCentroid(0.0f,0.0f,0.0f);
	float massPyramids=0.0f;
	//Run through the faced list
	facetT *curFac = qh facet_list;
	
	while(curFac != qh facet_tail)
	{
		vertexT *vertex;
		Point3D pyramidCentroid;
		unsigned int ui;
		Point3D ptArray[3];
		float vol;

		//find the pyramid volume + centroid
		pyramidCentroid = midPoint;
		ui=0;
		
		//This assertion fails, some more processing is needed to be done to break
		//the facet into something simplical
		ASSERT(curFac->simplicial);
		vertex  = (vertexT *)curFac->vertices->e[ui].p;
		while(vertex)
		{	//copy the vertex info into the pt array
			(ptArray[ui])[0]  = vertex->point[0];
			(ptArray[ui])[1]  = vertex->point[1];
			(ptArray[ui])[2]  = vertex->point[2];
		
			//aggregate pyramidal points	
			pyramidCentroid += ptArray[ui];
		
			//increment before updating vertex
			//to allow checking for NULL termination	
			ui++;
			vertex  = (vertexT *)curFac->vertices->e[ui].p;
		
		}
		
		//note that this counter has been post incremented. 
		ASSERT(ui ==3);
		vol = pyramidVol(ptArray,midPoint);
		
		ASSERT(vol>=0);
		
		//Find the midpoint of the pyramid, this will be the 
		//same as its centre of mass.
		pyramidCentroid*= 0.25f;
		hullCentroid = hullCentroid + (pyramidCentroid*vol);
		massPyramids+=vol;

		curFac=curFac->next;
	}

	hullCentroid *= 1.0f/massPyramids;
	
	float minDist=std::numeric_limits<float>::max();
	//find the smallest distance between the centroid and the
	//convex hull
       	curFac=qh facet_list;
	while(curFac != qh facet_tail)
	{
		float temp;
		Point3D vertexPt[3];
		
		//The shortest distance from the plane to the point
		//is the dot product of the UNIT normal with 
		//A-B, where B is on plane, A is point in question
		for(unsigned int ui=0; ui<3; ui++)
		{
			vertexT *vertex;
			//grab vertex
			vertex  = ((vertexT *)curFac->vertices->e[ui].p);
			vertexPt[ui] = Point3D(vertex->point[0],vertex->point[1],vertex->point[2]);
		}

		//Find the distance between hull centroid and a given facet
		temp = distanceToFacet(vertexPt[0],vertexPt[1],vertexPt[2],hullCentroid,
					Point3D(curFac->normal[0],curFac->normal[1],curFac->normal[2]));

		if(temp < minDist)
			minDist = temp;
	
		curFac=curFac->next;
	}

	//shrink the convex hull such that it lies at
	//least reductionDim from the original surface of
	//the convex hull
	float scaleFactor;
	scaleFactor = 1  - reductionDim/ minDist;

	if(scaleFactor < 0.0f)
		return RDF_ERR_NEGATIVE_SCALE_FACT;

	
	//now scan through the input points and see if they
	//lie in the reduced convex hull
	vertexT *vertex = qh vertex_list;	

	unsigned int ui=0;
	while(vertex !=qh vertex_tail)
	{
		//Translate around hullCentroid before scaling, 
		//then undo translation after scale
		//Modify the vertex data such that it is scaled around the hullCentroid
		vertex->point[0] = (vertex->point[0] - hullCentroid[0])*scaleFactor + hullCentroid[0];
		vertex->point[1] = (vertex->point[1] - hullCentroid[1])*scaleFactor + hullCentroid[1];
		vertex->point[2] = (vertex->point[2] - hullCentroid[2])*scaleFactor + hullCentroid[2];
	
		vertex = vertex->next;
		ui++;
	}

	//if the dot product of the normal with the point vector of the
	//considered point P, to any vertex on all of the facets of the 
	//convex hull F1, F2, ... , Fn is negative,
	//then P does NOT lie inside the convex hull.
	pointResult.reserve(points.size()/2);
	curFac = qh facet_list;
	
	//minimum distance from centroid to convex hull
	for(unsigned int ui=points.size(); ui--;)
	{
		float fX,fY,fZ;
		double *ptArr,*normalArr;
		fX =points[ui][0];
		fY = points[ui][1];
		fZ = points[ui][2];
		
		//loop through the facets
		curFac = qh facet_list;
		while(curFac != qh facet_tail)
		{
			//Dont ask. It just grabs the first coords of the vertex
			//associated with this facet
			ptArr = ((vertexT *)curFac->vertices->e[0].p)->point;
			
			normalArr = curFac->normal;
			//if the dotproduct is negative, then the point vector from the 
			//point in question to the surface is in opposite to the outwards facing
			//normal, which means the point lies outside the hull	
			if (dotProduct( (float)ptArr[0]  - fX,
					(float)ptArr[1] - fY,
					(float)ptArr[2] - fZ,
					normalArr[0], normalArr[1],
					normalArr[2]) >= 0)
			{
				curFac=curFac->next;
				continue;
			}
			goto reduced_loop_next;
		}
		//we passed all tests, point is inside convex hull
		pointResult.push_back(points[ui]);
		
reduced_loop_next:
	;
	}

	freeConvexHull();

	return 0;
}
#endif


#ifdef DEBUG



bool testDelaunay()
{
	vector<Point3D> p;

	p.emplace_back(Point3D(0,0,1));
	p.emplace_back(Point3D(1,0,1));
	p.emplace_back(Point3D(1,1,1));
	p.emplace_back(Point3D(0,0,0));
	p.emplace_back(Point3D(-1,2,2));

	//just see if we can run the triangulation routine
	// to retrieve the triangulations
	vector<size_t> tA,tB,tC;
	TEST(do3DDelaunay(p,tA,tB,tC) ==0 , "Delaunay failed")

	//FIXME: Im not sure that qhull wont re-order the inputs.
	// need to check this
	// triangle vertex ids  should all be the same size (one for each vertex)
	TEST(tA.size() == tB.size(),"triangle id check");
	TEST(tB.size() == tC.size(),"triangle id check");


	p.clear();
	tA.clear();
	tB.clear();
	tC.clear();

	//Perform 2D delaunay check
	p.emplace_back(Point3D(0,1,0));
	p.emplace_back(Point3D(1,1,0));
	p.emplace_back(Point3D(1,0,0));
	p.emplace_back(Point3D(2,2,0));


	double *buffer = new double[p.size()*2];
	
	for(unsigned int ui=0;ui<p.size();ui++)
	{
		buffer[ui*2] = p[ui][0];
		buffer[ui*2+1] = p[ui][1];
	}

	TEST(do2DDelaunay(buffer,p.size(),tA,tB,tC) ==0 , "2D Delaunay failed")

	// triangle vertex ids  should all be the same size (one for each vertex)
	TEST(tA.size() == tB.size(),"triangle id check");
	TEST(tB.size() == tC.size(),"triangle id check");

	delete[] buffer;

	return true;
}


bool testConvexHull()
{
	Point3D pTet[4];

	pTet[0] = Point3D(0,0,0);
	pTet[1] = Point3D(0,0,1);
	pTet[2] = Point3D(0,1,0);
	pTet[3] = Point3D(1,0,0);

	IonStreamData *d = new IonStreamData;

	for(unsigned int ui=0;ui<4;ui++)
		d->data.emplace_back(IonHit(pTet[ui],ui));

	vector<const FilterStreamData*> dataStream;
	dataStream.push_back(d);
	unsigned int dummyProg;
	vector<Point3D> hull;
#ifdef HAVE_SWEEP_HULL
	vector<HULLTRI> facets;
	computeConvexHull(dataStream,&dummyProg,hull,facets);
	TEST(hull.size() == 4,"Hull pt count (sweep-hull)");
#else
	computeConvexHull(dataStream,&dummyProg,hull,false,false);
	TEST(hull.size() == 4,"Hull pt count (qhull)");
#endif


	double tVol,cVol;
	tVol = pyramidVol(pTet,pTet[3]);
	Point3D pConvex[4];
	for(unsigned int ui=0;ui<4;ui++)
		pConvex[ui] = hull[ui];

	cVol = pyramidVol(pConvex,pConvex[3]);
	TEST(EQ_TOL(cVol,tVol) ,"Hull volume");

	delete d;
	return true;

}

bool testConvexHull2()
{
	Point3D pCube[8];

	pCube[0] = Point3D(0,0,0);
	pCube[1] = Point3D(0,0,1);
	pCube[2] = Point3D(0,1,0);
	pCube[3] = Point3D(0,1,1);
	pCube[4] = Point3D(1,0,0);
	pCube[5] = Point3D(1,0,1);
	pCube[6] = Point3D(1,1,0);
	pCube[7] = Point3D(1,1,1);

	IonStreamData *d = new IonStreamData;

	for(unsigned int ui=0;ui<8;ui++)
		d->data.emplace_back(IonHit(pCube[ui],ui));

	vector<const FilterStreamData*> dataStream;
	dataStream.push_back(d);
	unsigned int dummyProg;
	vector<Point3D> hull;
#ifdef HAVE_SWEEP_HULL
	vector<HULLTRI> facets;
	computeConvexHull(dataStream,&dummyProg,hull,facets);
#else
	computeConvexHull(dataStream,&dummyProg,hull,true,false);

#endif

	TEST(hull.size() == 8,"Hull pt count");
#ifdef HAVE_SWEEP_HULL
	float cVol = getConvexVolume(hull,facets);
	TEST(EQ_TOLV(cVol,1.0f,1e-2) ,"Hull volume (cube, shull)");
#else
	TEST(EQ_TOL(qh totvol,1.0f),"Hull volume (cube, qhull)");
	freeConvexHull();
#endif

	delete d;
	return true;

}
bool testHull()
{
	cerr << "Hull tests " << endl;;
	//Delaunay is not implemented for sweep-hull
	TEST(testDelaunay(),"delaunay test");
	TEST(testConvexHull(), "convex hull test (tetrahedron)");
	TEST(testConvexHull2(), "convex hull test (cube)");
	return true;
}

#endif
