/*------------------------------------------------------------------------------*/
void GW_VoronoiMesh::BuildGeodesicParametrization( GW_GeodesicMesh& Mesh )
{
	Mesh.RegisterForceStopCallbackFunction( FastMarchingCallbackFunction_Parametrization );
	Mesh.ResetParametrizationData();
	/* for each base vertex, perform front propagation on the surrounding faces */
	for( GW_U32 i=0; i<this->GetNbrVertex(); ++i )
	{
		CurrentTargetVertex_.clear();
		pCurrentVoronoiVertex_ = (GW_VoronoiVertex*) this->GetVertex(i);	// set up global data for callback function
		GW_ASSERT( pCurrentVoronoiVertex_!=NULL );

		GW_OutputComment("Performing local fast marching.");
		this->PerformLocalFastMarching( Mesh, *pCurrentVoronoiVertex_ );

		/* register geodesic boundaries length */
		for( GW_VertexIterator it=pCurrentVoronoiVertex_->BeginVertexIterator(); it!=pCurrentVoronoiVertex_->EndVertexIterator(); ++it )
		{
			GW_VoronoiVertex* pNeighbor = (GW_VoronoiVertex*) *it;
			GW_ASSERT( pNeighbor!=NULL );
			GW_GeodesicVertex* pGeodesicVert = pNeighbor->GetBaseVertex();
			GW_ASSERT( pGeodesicVert!=NULL );
			GW_U32 nID = GW_Vertex::ComputeUniqueId( *pCurrentVoronoiVertex_, *pNeighbor );
			if( GeodesicDistanceMap_.find(nID)==GeodesicDistanceMap_.end() )
			{
				GW_ASSERT( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead );
				/* this is the first time the distance is computed */
				if( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead )
					GeodesicDistanceMap_[nID]	 = pGeodesicVert->GetDistance();
			}
			else
			{
				GW_ASSERT( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead );
				if( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead )
				{
					/* distance alreay computed in the reverse direction : take the average */
					GW_Float rPrevDist = GeodesicDistanceMap_[nID];
					GW_Float rNewDist = pGeodesicVert->GetDistance();
					GeodesicDistanceMap_[nID] = GW_MIN(rPrevDist, rNewDist); //(rPrevDist+rNewDist)*0.5;
				}
			}
		}

	}

	/* now really compute the 3 parameters for each vertex */
	GW_OutputComment("Computing geodesic parametrisation.");
	this->ComputeVertexParameters( Mesh );

	pCurrentVoronoiVertex_ = NULL;
	Mesh.RegisterForceStopCallbackFunction( NULL );

}
/*------------------------------------------------------------------------------*/
GW_Bool GW_VoronoiMesh::FastMarchingCallbackFunction_Boundaries( GW_GeodesicVertex& CurVert )
{
	// \todo for the moment propagate on the full neighborhood
//	for( IT_VoronoiVertexList it=CurrentTargetVertex_.begin(); it!=CurrentTargetVertex_.end(); ++it )
//	{
	GW_ASSERT( pCurrentVoronoiVertex_!=NULL );
	for( GW_VertexIterator it=pCurrentVoronoiVertex_->BeginVertexIterator(); it!=pCurrentVoronoiVertex_->EndVertexIterator(); ++it )
	{
		GW_VoronoiVertex* pVornoiVert = (GW_VoronoiVertex*) *it;
		GW_ASSERT( pVornoiVert!=NULL );
		GW_GeodesicVertex* pVert = pVornoiVert->GetBaseVertex();
		GW_ASSERT( pVert!=NULL );
		if( pVert->GetState()!=GW_GeodesicVertex::kDead )
			return GW_False;
	}
	return GW_True;
}
/*------------------------------------------------------------------------------*/
GW_Vertex* GW_GeodesicMesh::GetRandomVertex(GW_Bool bForceFar)
{
	GW_U32 nNumber = 0;
	GW_GeodesicVertex* pStartVertex = NULL;
	while( pStartVertex==NULL )
	{
		if( nNumber>=this->GetNbrVertex()/10 )
			return NULL;
		GW_U32 nNumVert = (GW_U32) floor(GW_RAND*this->GetNbrVertex());
		pStartVertex = (GW_GeodesicVertex*) this->GetVertex( nNumVert );
		if( bForceFar==GW_True && pStartVertex->GetState()!=GW_GeodesicVertex::kFar )
			pStartVertex = NULL;
		if( pStartVertex!=NULL && pStartVertex->GetFace()==NULL )
			pStartVertex = NULL;
		nNumber++;
	}
	return pStartVertex;
}
void mexFunction(	int nlhs, mxArray *plhs[], 
				 int nrhs, const mxArray*prhs[] ) 
{ 
	nbr_iter = 0;
	/* retrive arguments */
	if( nrhs<6 ) 
		mexErrMsgTxt("6 or 7 input arguments are required."); 
	if( nlhs<1 ) 
		mexErrMsgTxt("1 or 2 output arguments are required."); 

	// arg1 : vertex
	vertex = mxGetPr(prhs[0]);
	nverts = mxGetN(prhs[0]); 
	if( mxGetM(prhs[0])!=3 )
		mexErrMsgTxt("vertex must be of size 3 x nverts."); 
	// arg2 : faces
	faces = mxGetPr(prhs[1]);
	nfaces = mxGetN(prhs[1]);
	if( mxGetM(prhs[1])!=3 )
		mexErrMsgTxt("face must be of size 3 x nfaces."); 
	// arg3 : W
	Ww = mxGetPr(prhs[2]);
	int m = mxGetM(prhs[2]);
	if( m!=nverts )
		mexErrMsgTxt("W must be of same size as vertex."); 
	// arg4 : start_points
	start_points = mxGetPr(prhs[3]);
	nstart = mxGetM(prhs[3]);
	// arg5 : end_points
	end_points = mxGetPr(prhs[4]);
	nend = mxGetM(prhs[4]);
	// arg6 : niter_max
	niter_max = (int) *mxGetPr(prhs[5]);
	// arg7 : H
	if( nrhs>=7 )
	{
		H = mxGetPr(prhs[6]);
		int m =mxGetM(prhs[6]);
		if( m>0 && m!=nverts )
			mexErrMsgTxt("H must be of size nverts."); 
		if( m==0 )
			H = NULL;
	}
	else
	{
		H = NULL;
	}
	// arg8 : L
	if( nrhs>=8 )
	{
		L = mxGetPr(prhs[7]);
		int m =mxGetM(prhs[7]);
		if( m>0 && mxGetM(prhs[7])!=nverts )
			mexErrMsgTxt("L must be of size nverts."); 
		if( m==0 )
			L = NULL;
	}
	else
		L = NULL;
		
	// argument 9: value list
	if( nrhs>=9 )
	{
		values = mxGetPr(prhs[8]);
		if( mxGetM(prhs[8])==0 && mxGetN(prhs[8])==0 )
			values=NULL;
		if( values!=NULL && (mxGetM(prhs[8])!=nstart || mxGetN(prhs[8])!=1) )
			mexErrMsgTxt("values must be of size nb_start_points x 1."); 
	}
	else
		values = NULL;
	// argument 10: dmax
	if( nrhs>=9 )
		dmax = *mxGetPr(prhs[9]);
	else
		dmax = 1e9;


	// first ouput : distance
	plhs[0] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	D = mxGetPr(plhs[0]);
	// second output : state
	plhs[1] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	S = mxGetPr(plhs[1]);
	// second output : segmentation
	plhs[2] = mxCreateDoubleMatrix(nverts, 1, mxREAL); 
	Q = mxGetPr(plhs[2]);

	// create the mesh
	GW_GeodesicMesh Mesh;
	Mesh.SetNbrVertex(nverts);
	for( int i=0; i<nverts; ++i )
	{
		GW_GeodesicVertex& vert = (GW_GeodesicVertex&) Mesh.CreateNewVertex();
		vert.SetPosition( GW_Vector3D(vertex_(0,i),vertex_(1,i),vertex_(2,i)) );
		Mesh.SetVertex(i, &vert);
	}
	Mesh.SetNbrFace(nfaces);
	for( int i=0; i<nfaces; ++i )
	{
		GW_GeodesicFace& face = (GW_GeodesicFace&) Mesh.CreateNewFace();
		GW_Vertex* v1 = Mesh.GetVertex((int) faces_(0,i)); GW_ASSERT( v1!=NULL );
		GW_Vertex* v2 = Mesh.GetVertex((int) faces_(1,i)); GW_ASSERT( v2!=NULL );
		GW_Vertex* v3 = Mesh.GetVertex((int) faces_(2,i)); GW_ASSERT( v3!=NULL );
		face.SetVertex( *v1,*v2,*v3 );
		Mesh.SetFace(i, &face);
	}
	Mesh.BuildConnectivity();

	// set up fast marching	
	Mesh.ResetGeodesicMesh();
	for( int i=0; i<nstart; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) start_points[i]);
		GW_ASSERT( v!=NULL );
		Mesh.AddStartVertex( *v );
	}
	Mesh.SetUpFastMarching();
	Mesh.RegisterWeightCallbackFunction( WeightCallback );
	Mesh.RegisterForceStopCallbackFunction( StopMarchingCallback );
	Mesh.RegisterVertexInsersionCallbackFunction( InsersionCallback );
	if( H!=NULL )
		Mesh.RegisterHeuristicToGoalCallbackFunction( HeuristicCallback );
	// initialize the distance of the starting points
	if( values!=NULL )
	for( int i=0; i<nstart; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) start_points[i]);
		GW_ASSERT( v!=NULL );
		v->SetDistance( values[i] );
	}
	
	// perform fast marching
//	display_message("itermax=%d", niter_max);
	Mesh.PerformFastMarching();

	// output result
	for( int i=0; i<nverts; ++i )
	{
		GW_GeodesicVertex* v = (GW_GeodesicVertex*) Mesh.GetVertex((GW_U32) i);
		GW_ASSERT( v!=NULL );
		D[i] = v->GetDistance();
		S[i] = v->GetState();
		GW_GeodesicVertex* v1 = v->GetFront();
		if( v1==NULL )
			Q[i] = -1;
		else
			Q[i] = v1->GetID();
	}
	

	return;
}
/*------------------------------------------------------------------------------*/
void GW_VoronoiMesh::BuildGeodesicBoundaries( GW_GeodesicMesh& Mesh )
{
	Mesh.RegisterForceStopCallbackFunction( FastMarchingCallbackFunction_Boundaries );

	VertexPathMap_.clear();
	GeodesicDistanceMap_.clear();
	BoundaryEdgeMap_.clear();

	for( GW_U32 i=0; i<this->GetNbrVertex(); ++i )
	{
		CurrentTargetVertex_.clear();
		pCurrentVoronoiVertex_ = (GW_VoronoiVertex*) this->GetVertex(i);
		GW_ASSERT( pCurrentVoronoiVertex_!=NULL );

		/* set the list of vertex to which the boundary needs to be computed */
		for( GW_VertexIterator it=pCurrentVoronoiVertex_->BeginVertexIterator(); it!=pCurrentVoronoiVertex_->EndVertexIterator(); ++it )
		{
			GW_VoronoiVertex* pVert = (GW_VoronoiVertex*) *it;
			GW_ASSERT( pVert!=NULL );
			GW_U32 nID = GW_Vertex::ComputeUniqueId( *pCurrentVoronoiVertex_, *pVert );
			if( VertexPathMap_.find( nID )==VertexPathMap_.end() )
				CurrentTargetVertex_.push_back( pVert );
		}

		/* compute fast marching */
		GW_GeodesicVertex* pGeodesicVert = pCurrentVoronoiVertex_->GetBaseVertex();
		GW_ASSERT( pGeodesicVert!=NULL );
		Mesh.ResetGeodesicMesh();
		GW_OutputComment("Performing Fast Marching.");
		Mesh.PerformFastMarching( pGeodesicVert );

		/* track back and compute geodesic path */
		for( IT_VoronoiVertexList it=CurrentTargetVertex_.begin(); it!=CurrentTargetVertex_.end(); ++it )
		{
			GW_VoronoiVertex* pVert = *it;
			GW_ASSERT( pVert!=NULL );
			GW_GeodesicVertex* pGeodesicVert = pVert->GetBaseVertex();
			GW_ASSERT( pGeodesicVert!=NULL );
			GW_ASSERT( pGeodesicVert->GetState()==GW_GeodesicVertex::kDead );
			GW_U32 nID = GW_Vertex::ComputeUniqueId( *pCurrentVoronoiVertex_, *pVert );
			GW_ASSERT( VertexPathMap_.find( nID ) ==VertexPathMap_.end() );
			T_GeodesicVertexList* pVertexGeodesicEdge = new T_GeodesicVertexList;
			VertexPathMap_[nID] = pVertexGeodesicEdge;
			/* track back and compute the path */
			GW_OutputComment("Computing geodesic path.");
			GW_GeodesicPath GeodesicPath;
			GeodesicPath.ComputePath( *pGeodesicVert, 5000 );
			/* convert the path into vertex */
			this->AddPathToMeshVertex( Mesh, GeodesicPath, *pVertexGeodesicEdge );
		}			
	}

	/* \todo This is just a debug test, remove it */
	for( IT_VertexPathMap it = VertexPathMap_.begin(); it!=VertexPathMap_.end(); ++it )
	{
		T_GeodesicVertexList* pPath = it->second;
		GW_GeodesicVertex* pPrevVert = NULL;
		GW_U32 nNum = 0;
		for( IT_GeodesicVertexList itVert = pPath->begin(); itVert!=pPath->end(); ++itVert )
		{
			GW_GeodesicVertex* pVert = *itVert;
			if( pPrevVert==NULL )
				pPrevVert = pVert;
			else
			{
				GW_Face* pFace1, *pFace2;
				pPrevVert->GetFaces( *pVert, pFace1, pFace2 );
				// GW_ASSERT( pFace1!=NULL || pFace2!=NULL )	// \todo FIX THIS BUG
				if( pFace1!=NULL || pFace2!=NULL )
				{
//					cout << "Gap in a geodesic boundary." << endl;
				}					
				pPrevVert = pVert;
			}
			nNum++;
		}
	}

	Mesh.RegisterForceStopCallbackFunction( NULL );
	Mesh.ResetGeodesicMesh();
}