/*!
 * \brief Constructor.
 */
CoarseLocalSearch::CoarseLocalSearch( 
    const EntityIterator& entity_iterator,
    const Teuchos::RCP<EntityLocalMap>& local_map,
    const Teuchos::ParameterList& parameters )
{
    // Setup the centroid array. These will be interleaved.
    int space_dim = 0;
    int num_entity = entity_iterator.size();
    if ( num_entity > 0 )
    {
	space_dim = entity_iterator.begin()->physicalDimension();
    }
    d_entity_centroids.resize( space_dim * num_entity );

    // Add the centroids.
    EntityIterator entity_it;
    EntityIterator begin_it = entity_iterator.begin();
    EntityIterator end_it = entity_iterator.end();
    int entity_local_id = 0;
    for ( entity_it = begin_it;
	  entity_it != end_it;
	  ++entity_it )
    {
	local_map->centroid( 
	    *entity_it, 
	    d_entity_centroids(space_dim*entity_local_id,space_dim) );
	d_entity_map.emplace( entity_local_id, *entity_it );
	++entity_local_id;
    }

    // Build a static search tree.
    int leaf_size = 20;
    if ( parameters.isParameter("Coarse Local Search Leaf Size") )
    {
	leaf_size = parameters.get<int>("Coarse Local Search Leaf Size");
    }
    leaf_size = std::min( leaf_size, num_entity );
    d_tree = SearchTreeFactory::createStaticTree(
	space_dim, d_entity_centroids(), leaf_size );
    DTK_ENSURE( Teuchos::nonnull(d_tree) );
}
//---------------------------------------------------------------------------//
// Assemble the local bounding box around an iterator.
void CoarseGlobalSearch::assembleBoundingBox( 
    const EntityIterator& entity_iterator,
    Teuchos::Tuple<double,6>& bounding_box ) const
{
    double max = std::numeric_limits<double>::max();
    bounding_box = Teuchos::tuple( max, max, max, -max, -max, -max );
    Teuchos::Tuple<double,6> entity_bounds;
    EntityIterator entity_begin = entity_iterator.begin();
    EntityIterator entity_end = entity_iterator.end();
    EntityIterator entity_it;
    for ( entity_it = entity_begin; entity_it != entity_end; ++entity_it )
    {
	entity_it->boundingBox( entity_bounds );
	for ( int n = 0; n < 3; ++n )
	{
	    bounding_box[n] = 
		std::min( bounding_box[n], entity_bounds[n] );
	    bounding_box[n+3] = 
		std::max( bounding_box[n+3], entity_bounds[n+3] );
	}
    }
}
//---------------------------------------------------------------------------//
// Redistribute a set of range entity centroid coordinates with their owner
// ranks to the owning domain process.
void CoarseGlobalSearch::search( const EntityIterator& range_iterator,
				 const Teuchos::RCP<EntityLocalMap>& range_local_map,
				 const Teuchos::ParameterList& parameters,
				 Teuchos::Array<EntityId>& range_entity_ids,
				 Teuchos::Array<int>& range_owner_ranks,
				 Teuchos::Array<double>& range_centroids ) const
{
    // Assemble the local range bounding box.
    Teuchos::Tuple<double,6> range_box;
    assembleBoundingBox( range_iterator, range_box );

    // Find the domain boxes it intersects with.
    Teuchos::Array<int> neighbor_ranks;
    Teuchos::Array<Teuchos::Tuple<double,6> > neighbor_boxes;
    int num_domains = d_domain_boxes.size();
    for ( int n = 0; n < num_domains; ++n )
    {
	if ( boxesIntersect(range_box,d_domain_boxes[n],d_inclusion_tol) )
	{
	    neighbor_ranks.push_back(n);
	    neighbor_boxes.push_back( d_domain_boxes[n] );
	}
    }

    // For each local range entity, find the neighbors we should send it to.
    int num_neighbors = neighbor_boxes.size();
    EntityIterator range_begin = range_iterator.begin();
    EntityIterator range_end = range_iterator.end();
    EntityIterator range_it;
    Teuchos::Array<EntityId> send_ids;
    Teuchos::Array<int> send_ranks;
    Teuchos::Array<double> send_centroids;
    Teuchos::Array<double> centroid(d_space_dim);
    bool found_entity = false;
    for ( range_it = range_begin; range_it != range_end; ++range_it )
    {
	// Get the centroid.
	range_local_map->centroid( *range_it, centroid() );

	// Check the neighbors.
	found_entity = false;
	for ( int n = 0; n < num_neighbors; ++n )
	{
	    // If the centroid is in the box, add it to the send list.
	    if ( pointInBox(centroid(),neighbor_boxes[n],d_inclusion_tol) )
	    {
		found_entity = true;
		send_ids.push_back( range_it->id() );
		send_ranks.push_back( neighbor_ranks[n] );
		for ( int d = 0; d < d_space_dim; ++d )
		{
		    send_centroids.push_back( centroid[d] );
		}
	    }
	}

	// If we are tracking missed range entities, add the entity to the
	// list.
	if ( d_track_missed_range_entities && !found_entity )
	{
	    d_missed_range_entity_ids.push_back( range_it->id() );
	}
    }
    int num_send = send_ranks.size();
    Teuchos::Array<int> range_ranks( num_send, d_comm->getRank() );

    // Create a distributor.
    Tpetra::Distributor distributor(d_comm);
    int num_range_import = distributor.createFromSends( send_ranks() );

    // Redistribute the range entity ids.
    Teuchos::ArrayView<const EntityId> send_ids_view = send_ids();
    range_entity_ids.resize( num_range_import );
    distributor.doPostsAndWaits( send_ids_view, 1, range_entity_ids() );

    // Redistribute the range entity owner ranks.
    Teuchos::ArrayView<const int> range_ranks_view = range_ranks();
    range_owner_ranks.resize( num_range_import );
    distributor.doPostsAndWaits( range_ranks_view, 1, range_owner_ranks() );

    // Redistribute the range entity centroids.
    range_centroids.resize( d_space_dim*num_range_import );
    Teuchos::ArrayView<const double> send_centroids_view = send_centroids();
    distributor.doPostsAndWaits( 
	send_centroids_view, d_space_dim, range_centroids() );
}