/**
 * Calls the given function on all vertices that are on a boundary and have a normal and curvature
 * @param callback The function to call
 * @param material The material type (default ROCK)
 */
void StoneWeatherer::callOnVertices( void ( *callback )( const Vertex & v ), Contents material ) const {

	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		if ( it->info().kill <= BORDER && ( it->info().flag & material ) ) {

			callback( *it );
		}
	}
}
/**
 * Executes one custom timestep
 * @param averageEdgeLength The function to compute the average edge length for a given vertex
 * @param getOffsetPoint Computes the new location of the given point
 * @return The results of running the timestep
 */
stepResults StoneWeatherer::doOneCustomStep( double ( *averageEdgeLength )( const Vertex_handle & v ), Point( *getOffsetPoint )( const Point & p, const VertexData & d, const StoneWeatherer * caller ) ) {

	CGAL::Timer timestamp;
	stepResults result;
	result.secondsTotal = 0.0;
	timestamp.start();
	timestamp.reset();
	
	/**
	 * Maps circumcenters to where they really came from
	 */
	 map<Point,Point> pointMap;

	// Generate the correct-resolution offset points
	vector<Point> newPoints;
	int n;
	int i;
	int j;
	Vertex_handle vhi;
	Vertex_handle vhj;
	double dist2;
	Point a;
	Point b;
	Point c;
	VertexData d;

	// Put in midPoints of edges as needed, and flag redundant vertices
	for ( Cell_iterator it = newDT->finite_cells_begin(); it != newDT->finite_cells_end(); ++it ) {

		if ( it->info() != AIR ) {
			
			for ( n = 0; n < 4; ++n ) {

				if ( it->neighbor( n )->info() != it->info() || newDT->is_infinite( it->neighbor( n ) ) ) {

					for ( i = 1; i < 4; ++i ) {

						Vertex_handle vhi = it->vertex( n ^ i );

						for ( j = i + 1; j < 4; ++j ) {

							Vertex_handle vhj = it->vertex( n ^ j );

							// Only check each edge once...
							if ( vhi < vhj ) {

								dist2 = ( vhi->point() - vhj->point() ).squared_length();

								if ( dist2 > maxSquareDistance( averageEdgeLength, vhi, vhj ) ) {

									// Don't split non-live edges
									a = vhi->point();
									b = vhj->point();

									if ( !live( a.x(), a.y(), a.z() ) && !live( b.x(), b.y(), b.z() ) ) {

										continue;
									}

									// Split edge
									a = CGAL::ORIGIN + ( ( a - CGAL::ORIGIN ) + ( b - CGAL::ORIGIN ) ) * 0.5;
									d = VertexData::midPoint( vhi->info(), vhj->info() );
									
									//// If the vertex is shared by both objects, split it
									//if ( ( d.flag & ROCK ) && ( d.flag & MORE_ROCK ) ) {

									//	VertexData d1;
									//	VertexData d2;
									//	splitVertexData( d, d1, d2 );
									//	
									//	Point a1 = jitterPoint( a );
									//	Point a2 = jitterPoint( a );
									//	
									//	c = getOffsetPoint( a1, d1, this );
									//	newPoints.push_back( c );
									//	pointMap.insert( pair<Point,Point>( c, a1 ) );

									//	c = getOffsetPoint( a2, d2, this );
									//	newPoints.push_back( c );
									//	pointMap.insert( pair<Point,Point>( c, a2 ) );
									//}
									//else {

										c = getOffsetPoint( a, d, this );
										newPoints.push_back( c );
										pointMap.insert( pair<Point,Point>( c, a ) );
									//}
								}
								else if ( vhi->info().kill != TOO_CLOSE && dist2 < minSquareDistance( averageEdgeLength, vhi, vhj ) ) {

									// The higher-address endpoint
									vhj->info().kill = TOO_CLOSE;
								}
							}
						}
					}
				}
			}
		}
	}

	double bound[ 3 ][ 2 ] = {
		{ 1.0e30, -1.0e30 },
		{ 1.0e30, -1.0e30 },
		{ 1.0e30, -1.0e30 }
	};

	// Put in all the border vertices
	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		if ( it->point().x() < bound[ 0 ][ 0 ] ) {

			bound[ 0 ][ 0 ] = it->point().x();
		}
		else if ( it->point().x() > bound[ 0 ][ 1 ] ) {

			bound[ 0 ][ 1 ] = it->point().x();
		}

		if ( it->point().y() < bound[ 1 ][ 0 ] ) {

			bound[ 1 ][ 0 ] = it->point().y();
		}
		else if ( it->point().y() > bound[ 1 ][ 1 ] ) {

			bound[ 1 ][ 1 ] = it->point().y();
		}

		if ( it->point().z() < bound[ 2 ][ 0 ] ) {

			bound[ 2 ][ 0 ] = it->point().z();
		}
		else if ( it->point().z() > bound[ 2 ][ 1 ] ) {

			bound[ 2 ][ 1 ] = it->point().z();
		}

		if ( !live( it->point().x(), it->point().y(), it->point().z() ) ) {

			newPoints.push_back( it->point() );
			pointMap.insert( pair<Point,Point>( it->point(), it->point() ) );
		}
		else if ( it->info().kill == BORDER ) {

			VertexData d = it->info();
			Point a = it->point();
			Point c;

			//// If the vertex is shared by both objects, split it
			//if ( ( d.flag & ROCK ) && ( d.flag & MORE_ROCK ) ) {

			//	VertexData d1;
			//	VertexData d2;
			//	splitVertexData( d, d1, d2 );
			//	
			//	Point a1 = jitterPoint( a );
			//	Point a2 = jitterPoint( a );
			//	
			//	c = getOffsetPoint( a1, d1, this );
			//	newPoints.push_back( c );
			//	pointMap.insert( pair<Point,Point>( c, a1 ) );

			//	c = getOffsetPoint( a2, d2, this );
			//	newPoints.push_back( c );
			//	pointMap.insert( pair<Point,Point>( c, a2 ) );
			//}
			//else {

				c = getOffsetPoint( a, d, this );
				newPoints.push_back( c );
				pointMap.insert( pair<Point,Point>( c, a ) );
			//}
		}
	}

	result.secondsTotal += ( result.secondsMotion = timestamp.time() );
	timestamp.reset();

	// Create the new mesh
	swapDT();
	newDT->clear();
	newDT->insert( newPoints.begin(), newPoints.end() );

	result.secondsTotal += ( result.secondsCGAL = timestamp.time() );
	//secondsTotal += result.secondsCGAL;
	//secondsCGAL += result.secondsCGAL;
	timestamp.reset();

	// Update the inside-outside flags of new tetrahedrons
	setContentFlags( result.midPoint, pointMap );
	result.midPoint[ 0 ] = ( bound[ 0 ][ 0 ] + bound[ 0 ][ 1 ] ) * 0.5;
	result.midPoint[ 1 ] = ( bound[ 1 ][ 0 ] + bound[ 1 ][ 1 ] ) * 0.5;
	result.midPoint[ 2 ] = ( bound[ 2 ][ 0 ] + bound[ 2 ][ 1 ] ) * 0.5;

	result.secondsTotal += ( result.secondsLabeling = timestamp.time() );
	//secondsTotal += result.secondsLabeling;
	//secondsLabeling += result.secondsLabeling;
	timestamp.reset();

	// Update vertex information
	setVertexInfo();

	result.secondsTotal += ( result.secondsAnalysis = timestamp.time() );
	timestamp.reset();
	timestamp.stop();

	result.numVertices = newDT->number_of_vertices();
	result.numTetrahedrons = newDT->number_of_cells();

	cumulativeResults.secondsTotal += result.secondsTotal;
	cumulativeResults.secondsMotion += result.secondsMotion;
	cumulativeResults.secondsCGAL += result.secondsCGAL;
	cumulativeResults.secondsLabeling += result.secondsLabeling;
	cumulativeResults.secondsAnalysis += result.secondsAnalysis;

	return result;
}
/**
 * Sets the new vertex info for the vertices in the new mesh
 */
void StoneWeatherer::setVertexInfo() {

	// Clear everything
	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		it->info().clear();
	}

	int i;

	// Label the vertices to keep (those defining boundaries between materials, where "infinite" means "air")
	for ( All_Cell_iterator it = newDT->all_cells_begin(); it != newDT->all_cells_end(); ++it ) {

		if ( newDT->is_infinite( it ) ) {

			for ( i = 0; i < 4; ++i ) {

				it->vertex( i )->info().flag |= AIR;
			}
		}
		else {

			for ( i = 0; i < 4; ++i ) {

				it->vertex( i )->info().flag |= it->info();
			}
		}
	}

	// Simplify the labels for mesh simplification later
	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		switch ( it->info().flag ) {

			case AIR | DIRT | ROCK | MORE_ROCK:

				// border
			case AIR | DIRT | ROCK:

				// border

			case AIR | DIRT | MORE_ROCK:

				// border

			case AIR | ROCK | MORE_ROCK:

				// border

			case DIRT | ROCK | MORE_ROCK:

				// border

			case AIR | ROCK:

				// border

			case AIR | DIRT:

				// border

			case AIR | MORE_ROCK:

				// border

			case ROCK | DIRT:

				// border

			case DIRT | MORE_ROCK:

				// border

			case ROCK | MORE_ROCK:

				// border

				it->info().kill = BORDER;
				break;

			default:

				it->info().kill = FLOATER;
		}
	}

	int n;

	// Accumulate face normals for border faces
	for ( Cell_iterator it = newDT->finite_cells_begin(); it != newDT->finite_cells_end(); ++it ) {

		if ( it->info() != AIR ) {

			for ( n = 0; n < 4; ++n ) {

				if ( it->neighbor( n )->info() < it->info() || newDT->is_infinite( it->neighbor( n ) ) ) {

					Point t0 = it->vertex( n ^ 1 )->point();
					Vector s1 = it->vertex( n ^ 2 )->point() - t0;
					Vector s2 = it->vertex( n ^ 3 )->point() - t0;
					Vector norm = CGAL::cross_product( s1, s2 );

					for ( i = 1; i < 4; ++i ) {

						it->vertex( n ^ i )->info().normal = it->vertex( n ^ i )->info().normal + norm;
					}
				}
			}
		}
	}

	// Normalize the normals
	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		if ( it->info().kill != FLOATER ) {

			it->info().normal = it->info().normal / sqrt( it->info().normal.squared_length() );
		}
	}

	// Accumulate the edge curvatures and find range of edge lengths for future stepsize and vertex importance
	minEdgeLength = -1.0e300;
	int j;
	Vector e;
	double elen2;
	double curveMult;
	double importanceMult;
	double n_dot_e;

	for ( Cell_iterator it = newDT->finite_cells_begin(); it != newDT->finite_cells_end(); ++it ) {

		if ( it->info() != AIR ) {

			for ( n = 0; n < 4; ++n ) {

				if ( it->neighbor( n )->info() < it->info() || newDT->is_infinite( it->neighbor( n ) ) ) {

					for ( i = 1; i < 4; ++i ) {

						for ( j = i + 1; j < 4; ++j ) {

							e = it->vertex( n ^ i )->point() - it->vertex( n ^ j )->point();
							elen2 = e.squared_length();
							SETMAX( minEdgeLength, -elen2 );
							curveMult = 1.0 / elen2;
							importanceMult = sqrt( curveMult ); // Geometric importance is edge-length independent
							n_dot_e = dot( it->vertex( n ^ i )->info().normal, e );
							it->vertex( n ^ i )->info().numEdges += 1;
							it->vertex( n ^ i )->info().curvature += 2.0 * n_dot_e * curveMult;
							SETMAX( it->vertex( n ^ i )->info().importance, fabs( n_dot_e * importanceMult ) );
						}
					}
				}
			}
		}
	}

	minEdgeLength = sqrt( -minEdgeLength );
	minCurvature = -1.0e300;
	maxCurvature = -1.0e300;

	for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

		if ( it->info().numEdges > 0 ) {

			it->info().curvature /= it->info().numEdges;
			SETMAX( minCurvature, -it->info().curvature );
			SETMAX( maxCurvature, +it->info().curvature );
		}
	}

	// Colorize the vertices
	//for ( Vertex_iterator it = newDT->finite_vertices_begin(); it != newDT->finite_vertices_end(); ++it ) {

	//	it->info().rgb[ 0 ] = 0.0;
	//	it->info().rgb[ 1 ] = 0.0;
	//	it->info().rgb[ 2 ] = 0.0;

	//	if ( it->info().flag & ROCK ) {

	//		it->info().rgb[ 2 ] = 1.0;
	//	}

	//	if ( it->info().flag & MORE_ROCK ) {

	//		it->info().rgb[ 0 ] = 1.0;
	//	}
	//}

	minCurvature *= -1.0;
}