
#ifndef WRITE_HDF5_PARALLEL_HPP
#define WRITE_HDF5_PARALLEL_HPP

#include "WriteHDF5.hpp"
#include <H5Spublic.h>
#include <map>

namespace moab {

struct RemoteSetData;
class ParallelComm;
class IODebugTrack;

/** 
 * \brief Write MOAB HDF5 file in parallel.
 * \author Jason Kraftcheck
 * \data   22 July 2004
 */
class WriteHDF5Parallel : public WriteHDF5
{
  public:

    static WriterIface* factory( Interface* );
    
      /** Consturctor
       */
    WriteHDF5Parallel( Interface* iface );

    virtual ~WriteHDF5Parallel();
    
  
  protected:
    
    virtual void debug_barrier_line(int lineno);

  
    virtual void print_times( const double* times ) const;
  
      //! Called by normal (non-parallel) writer.  Sets up
      //! necessary data for parallel write.
    virtual ErrorCode parallel_create_file( const char* filename,
                                     bool overwrite,
                                     const std::vector<std::string>& qa_records,
                                     const FileOptions& opts,
                                     const Tag* user_tag_list = 0,
                                     int user_tag_count = 0,
                                     int dimension = 3,
                                     double* times = 0);
    
      //! Figure out which mesh local mesh is duplicated on
      //! remote processors and which processor will write
      //! that mesh.
      //!\param non_local_ents Output list of entities that are not to
      //!                  be written by this processor but are
      //!                  referenced by other entities that are
      //!                  to be written.
    ErrorCode gather_interface_meshes( Range& non_local_ents );
    
      //! For entities that will be written by another 
      //! processor but are referenced by entities on this
      //! processor, get the file Ids that will be assigned
      //! to those so they can be referenced by
      //! entities to be written on this processor.
      //!\param non_local_ents List of entities that are not to
      //!                  be written by this processor but are
      //!                  referenced by other entities that are
      //!                  to be written.
    ErrorCode exchange_file_ids( const Range& non_local_ents );
    
      //! Get remote ids for shared sets
    ErrorCode communicate_shared_set_ids( const Range& owned, const Range& remote );
    
      //! Pack set data for communication.  
      //!
      //! If set_data_length is insufficient for the set data,
      //! the length entries at indices 1, 2, and 3 of set_data
      //! will be set with the necessary lengths, but no data will
      //! be written to set_data beyond that.
    ErrorCode pack_set( Range::const_iterator set,
                        unsigned long* set_data,
                        size_t set_data_length );
  
      //! Unpack set data from communication
    ErrorCode unpack_set( EntityHandle set, 
                          const unsigned long* set_data, 
                          size_t set_data_length );
                          
      //! Communicate set contents between processors such that each
      //! owner knows the contents, parents, & child lists from all
      //! processors that have a copy of the set.
    ErrorCode communicate_shared_set_data( const Range& owned, const Range& remote );
    
      //! Create the node table in the file.
    ErrorCode create_node_table( int dimension );
    
      //! Communicate with other processors to negotiate 
      //! the types of elements that will be written
      //! (the union of the types defined on each proc.)
    ErrorCode negotiate_type_list();
    
      //! Create tables to hold element connectivity
    ErrorCode create_element_tables();
    
      //! Create tables to hold element adjacencies.
    ErrorCode create_adjacency_tables();
    
      //! Create tables for mesh sets
    ErrorCode create_meshset_tables(double* times);
    
      //! Write tag descriptions and create tables to hold tag data.
    ErrorCode create_tag_tables();
   
      //! Remove any remote mesh entities from the passed range.
    void remove_remote_entities( EntityHandle relative, Range& range );
    void remove_remote_entities( EntityHandle relative, std::vector<EntityHandle>& vect );
    void remove_remote_sets( EntityHandle relative, Range& range );
    void remove_remote_sets( EntityHandle relative, std::vector<EntityHandle>& vect );
  
      //! get any existing tags which aren't excluded and add to shared set tags
    ErrorCode get_sharedset_tags();

    ErrorCode append_serial_tag_data( std::vector<unsigned char>& buffer,
                                      const WriteHDF5::TagDesc& tag );

      //! helper function for create_tag_tables
    ErrorCode check_serial_tag_data( 
                               const std::vector<unsigned char>& buffer,
                               std::vector<TagDesc*>* missing = 0,
                               std::vector<TagDesc*>* newlist = 0 );
  
      /**\brief Argument ot create_dataset */
    struct DataSetCreator {
      virtual ErrorCode operator()( WriteHDF5* writer,
                                    long data_set_size,
                                    const ExportSet* group, 
                                    long& start_id_out ) const = 0;
    };
    struct NoopDescCreator : public DataSetCreator {
      ErrorCode operator()( WriteHDF5*, long, const ExportSet*, long& start_id ) const
      { start_id = -1; return MB_SUCCESS; }
    };
    
      /**\brief Do typical communication for dataset creation
       *
       * Given the number of entities each processor intends to write,
       * do necessary communication and create dataset on root, passing
       * back misc info to each proc.
       *
       *\param creator             Functor to do actual dataset creation.  Used 
       *                           only on root process.
       *\param num_datasets        The number of datasets to create.
       *\param groups              Third argument passed to DataSetCreator.
       *                           Array of length \c num_datasets pr NULL.
       *\param num_owned_entities  The number of entities this proc will write.
       *                           Array of length \c num_datasets .
       *\param offsets_out         Output: The offset in the dataset at which 
       *                           this process should write.  
       *                           Array of length \c num_datasets .
       *\param max_proc_ents_out   Output: The maximun number of entities that 
       *                           any proc will write
       *                           Array of length \c num_datasets .
       *\param total_ents_out      Output: The size of the created dataset (sum 
       *                           of counts over all procs)
       *                           Array of length \c num_datasets .
       *\param first_ids_out       Output: The first ID of the first entity in the 
       *                           data set.  First ID for this proc's entities is 
       *                           first_id_out+offset_out
       *                           Array of length \c num_datasets or NULL.
       */
    ErrorCode create_dataset( int num_datasets,
                              const long* num_owned_entities,
                              long* offsets_out,
                              long* max_proc_ents_out,
                              long* total_ents_out,
                              const DataSetCreator& creator = NoopDescCreator(),
                              ExportSet* groups[] = 0,
                              id_t* first_ids_out = NULL );
  
    void print_shared_sets();
    void print_set_sharing_data( const Range& range, const char* label, Tag idt );
  
  private:

      //! pcomm controlling parallel nature of mesh
    ParallelComm *myPcomm;

      //! whether this instance allocated (and dtor should delete) the pcomm
    bool pcommAllocated;
    
      //! Operation to use to append hyperslab selections
    H5S_seloper_t hslabOp;
};

} // namespace moab

#endif
