/**
 * 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_WRITE_UTIL_IFACE_HPP
#define MOAB_WRITE_UTIL_IFACE_HPP

#include "moab/Range.hpp"
#include "moab/Compiler.hpp"
#include <vector>

namespace moab {

//! Interface implemented in MOAB which provides memory for mesh reading utilities
class WriteUtilIface
{
public:

  //! Constructor
  WriteUtilIface(){}

  //! Destructor
  virtual ~WriteUtilIface(){}

  //! Check if the specified file already exists.
  //! Returns MB_SUCCESS if file does not exist, MB_ALREADY_ALLOCATED
  //! if file does exist, or MB_FAILURE for some other error condition.
  virtual ErrorCode check_doesnt_exist(const char* file_name) = 0;

  //! Gather all entities in the mesh, or in the sets specified
  virtual ErrorCode gather_entities(Range &all_ents, /**< range in which entities are returned */
                                    const EntityHandle *ent_sets = NULL, /**< entity sets whose contents are to be gathered */
                                    int num_sets = 0 /**< number of sets in list */) = 0;

    //! Given information about the nodes to be written, and pointers to memory
    //! to which coordinates will be written, writes coordinate data there, and
    //! also assigns global ids to nodes & writes to a tag, if a tag is specified
    //! \param num_arrays Number of coordinate arrays requested
    //! \param num_nodes Number of nodes to be written
    //! \param entities Range of nodes to be written
    //! \param node_id_tag Tag used to write ids to nodes
    //! \param start_node_id Starting value for node ids
    //! \param arrays Pointers to memory where coordinate data will be written
    //! \return status Return status
  virtual ErrorCode get_node_coords(const int num_arrays,
                                    const int num_nodes,
                                    const Range& entities,
                                    Tag node_id_tag,
                                    const int start_node_id,
                                    std::vector<double*>& arrays) = 0;

  /** Get array of coordinate values for nodes
   *
   * Given a range of node handles, retrieve a single or multiple coordinate
   * value(s) for each. 
   *
   * Failure conditions:
   *  - invalid entity handles (not vertices, non-existent entity, etc.)
   *  - range is empty (<code>iter == end</code>)
   *  - <code>output_array</code> is null
   *  - insufficient space in <code>output_array</code>
   *
   *\param which_array  The coordinate to retrieve (0-&gt;X, 1-&gt;Y, 2-&gt;Z, -1-&gt;all)
   *\param begin        The first node handle.
   *\param end          One past the last node handle.
   *\param output_size  The size of <code>output_array</code>.
   *\param output_array The memory in which to write the node coordinates.
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_node_coords(const int which_array,
                                    Range::const_iterator begin,
                                    const Range::const_iterator end,
                                    const size_t output_size,
                                    double* const output_array) = 0;

    //! Given information about elements to be written and a pointer to memory
    //! where connectivity for those elements should be written, writes connectivity
    //! to that memory; uses node ids stored in a tag during call to <em>get_node_coords</em>
    //! function
    //! \param num_elements Number of elements to be written
    //! \param verts_per_element Number of vertices per element
    //! \param node_id_tag Tag used to store node ids
    //! \param entities Range of elements to be written
    //! \param element_id_tag Tag which should be used to store element ids
    //! \param start_element_id Starting value for element ids
    //! \param array Pointer to memory where connectivity data will be written
    //! \return status Return status
  virtual ErrorCode get_element_connect(const int num_elements,
                                        const int verts_per_element,
                                        Tag node_id_tag,
                                        const Range& entities,
                                        Tag element_id_tag,
                                        int start_element_id,
                                        int* array,
                                        bool add_sizes = false) = 0;

  /** Get connectivity for elements 
   *
   * Get the connectivity list for a range of elements.
   *
   * Failure cases:
   *  - Passed range is empty (<code>begin == end</code>).
   *  - <code>vertices_per_elem</code> is less than one
   *  - <code>element_array</code> is null.
   *  - The range contains invalid handles (non-existent entities,
   *      not an element, etc.)
   *  - Retrieving ID tag for an entity failed.
   *  - Insufficient space in passed array.
   *
   *\param begin        The first element handle
   *\param end          One past the last element handle
   *\param vertices_per_elem Number of vertices to retrieve for each
   *                    element. If the element has more vertices, the
   *                    element connectivity will be truncated. If
   *                    <code>vertices_per_elem</code> is greater than the
   *                    number of nodes for an element, the data will be
   *                    padded with zeros.
   *\param node_id_tag  A tag with integer values.
   *\param array_size   The length of <code>element_array</code>
   *\param element_array The memory location at which to store the
   *                    connectivity list.
   *\param add_sizes If true, writes size of connect array before connectivity in array
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_element_connect(Range::const_iterator begin,
                                        const Range::const_iterator end,
                                        const int vertices_per_elem,
                                        Tag node_id_tag,
                                        const size_t array_size,
                                        int *const element_array,
                                        bool add_sizes = false) = 0;

  /** Get connectivity for elements
   *
   * Get the connectivity list for a range of elements.
   *
   * Failure cases:
   *  - Passed range is empty (<code>begin == end</code>).
   *  - <code>vertices_per_elem</code> is less than one
   *  - <code>element_array</code> is null.
   *  - The range contains invalid handles (non-existent entities,
   *      not an element, etc.)
   *  - Insufficient space in passed array.
   *
   *\param begin        The first element handle
   *\param end          One past the last element handle
   *\param vertices_per_elem Number of vertices to retrieve for each
   *                    element. If the element has more vertices, the
   *                    element connectivity will be truncated. If
   *                    <code>vertices_per_elem</code> is greater than the
   *                    number of nodes for an element, the data will be
   *                    padded with zeros.
   *\param array_size   The length of <code>element_array</code>
   *\param element_array The memory location at which to store the
   *                    connectivity list.
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_element_connect(Range::const_iterator begin,
                                        const Range::const_iterator end,
                                        const int vertices_per_elem,
                                        const size_t array_size,
                                        EntityHandle *const element_array) = 0;

  /** Get poly (polygon or polyhedron) connectivity size
   *\param begin  First iterator in range of poly
   *\param end    One past last in range of poly.
   *\param connectivity_size  The lenght of the connectivity list
   *              For the specified range of polyhedra.
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_poly_connect_size(Range::const_iterator begin,
                                          const Range::const_iterator end,
                                          int& connectivity_size) = 0;

  /** Get poly (polygon or polyhedron) connectivity.
   *
   * Connectivity is returned in two arrays. The first is
   * an array of global IDs that is the concatenation of the
   * connectivity for the entire range of polys. The second
   * is the last index of the connectivity data for each poly
   * in the global ID array.
   *
   * This function will add as many polys as possible to the
   * passed arrays given the sizes of those arrays. It will
   * then pass back position at which it stopped and the sizes
   * of the data written to the arrays.
   *
   * Failure cases:
   *  - Passed range is empty (<code>begin == end</code>).
   *  - <code>element_array</code> or <code>index_array</code> is null.
   *  - The range contains invalid handles (non-existent entities,
   *      not an poly, etc.)
   *  - Retrieving ID tag for an entity failed.
   *
   *\param iter               As input, the first element handle.
   *                          As output, one past the last element handle
   *                          for which data was written to the arrays.
   *\param end                The iterator at which to stop.
   *\param node_id_tag        A tag with integer values.
   *\param element_array_len  As input, length of <code>element_array</code>.
   *                          As output, the number of entries written in that
   *                          array.
   *\param element_array      The memory location at which to store the
   *                          connectivity list.
   *\param index_array_len    As input, the length of <code>index_array</code>.
   *                          As output, the number of entries written in that
   *                          array.
   *\param index_array        The memory location at which to store offsets.
   *\param index_offset       Value to offset (add to) index values. As output
   *                          the input value plus the amount of data
   *                          written to the element array. (The value you
   *                          presumably want to pass to the next call.)
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_poly_connect(Range::const_iterator& iter,
                                     const Range::const_iterator end,
                                     const Tag node_id_tag,
                                     size_t& element_array_len,
                                     int *const element_array,
                                     size_t& index_array_len,
                                     int *const index_array,
                                     int& index_offset) = 0;

  //! Given elements to be written, gather all the nodes which define those elements
  //! \param elements Range of elements to be written
  //! \param node_bit_mark_tag Bit tag to use to identify nodes
  //! \param nodes Range of nodes gathered from elements (returned)
  //! \return status Return status
  virtual ErrorCode gather_nodes_from_elements(const Range& elements,
                                               const Tag node_bit_mark_tag,
                                               Range& nodes) = 0;

    //! assign ids to input entities starting with start_id, written to id_tag
    //! if id_tag is zero, assigns to GLOBAL_ID_TAG_NAME
    //! \param elements Entities to be written
    //! \param id_tag Tag used to store entity id
    //! \param start_id Starting value for entity ids
    //! \return status Return status
  virtual ErrorCode assign_ids(Range &elements,
                               Tag id_tag,
                               const int start_id) = 0;

  /** Get explicit adjacencies
   *
   * Get explicit adjacences stored in database.
   * Does not create any explicit adjacencies or search for
   * implicit ones.
   *
   *\param entity  The entity to retrieve adjacencies for.
   *\param id_tag  The global ID tag
   *\param adj     The output list of global IDs of adjacent entities.
   *\author Jason Kraftcheck
   */
  virtual ErrorCode get_adjacencies(EntityHandle entity,
                                    Tag id_tag,
                                    std::vector<int>& adj) = 0;

  virtual ErrorCode get_adjacencies(EntityHandle entity,
                                    const EntityHandle*& adj_array,
                                    int& num_adj) = 0;

  /**\brief Re-order outgoing element connectivity
   *
   * Permute the connectivity of each element such that the node
   * order is that of the target file format rather than that of MBCN.
   *\param order The permutation to use. Must be an array of 'node_per_elem'
   *             integers and be a permutation of the values [0..node_per_elem-1].
   *             Such that for a single element:
   *             target_conn[i] == mbcn_conn[order[i]]
   *\param conn  The connectivity array to re-order
   *\param num_elem  The number of elements in the connectivity array
   *\param node_per_elem The number of nodes in each element's connectivity list.
   */
  template <typename T> static inline
  void reorder(const int* order, T* conn, int num_elem, int node_per_elem);

  /**\brief Get list of tags to write.
   *
   * Get the list of tags to write to the file, possibly using
   * an optional user-specified tag list. This function consolidates
   * some common code for file writers to use to figure out what
   * tag data to write to the file. It provides the following features:
   *  o filter list based on user-specified array of tag handles
   *  o filter internal tags (those for which the name is prefixed with
   *    two underscore characters)
   *  o filter anonymous tags
   *  o optionally filter variable-length tags.
   *
   *\author Jason Kraftcheck
   *\param result_list List of tag handles for which to write data
   *\param user_tag_list Optional array of tag handles passed by user
   *                     to write to file.
   *\param include_variable_length_tags If false, return only fixed-length
   *                                    tags.
   */
  virtual ErrorCode 
  get_tag_list(std::vector<Tag>& result_list,
               const Tag* user_tag_list = 0,
               int user_tag_list_length = 0,
               bool include_variable_length_tags = true) = 0;

  enum EntityListType {
    CONTENTS = 0,
    CHILDREN = 1,
    PARENTS = 2,
    TOPOLOGICAL = 1
  };

  /*\brief Get pointers to internal storage of entity data
   *
   * Get pointers to element connectivity or set content storage.
   *\param query_begin Start of range of entities for which to return results
   *\param query_end   End of range of entities for which to return results.
   *\param output_pointer_array Result list of pointers. Points to either
   *          element connectivity or set contents. Note: set contents
   *          may be in range-compacted format.
   *\param lengths Optional per-entity length of list. If passed, then
   *          always set, for each entity, to the number of values in the
   *          array passed back in \c output_pointer_array
   *\param relation If entity is entity set, which set data to return
   *          (contents array, parent array, or child array). If
   *          entity is an element, then CONTENTS for complete connectivity
   *          or TOPOLOGICAL for only corner vertices.
   *\param flags Optional per-entity flag values. If passed, then
   *          always set to zero for elements and set to set creation
   *          flags for entity sets.
   *\return MB_STRUCTURED_MESH if one or more input elements are stored as
   *          structured mesh and therefore do not have explicit storage.
   *        MB_TYPE_OUT_OF_RANGE if called for vertices.
   */
  virtual ErrorCode
  get_entity_list_pointers(Range::const_iterator query_begin,
                           Range::const_iterator query_end,
                           EntityHandle const* * output_pointer_array,
                           EntityListType relation = CONTENTS,
                           int* lengths = 0,
                           unsigned char* flags = 0) = 0;

  /*\brief Get pointers to internal storage of entity data
   *
   * Get pointers to element connectivity or set content storage.
   *\param entities Pointer to list of entities for which to return results
   *\param num_entities Number of entities in list
   *\param output_pointer_array Result list of pointers. Points to either
   *          element connectivity or set contents. Note: set contents
   *          may be in range-compacted format.
   *\param lengths Optional per-entity length of list. If passed, then
   *          always set, for each entity, to the number of values in the
   *          array passed back in \c output_pointer_array
   *\param relation If entity is entity set, which set data to return
   *          (contents array, parent array, or child array). If
   *          entity is an element, then CONTENTS for complete connectivity
   *          or TOPOLOGICAL for only corner vertices.
   *\param flags Optional per-entity flag values. If passed, then
   *          always set to zero for elements and set to set creation
   *          flags for entity sets.
   *\return MB_STRUCTURED_MESH if one or more input elements are stored as
   *          structured mesh and therefore do not have explicit storage.
   *        MB_TYPE_OUT_OF_RANGE if called for vertices.
   */
  virtual ErrorCode
  get_entity_list_pointers(EntityHandle const* entities,
                           int num_entities,
                           EntityHandle const* * output_pointer_array,
                           EntityListType relation = CONTENTS,
                           int* lengths = 0,
                           unsigned char* flags = 0) = 0;
};

template <typename T> inline
void WriteUtilIface::reorder(const int* order, T* conn,
                             int num_elem, int node_per_elem)
{
  std::vector<T> elem(node_per_elem);
  T* const end = conn + num_elem*node_per_elem;
  while (conn != end) {
    std::copy(conn, conn + node_per_elem, elem.begin());
    for (int j = 0; j < node_per_elem; ++j, ++conn)
      *conn = elem[order[j]];
  }
}

} // namespace moab 

#endif
