/**
 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
 * storing and accessing finite element mesh data.
 * 
 * Copyright 2004 Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 */


#ifndef MOAB_MESH_TOPO_UTIL_HPP
#define MOAB_MESH_TOPO_UTIL_HPP

#include "moab_export.h"

#include "moab/Forward.hpp"

namespace moab {

/*!
 *  \authors Tim Tautges
 *  \date    2/04
 *  \brief   MeshTopoUtil contains general mesh utility functions 
 *          
 */ 
class MOAB_EXPORT MeshTopoUtil
{
public:
  MeshTopoUtil(Interface *impl) : mbImpl(impl) {}
  
  ~MeshTopoUtil() {}

    //! generate all the AEntities bounding the vertices
  ErrorCode construct_aentities(const Range &vertices);

    //! given an entity, get its average position (avg vertex locations)
  ErrorCode get_average_position(Range &entities,
                                   double *avg_position);

    //! given an entity, get its average position (avg vertex locations)
  ErrorCode get_average_position(const EntityHandle entity,
                                   double *avg_position);

    //! given a set of entities, get their average position (avg vertex locations)
  ErrorCode get_average_position(const EntityHandle *entities,
                                   const int num_entities,
                                   double *avg_position);

    //! get (target_dim)-dimensional manifold entities connected to star_entity; that is,
    //! the entities with <= 1 connected (target_dim+2)-dimensional adjacent entities;
    //! for target_dim=3, just return all of them
    //! just insert into the list, w/o clearing manifold list first
  ErrorCode get_manifold(const EntityHandle star_entity,
                           const int target_dim,
                           Range &manifold);
  
  //! given an entity, find the entities of next higher dimension around
  //! that entity, ordered by connection through next higher dimension entities; 
  //! if any of the star entities is in only entity of next higher dimension, 
  //! on_boundary is returned true
  ErrorCode star_entities(const EntityHandle star_center,
                            std::vector<EntityHandle> &star_entities,
                            bool &bdy_entity,
                            const EntityHandle starting_star_entity = 0,
                            std::vector<EntityHandle> *star_entities_dp1 = NULL,
                            Range *star_entities_candidates_dp1 = NULL);

    //! Get a series of (d+1)-dimensional stars around a d-dimensional entity, such that
    //! each star is on a (d+2)-manifold containing the d-dimensional entity; each star
    //! is either open or closed, and also defines a (d+2)-star whose entities are bounded by
    //! (d+1)-entities on the star and on the (d+2)-manifold
  ErrorCode star_entities_nonmanifold(const EntityHandle star_entity,
                                        std::vector<std::vector<EntityHandle> > &stars,
                                        std::vector<bool> *bdy_flags = NULL,
                                        std::vector<std::vector<EntityHandle> > *dp2_stars = NULL);

    //! given a star_center, a last_entity (whose dimension should be 1 greater than center)
    //! and last_dp1 (dimension 2 higher than center), returns the next star entity across
    //! last_dp1, and the next dp1 entity sharing next_entity; if star_candidates is non-empty,
    //! star must come from those
  ErrorCode star_next_entity(const EntityHandle star_center,
                               const EntityHandle last_entity,
                               const EntityHandle last_dp1,
                               Range *star_candidates_dp1,
                               EntityHandle &next_entity,
                               EntityHandle &next_dp1);
  
    //! get "bridge" or "2nd order" adjacencies, going through dimension bridge_dim
  ErrorCode get_bridge_adjacencies(Range &from_entities,
                                     int bridge_dim,
                                     int to_dim, 
                                     Range &to_ents,
                                     int num_layers = 1);
  
    //! get "bridge" or "2nd order" adjacencies, going through dimension bridge_dim
  ErrorCode get_bridge_adjacencies(const EntityHandle from_entity,
                                     const int bridge_dim,
                                     const int to_dim,
                                     Range &to_adjs);

    //! return a common entity of the specified dimension, or 0 if there isn't one
  EntityHandle common_entity(const EntityHandle ent1,
                               const EntityHandle ent2,
                               const int dim);
  
  //! return the opposite side entity given a parent and bounding entity.
  //! This function is only defined for certain types of parent/child types;
  //! See MBCN.hpp::OppositeSide for details.
  //!
  //! \param parent The parent element
  //! \param child The child element
  //! \param opposite_element The index of the opposite element
  ErrorCode opposite_entity(const EntityHandle parent,
                              const EntityHandle child,
                              EntityHandle &opposite_element);

    //! split entity which is non-manifold, that is, which has > 2 connected entities
    //! of next higher dimension; assumes that there are >= 2 connected regions of
    //! (d+2)-dimensional entities; a new d-entity is created for each region after the
    //! first, and it's made explicitly-adjacent to the region to which it corresponds
  ErrorCode split_entity_nonmanifold(EntityHandle split_ent,
                                       Range &old_adjs,
                                       Range &new_adjs,
                                       EntityHandle &new_entity);
  
    //! split entities that are manifold (shared by two or less entities of each higher dimension),
    //! optionally creating an entity of next higher dimension to fill the gap
    /**
       \param entities The entities to be split
       \param new_entities New entities, in order of correspondence to that of entities
       \param fill_entities If non-NULL, create an entity of next higher dimension to fill the gap,
                       passing it back in *fill_entities
    */
  ErrorCode split_entities_manifold(Range &entities,
                                      Range &new_entities,
                                      Range *fill_entities);
  
    //! split entities that are manifold (shared by two or less entities of each higher dimension),
    //! optionally creating an entity of next higher dimension to fill the gap
    /**
       \param entities The entities to be split
       \param new_entities New entities, in order of correspondence to that of entities
       \param fill_entities If non-NULL, create an entity of next higher dimension to fill the gap,
                       passing it back in *fill_entities
       \param gowith_ents If non-NULL, each of the new entities will adj to the
                       corresponding gowith entities after the split; this parameter is
                       ignored for boundary split entities; in that case, the split entity
                       remains on the boundary (i.e. not adj to any entity of higher 
                       dimension).  Dimension of gowith_ents must be the same as entities.
    */
  ErrorCode split_entities_manifold(EntityHandle *entities,
                                      const int num_entities,
                                      EntityHandle *new_entities,
                                      Range *fill_entities,
                                      EntityHandle *gowith_ents = NULL);

    //! return whether entity is equivalent to any other of same type and same vertices;
    //! if equivalent entity is found, it's returned in equiv_ents and return value is true,
    //! false otherwise.
  bool equivalent_entities(const EntityHandle entity,
                           Range *equiv_ents = NULL);
  
                                  
private:
  Interface *mbImpl;
  
};

} // namespace moab 

#endif

