/**
 * 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.
 *
 */

//-------------------------------------------------------------------------
// Filename      : WriteCCMIO.hpp
//
// Purpose       : ExodusII writer
//
// Special Notes : Lots of code taken from verde implementation
//
// Creator       : Corey Ernst
//
// Date          : 8/02
//
// Owner         : Corey Ernst
//-------------------------------------------------------------------------

#ifndef WRITECCMIO_HPP
#define WRITECCMIO_HPP

#ifndef IS_BUILDING_MB
#error "WriteCCMIO.hpp isn't supposed to be included into an application"
#endif

#include <vector>
#include <string>

#include "moab/Forward.hpp"
#include "moab/Range.hpp"
#include "moab/ExoIIInterface.hpp"
#include "moab/WriterIface.hpp"
#include "ccmio.h"

namespace moab
{

class WriteUtilIface;

class WriteCCMIO : public WriterIface
{

  public:
    //! Constructor
    WriteCCMIO( Interface* impl );

    //! Destructor
    virtual ~WriteCCMIO();

    static WriterIface* factory( Interface* );

    //! writes out a file
    ErrorCode write_file( const char* file_name, const bool overwrite, const FileOptions& opts,
                          const EntityHandle* output_list, const int num_sets,
                          const std::vector< std::string >& qa_list, const Tag* tag_list = NULL, int num_tags = 0,
                          int export_dimension = 3 );

  protected:
    //! number of dimensions in this file
    // int number_dimensions();

    //! open a file for writing
    ErrorCode open_file( const char* filename, bool overwrite, CCMIOID& rootID );

    //! contains the general information about a mesh
    class MeshInfo
    {
      public:
        unsigned int num_dim;
        unsigned int num_nodes;
        unsigned int num_elements;
        unsigned int num_matsets;
        unsigned int num_dirsets;
        unsigned int num_neusets;
        Range nodes;

        MeshInfo()
            : num_dim( 0 ), num_nodes( 0 ), num_elements( 0 ), num_matsets( 0 ), num_dirsets( 0 ), num_neusets( 0 )
        {
        }
    };

    // material set information
    class MaterialSetData
    {
      public:
        Range elems;               // elements in material set
        EntityHandle setHandle;    // handle of the material set
        EntityType entityType;     // entity type of these elements
        int verts_per_element;     // number of vertices in each element
        int matsetId;              // id of this matset, from MATERIAL_SET tag
        int materialId;            // materialid, if any (from CCMIO)
        std::string setName;       // name for this matset, if any
        std::string materialType;  // material type for this matset, if any

        MaterialSetData()
            : setHandle( 0 ), entityType( MBMAXTYPE ), verts_per_element( 0 ), matsetId( -1 ), materialId( -1 )

        {
        }
    };

    // neumann set information
    class NeumannSetData
    {
      public:
        Range elems;             // elements in neumann set
        EntityHandle setHandle;  // handle of the neumann set
        EntityType entityType;   // entity type of these elements
        int verts_per_element;   // number of vertices in each element
        int neusetId;            // id of this matset, from NEUMANN_SET tag
        std::string setName;     // name for this neuset, if any

        NeumannSetData() : setHandle( 0 ), entityType( MBMAXTYPE ), verts_per_element( 0 ), neusetId( -1 ) {}
    };

  private:
    //! interface instance
    Interface* mbImpl;
    WriteUtilIface* mWriteIface;

    //! file name
    std::string fileName;

    //! Meshset Handle for the mesh that is currently being read
    EntityHandle mCurrentMeshHandle;

    //! Cached tags for reading.  Note that all these tags are defined when the
    //! core is initialized.
    Tag mMaterialSetTag;
    Tag mDirichletSetTag;
    Tag mNeumannSetTag;
    Tag mPartitionSetTag;
    Tag mHasMidNodesTag;
    Tag mGlobalIdTag;
    Tag mNameTag, mMaterialIdTag, mMaterialTypeTag;
    Tag mRadiationTag, mPorosityIdTag, mSpinIdTag, mGroupIdTag, mColorIdxTag, mProcessorIdTag, mLightMaterialTag,
        mFreeSurfaceMaterialTag;
    Tag mThicknessTag, mProstarRegionNumberTag, mBoundaryTypeTag, mCreatingProgramTag;

    Tag mEntityMark;  // used to say whether an entity will be exported

    int mDimension;  // dimension of entities being exported

    bool mWholeMesh;  // if true, whole mesh is being output

    //! gathers elements in each matset, and all the vertices used by them;
    //! marks the vertices with the mEntityMark bit flag
    ErrorCode gather_matset_info( std::vector< EntityHandle >& matsets, std::vector< MaterialSetData >& matset_data,
                                  Range& all_verts );

    //! gathers elements in each neuset
    ErrorCode gather_neuset_info( std::vector< EntityHandle >& neusets, std::vector< NeumannSetData >& neuset_data );

    ErrorCode close_and_compress( const char* filename, CCMIOID rootID );

    ErrorCode initialize_file( MeshInfo& mesh_info );

    //! write vertices to file
    ErrorCode write_nodes( CCMIOID rootID, const Range& nodes, const int dimension, CCMIOID& verticesID );

    //! write cells and internal/boundary faces, using vgids and verts input
    ErrorCode write_cells_and_faces( CCMIOID rootID, std::vector< WriteCCMIO::MaterialSetData >& matset_data,
                                     std::vector< WriteCCMIO::NeumannSetData >& neuset_data, Range& verts,
                                     CCMIOID& topologyID );

    //! write external faces, including connectivity and connected cells
    ErrorCode write_external_faces( CCMIOID rootID, CCMIOID topologyID, int set_num, Range& facets );

    // get global ids for these entities; allocates gids and passes back,
    // caller is responsible for deleting
    ErrorCode get_gids( const Range& ents, int*& gids, int& minid, int& maxid );

    ErrorCode write_meshes( MeshInfo& mesh_info, std::vector< MaterialSetData >& matset_data,
                            std::vector< NeumannSetData >& neuset_data, Range& verts, const int* vgids );

    ErrorCode get_valid_sides( Range& elems, const int sense, WriteCCMIO::NeumannSetData& neuset_data );

    void reset_matset( std::vector< MaterialSetData >& matset_info );

    ErrorCode get_neuset_elems( EntityHandle neuset, int current_sense, Range& forward_elems, Range& reverse_elems );

    ErrorCode transform_coords( const int dimension, const int num_nodes, double* coords );

    ErrorCode write_problem_description( CCMIOID rootID, CCMIOID stateID, CCMIOID& problemID, CCMIOID processorID,
                                         std::vector< MaterialSetData >& matset_data,
                                         std::vector< NeumannSetData >& neuset_data );

    // get the material, dirichlet, neumann, and partition sets to be written,
    // either from input sets or in the whole mesh
    ErrorCode get_sets( const EntityHandle* ent_handles, int num_sets, std::vector< EntityHandle >& matsets,
                        std::vector< EntityHandle >& dirsets, std::vector< EntityHandle >& neusets,
                        std::vector< EntityHandle >& partsets );

    //! create state and processor nodes
    ErrorCode create_ccmio_structure( CCMIOID rootID, CCMIOID& stateID, CCMIOID& processorID );

    //! write solution (tag) data
    ErrorCode write_solution_data();

    //! finalize processor
    ErrorCode write_processor( CCMIOID processorID, CCMIOID verticesID, CCMIOID topologyID );

    //! convert MOAB to CCMIO type
    int moab_to_ccmio_type( EntityType etype, int has_mid_nodes[] );

    ErrorCode write_int_option( const char* opt_name, EntityHandle seth, Tag& tag, CCMIOID& node );

    ErrorCode write_dbl_option( const char* opt_name, EntityHandle seth, Tag& tag, CCMIOID& node );

    ErrorCode write_str_option( const char* opt_name, EntityHandle seth, Tag& tag, CCMIOID& node,
                                const char* other_name = NULL );
};

}  // namespace moab

#endif
