#include <GmshReader.hpp>

#include <iostream>
#include <fstream>
#include <set>
#include <rang.hpp>

#include <CellType.hpp>
#include <Connectivity.hpp>

#include <Mesh.hpp>

#include <RefFaceList.hpp>

#include <map>
#include <regex>
#include <iomanip>

template<typename T>
inline std::string stringify(const T & t)
{
  std::ostringstream oss;
  oss << t;
  return oss.str();
}

template<>
inline std::string stringify<std::string>(const std::string& t)
{
  return t;
}

class ErrorHandler
{
 public:
  enum Type {
    asked,     /**< execution request by the user*/
    compilation, /**< syntax error in a language */
    normal,    /**< normal error due to a bad use of ff3d */
    unexpected /**< Unexpected execution error */
  };

 private:
  const std::string __filename;	/**< The source file name where the error occured */
  const size_t __lineNumber;	/**< The line number where exception was raised */
  const std::string __errorMessage; /**< The reporting message */

  const Type __type;		/**< the type of the error */
 public:
  /**
   * Prints the error message
   *
   */
  virtual void writeErrorMessage() const;

  /**
   * The copy constructor
   *
   * @param e an handled error
   */
  ErrorHandler(const ErrorHandler& e)
      : __filename(e.__filename),
        __lineNumber(e.__lineNumber),
        __errorMessage(e.__errorMessage),
        __type(e.__type)
  {
    ;
  }

  /**
   * Constructor
   *
   * @param filename the source file name
   * @param lineNumber the source line
   * @param errorMessage the reported message
   * @param type the type of the error
   */
  ErrorHandler(const std::string& filename,
               const size_t& lineNumber,
               const std::string& errorMessage,
               const Type& type);

  /**
   * The destructor
   *
   */
  virtual ~ErrorHandler()
  {
    ;
  }
};
void ErrorHandler::writeErrorMessage() const
{
  switch(__type) {
    case asked: {
      std::cerr << "\nremark: exit command explicitly called\n";
    }
    case normal: {
      std::cerr << '\n' << __filename << ':' << __lineNumber
                << ":remark: emitted the following message\n";
      std::cerr << "error: " << __errorMessage << '\n';
      break;
    }
    case compilation: {
      std::cerr << "\nline " << __lineNumber << ':' << __errorMessage << '\n';
      break;
    }
    case unexpected: {
      std::cerr << '\n' << __filename << ':' << __lineNumber << ":\n" << __errorMessage << '\n';
      std::cerr << "\nUNEXPECTED ERROR: this should not occure, please report it\n";
      std::cerr << "\nBUG REPORT: Please send bug reports to:\n"
                << "  ff3d-dev@nongnu.org or freefem@ann.jussieu.fr\n"
                << "or better, use the Bug Tracking System:\n"
                << "  http://savannah.nongnu.org/bugs/?group=ff3d\n";
      break;
    }
    default: {
      std::cerr << __filename << ':' << __lineNumber << ": " << __errorMessage << '\n';
      std::cerr << __FILE__ << ':' << __LINE__ << ":remark:  error type not implemented!\n";
    }
  }
}

ErrorHandler::
ErrorHandler(const std::string& filename,
             const size_t& lineNumber,
             const std::string& errorMessage,
             const Type& type)
    : __filename(filename),
      __lineNumber(lineNumber),
      __errorMessage(errorMessage),
      __type(type)
{
  ;
}


GmshReader::GmshReader(const std::string& filename)
    : m_filename(filename)
{
  try {
    m_fin.open(m_filename);
    if (not m_fin) {
      std::cerr << "cannot read file '" << m_filename << "'\n";
      std::exit(0);
    }

    // gmsh 2.2 format keywords
    __keywordList["$MeshFormat"]       = MESHFORMAT;
    __keywordList["$EndMeshFormat"]    = ENDMESHFORMAT;
    __keywordList["$Nodes"]            = NODES;
    __keywordList["$EndNodes"]         = ENDNODES;
    __keywordList["$Elements"]         = ELEMENTS;
    __keywordList["$EndElements"]      = ENDELEMENTS;
    __keywordList["$PhysicalNames"]    = PHYSICALNAMES;
    __keywordList["$EndPhysicalNames"] = ENDPHYSICALNAMES;

    __numberOfPrimitiveNodes.resize(16);
    __numberOfPrimitiveNodes[ 0] =  2; // edge
    __numberOfPrimitiveNodes[ 1] =  3; // triangle
    __numberOfPrimitiveNodes[ 2] =  4; // quadrangle
    __numberOfPrimitiveNodes[ 3] =  4; // Tetrahedron
    __numberOfPrimitiveNodes[ 4] =  8; // Hexaredron
    __numberOfPrimitiveNodes[ 5] =  6; // Prism
    __numberOfPrimitiveNodes[ 6] =  5; // Pyramid
    __numberOfPrimitiveNodes[ 7] =  3; // second order edge
    __numberOfPrimitiveNodes[ 8] =  6; // second order triangle
    __numberOfPrimitiveNodes[ 9] =  9; // second order quadrangle
    __numberOfPrimitiveNodes[10] = 10; // second order tetrahedron
    __numberOfPrimitiveNodes[11] = 27; // second order hexahedron
    __numberOfPrimitiveNodes[12] = 18; // second order prism
    __numberOfPrimitiveNodes[13] = 14; // second order pyramid
    __numberOfPrimitiveNodes[14] =  1; // point

    __primitivesNames[0]     = "edges";
    __supportedPrimitives[0] = true;
    __primitivesNames[1]     = "triangles";
    __supportedPrimitives[1] = true;
    __primitivesNames[2]     = "quadrangles";
    __supportedPrimitives[2] = true;
    __primitivesNames[3]     = "tetrahedra";
    __supportedPrimitives[3] = true;
    __primitivesNames[4]     = "hexahedra";
    __supportedPrimitives[4] = true;
    __primitivesNames[5]     = "prisms";
    __supportedPrimitives[5] = false;
    __primitivesNames[6]     = "pyramids";
    __supportedPrimitives[6] = false;
    __primitivesNames[7]     = "second order edges";
    __supportedPrimitives[7] = false;
    __primitivesNames[8]     = "second order triangles";
    __supportedPrimitives[8] = false;
    __primitivesNames[9]     = "second order quadrangles";
    __supportedPrimitives[9] = false;
    __primitivesNames[10]    = "second order tetrahedra";
    __supportedPrimitives[10]= false;
    __primitivesNames[11]    = "second order hexahedra";
    __supportedPrimitives[11]= false;
    __primitivesNames[12]    = "second order prisms";
    __supportedPrimitives[12]= false;
    __primitivesNames[13]    = "second order pyramids";
    __supportedPrimitives[13]= false;
    __primitivesNames[14]    = "point";
    __supportedPrimitives[14]= true;

    std::cout << "Reading file '" << m_filename << "'\n";

    // Getting vertices list
    GmshReader::Keyword kw = this->__nextKeyword();
    switch(kw.second) {
      // case NOD: {
      //   this->__readGmsh1();
      //   break;
      // }
      case MESHFORMAT: {
        double fileVersion = this->_getReal();
        if (fileVersion != 2.2) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "Cannot read Gmsh format '"+stringify(fileVersion)+"'",
                             ErrorHandler::normal);
        }
        int fileType = this->_getInteger();
        __binary = (fileType == 1);
        if ((fileType < 0) or (fileType > 1)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "Cannot read Gmsh file type '"+stringify(fileType)+"'",
                             ErrorHandler::normal);
        }

        int dataSize = this->_getInteger();
        if (dataSize != sizeof(double)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "Data size not supported '"+stringify(dataSize)+"'",
                             ErrorHandler::normal);
        }

        if (__binary) {
          //       int one=0;

          //       fseek(__ifh,1L,SEEK_CUR);
          //       fread(reinterpret_cast<char*>(&one),sizeof(int),1,__ifh);

          //       if (one == 1) {
          // 	__convertEndian = false;
          //       } else {
          // 	invertEndianess(one);

          // 	if (one == 1) {
          // 	  __convertEndian = true;
          // 	} else {
          // 	  throw ErrorHandler(__FILE__,__LINE__,
          // 	  		     "Cannot determine endianess",
          // 	  		     ErrorHandler::normal);
          // 	}
          // }

          //       std::cout << "- Binary format: ";
          // #ifdef    WORDS_BIGENDIAN
          //       if (not __convertEndian) {
          // 	std::cout << "big endian\n";
          //       } else {
          // 	std::cout << "little endian\n";
          //       }
          // #else  // WORDS_BIGENDIAN
          //       if (not __convertEndian) {
          // 	std::cout << "little endian\n";
          //       } else {
          // 	std::cout << "big endian\n";
          //       }
          // #endif // WORDS_BIGENDIAN
        }

        kw = this->__nextKeyword();
        if (kw.second != ENDMESHFORMAT) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': expecting $EndMeshFormat, '"+kw.first+"' was found",
                             ErrorHandler::normal);
        }

        this->__readGmshFormat2_2();

        break;
      }
      default: {
        throw ErrorHandler(__FILE__,__LINE__,
                           "cannot determine format version of '"+m_filename+"'",
                           ErrorHandler::normal);
      }
    }

    this->__proceedData();
    // this->__createMesh();
  }
  catch(const ErrorHandler& e) {
    e.writeErrorMessage();
    std::exit(0);
  }
}

void GmshReader::__readVertices()
{
  const int numberOfVerices = this->_getInteger();
  std::cout << "- Number Of Vertices: " << numberOfVerices << '\n';
  if (numberOfVerices<0) {
    throw ErrorHandler(__FILE__,__LINE__,
                       "reading file '"+this->m_filename
                       +"': number of vertices is negative",
                       ErrorHandler::normal);
  }

  __verticesNumbers.resize(numberOfVerices);
  __vertices = Kokkos::View<R3*>("vertices",numberOfVerices);

  if (not __binary) {
    for (int i=0; i<numberOfVerices; ++i) {
      __verticesNumbers[i] = this->_getInteger();
      const double x = this->_getReal();
      const double y = this->_getReal();
      const double z = this->_getReal();
      __vertices[i] = TinyVector<3, double>(x,y,z);
    }
  } else {
    // fseek(m_fin.file()__ifh,1L,SEEK_CUR);
    // for (int i=0; i<numberOfVerices; ++i) {
    //   int number = 0;
    //   fread(reinterpret_cast<char*>(&number),sizeof(int),1,__ifh);
    //   __verticesNumbers[i] = number;
    //   TinyVector<3,double> x;
    //   fread(reinterpret_cast<char*>(&(x[0])),sizeof(double),3,__ifh);
    //   for (size_t j=0; j<3; ++j) {
    // 	__vertices[i][j] = x[j];
    //   }
    // }

    // if (__convertEndian) {
    //   for (int i=0; i<numberOfVerices; ++i) {
    // 	invertEndianess(__verticesNumbers[i]);
    // 	for (size_t j=0; j<3; ++j) {
    // 	  invertEndianess(vertices[i][j]);
    // 	}
    //   }
    // }
  }
}

// void
// GmshReader::__readElements1()
// {
//   const int numberOfElements = this->_getInteger();
//   std::cout << "- Number Of Elements: " << numberOfElements << '\n';
//   if (numberOfElements<0) {
//     throw ErrorHandler(__FILE__,__LINE__,
// 		       "reading file '"+m_filename
// 		       +"': number of elements is negative",
// 		       ErrorHandler::normal);
//   }

//   __elementType.resize(numberOfElements);
//   __references.resize(numberOfElements);
//   __elementVertices.resize(numberOfElements);

//   for (int i=0; i<numberOfElements; ++i) {
//     this->_getInteger(); // drops element number
//     const int elementType = this->_getInteger()-1;

//     if ((elementType < 0) or (elementType > 14)) {
//       throw ErrorHandler(__FILE__,__LINE__,
// 			 "reading file '"+m_filename
// 			 +"': unknown element type '"+stringify(elementType)+"'",
// 			 ErrorHandler::normal);
//     }
//     __elementType[i] = elementType;
//     __references[i] = this->_getInteger(); // physical reference
//     this->_getInteger(); // drops entity reference

//     const int numberOfVertices = this->_getInteger();
//     if (numberOfVertices != __numberOfPrimitiveNodes[elementType]) {
//       std::stringstream errorMsg;
//       errorMsg << "reading file '" <<m_filename
// 	       << "':number of vertices (" << numberOfVertices
// 	       << ") does not match expected ("
// 	       << __numberOfPrimitiveNodes[elementType] << ")" << std::ends;

//       throw ErrorHandler(__FILE__,__LINE__,
// 			 errorMsg.str(),
// 			 ErrorHandler::normal);
//     }
//     __elementVertices[i].resize(numberOfVertices);
//     for (int j=0; j<numberOfVertices; ++j) {
//       __elementVertices[i][j] = this->_getInteger();
//     }
//   }
// }

void
GmshReader::__readElements2_2()
{
  const int numberOfElements = this->_getInteger();
  std::cout << "- Number Of Elements: " << numberOfElements << '\n';
  if (numberOfElements<0) {
    throw ErrorHandler(__FILE__,__LINE__,
                       "reading file '"+m_filename
                       +"': number of elements is negative",
                       ErrorHandler::normal);
  }

  __elementType.resize(numberOfElements);
  __references.resize(numberOfElements);
  __elementVertices.resize(numberOfElements);

  if (not __binary) {
    for (int i=0; i<numberOfElements; ++i) {
      this->_getInteger(); // drops element number
      const int elementType = this->_getInteger()-1;

      if ((elementType < 0) or (elementType > 14)) {
        throw ErrorHandler(__FILE__,__LINE__,
                           "reading file '"+m_filename
                           +"': unknown element type '"+stringify(elementType)+"'",
                           ErrorHandler::normal);
      }
      __elementType[i] = elementType;
      const int numberOfTags =  this->_getInteger();
      __references[i] = this->_getInteger(); // physical reference
      for (int tag=1; tag<numberOfTags; ++tag) {
        this->_getInteger(); // drops remaining tags
      }

      const int numberOfVertices = __numberOfPrimitiveNodes[elementType];
      __elementVertices[i].resize(numberOfVertices);
      for (int j=0; j<numberOfVertices; ++j) {
        __elementVertices[i][j] = this->_getInteger();
      }
    }
  } else {
    // fseek(__ifh,1L,SEEK_CUR);
    // int i=0;
    // for (;i<numberOfElements;) {
    //   int elementType = 0;
    //   fread(reinterpret_cast<char*>(&elementType),sizeof(int),1,__ifh);
    //   if (__convertEndian) {
    // 	invertEndianess(elementType);
    //   }
    //   --elementType;

    //   if ((elementType < 0) or (elementType > 14)) {
    // 	 throw ErrorHandler(__FILE__,__LINE__,
    // 	 		   "reading file '"+m_filename
    // 	 		   +"': unknown element type '"+stringify(elementType)+"'",
    // 	 		   ErrorHandler::normal);
    //   }

    //   int elementTypeNumber = 0;
    //   fread(reinterpret_cast<char*>(&elementTypeNumber),sizeof(int),1,__ifh);
    //   if (__convertEndian) {
    // 	invertEndianess(elementTypeNumber);
    //   }

    //   int numberOfTags = 0;
    //   fread(reinterpret_cast<char*>(&numberOfTags),sizeof(int),1,__ifh);
    //   if (__convertEndian) {
    // 	invertEndianess(numberOfTags);
    //   }

    //   for (int k=0; k<elementTypeNumber; ++k) {
    // 	__elementType[i] = elementType;

    // 	int elementNumber = 0;
    // 	fread(reinterpret_cast<char*>(&elementNumber),sizeof(int),1,__ifh);

    // 	int reference = 0;
    // 	fread(reinterpret_cast<char*>(&reference),sizeof(int),1,__ifh);

    // 	__references[i] = reference; // physical reference
    // 	for (int tag=1; tag<numberOfTags; ++tag) {
    // 	  int t;
    // 	  fread(reinterpret_cast<char*>(&t),sizeof(int),1,__ifh);
    // 	}

    // 	const int numberOfVertices = __numberOfPrimitiveNodes[elementType];
    // 	__elementVertices[i].resize(numberOfVertices);
    // 	fread(reinterpret_cast<char*>(&(__elementVertices[i][0])),sizeof(int),numberOfVertices,__ifh);
    // 	++i;
    //   }
    // }

    // if (__convertEndian) {
    //   for (size_t i=0; i<__references.size(); ++i) {
    // 	invertEndianess(__references[i]);
    //   }
    //   for (size_t i=0; i<__elementVertices.size(); ++i) {
    // 	for (size_t j=0; j<__elementVertices[i].size(); ++i) {
    // 	  invertEndianess(__elementVertices[i][j]);
    // 	}
    //   }
    // }
  }
}

void
GmshReader::
__readPhysicalNames2_2()
{
  const int number_of_names = this->_getInteger();
  for (int i=0; i<number_of_names; ++i) {
    const int physical_dimension = this->_getInteger();
    const int physical_number = this->_getInteger();
    std::string physical_name;
    m_fin >> physical_name;
    physical_name = std::regex_replace(physical_name, std::regex("(\")"), "");

    PhysicalRefId physical_ref_id(physical_dimension, RefId(physical_number, physical_name));
    auto searched_physical_ref_id = m_physical_ref_map.find(physical_number);
    if (searched_physical_ref_id != m_physical_ref_map.end()) {
      std::cerr << "Physical reference id '" << physical_ref_id
                << "' already defined as '" << searched_physical_ref_id->second << "'!";
      std::exit(0);
    }
    m_physical_ref_map[physical_number] = physical_ref_id;
  }
}

void
GmshReader::__proceedData()
{
  TinyVector<15,int> elementNumber = zero;
  std::vector<std::set<size_t> > elementReferences(15);

  for (size_t i=0; i<__elementType.size(); ++i) {
    const int elementType = __elementType[i];
    elementNumber[elementType]++;
    elementReferences[elementType].insert(__references[i]);
  }

  for (size_t i=0; i<elementNumber.dimension(); ++i) {
    if (elementNumber[i]>0) {
      std::cout << "  - Number of "
                << __primitivesNames[i]
                << ": " << elementNumber[i];
      if (not(__supportedPrimitives[i])) {
        std::cout << " [" << rang::fg::yellow << "IGNORED" << rang::style::reset << "]";
      } else {
        std::cout << " references: ";
        for(std::set<size_t>::const_iterator iref
                = elementReferences[i].begin();
            iref != elementReferences[i].end(); ++iref) {
          if (iref != elementReferences[i].begin()) std::cout << ',';
          std::cout << *iref;
        }

        switch (i) {
          // Supported entities
          case 0: { // edges
            __edges = Kokkos::View<Edge*>("edges",
                                          elementNumber[i]);
            __edges_ref.resize(elementNumber[i]);
            break;
          }
          case 1: { // triangles
            __triangles = Kokkos::View<Triangle*>("triangles",
                                                  elementNumber[i]);
            __triangles_ref.resize(elementNumber[i]);
            break;
          }
          case  2: { // quadrangles
            __quadrangles = Kokkos::View<Quadrangle*>("quadrangles",
                                                      elementNumber[i]);
            __quadrangles_ref.resize(elementNumber[i]);
            break;
          }
          case 3: { // tetrahedra
            __tetrahedra = Kokkos::View<Tetrahedron*>("tetrahedra",
                                                      elementNumber[i]);
            __tetrahedra_ref.resize(elementNumber[i]);
            break;
          }
          case  4: {// hexahedra
            __hexahedra = Kokkos::View<Hexahedron*>("hexahedra",
                                                    elementNumber[i]);
            __hexahedra_ref.resize(elementNumber[i]);
          }
            // Ignored entities
          case 14: {// point
            __points = Kokkos::View<Point*>("points",
                                            elementNumber[i]);
            __points_ref.resize(elementNumber[i]);
            break;
          }
            // Unsupported entities
          case  5: // prism
          case  6: // pyramid
          case  7: // second order edge
          case  8: // second order triangle
          case  9: // second order quadrangle
          case 10: // second order tetrahedron
          case 11: // second order hexahedron
          case 12: // second order prism
          case 13: // second order pyramid
          default: {
            throw ErrorHandler(__FILE__,__LINE__,
                               "reading file '"+m_filename
                               +"': ff3d cannot read this kind of element",
                               ErrorHandler::normal);
          }
        }
      }
      std::cout << '\n';
    }
  }

  std::cout << "- Building correspondance table\n";
  int minNumber = std::numeric_limits<int>::max();
  int maxNumber = std::numeric_limits<int>::min();
  for (size_t i=0; i<__verticesNumbers.size(); ++i) {
    const int& vertexNumber = __verticesNumbers[i];
    minNumber = std::min(minNumber,vertexNumber);
    maxNumber = std::max(maxNumber,vertexNumber);
  }

  if (minNumber<0) {
    throw ErrorHandler(__FILE__,__LINE__,
                       "reading file '"+m_filename
                       +"': ff3d does not implement negative vertices number",
                       ErrorHandler::normal);
  }

  // A value of -1 means that the vertex is unknown
  __verticesCorrepondance.resize(maxNumber+1,-1);

  for (size_t i=0; i<__verticesNumbers.size(); ++i) {

    __verticesCorrepondance[__verticesNumbers[i]] = i;
  }

  // reset element number to count them while filling structures
  elementNumber = zero;

  // Element structures filling
  for (size_t i=0; i<__elementType.size(); ++i) {
    std::vector<int>& elementVertices = __elementVertices[i];
    switch (__elementType[i]) {
      // Supported entities
      case  0: { // edge
        int& edgeNumber = elementNumber[0];
        const int a = __verticesCorrepondance[elementVertices[0]];
        const int b = __verticesCorrepondance[elementVertices[1]];
        if ((a<0) or (b<0)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': error reading element "+stringify(i)
                             +" [bad vertices definition]",
                             ErrorHandler::normal);
        }
        __edges[edgeNumber]
            = Edge(a, b);
        __edges_ref[edgeNumber] = __references[i];
        edgeNumber++; // one more edge
        break;
      }
      case 1: { // triangles
        int& triangleNumber = elementNumber[1];

        const int a = __verticesCorrepondance[elementVertices[0]];
        const int b = __verticesCorrepondance[elementVertices[1]];
        const int c = __verticesCorrepondance[elementVertices[2]];
        if ((a<0) or (b<0) or (c<0)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': error reading element "+stringify(i)
                             +" [bad vertices definition]",
                             ErrorHandler::normal);
        }
        __triangles[triangleNumber]
            = Triangle(a, b, c);
        __triangles_ref[triangleNumber] = __references[i];
        triangleNumber++; // one more triangle
        break;
      }
      case 2: { // quadrangle
        int& quadrilateralNumber = elementNumber[2];

        const int a = __verticesCorrepondance[elementVertices[0]];
        const int b = __verticesCorrepondance[elementVertices[1]];
        const int c = __verticesCorrepondance[elementVertices[2]];
        const int d = __verticesCorrepondance[elementVertices[3]];
        if ((a<0) or (b<0) or (c<0) or (d<0)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': error reading element "+stringify(i)
                             +" [bad vertices definition]",
                             ErrorHandler::normal);
        }
        __quadrangles[quadrilateralNumber] = Quadrangle(a,b,c,d);
        __quadrangles_ref[quadrilateralNumber] = __references[i];
        quadrilateralNumber++; // one more quadrangle
        break;
      }
      case 3: { // tetrahedra
        int& tetrahedronNumber = elementNumber[3];

        const int a = __verticesCorrepondance[elementVertices[0]];
        const int b = __verticesCorrepondance[elementVertices[1]];
        const int c = __verticesCorrepondance[elementVertices[2]];
        const int d = __verticesCorrepondance[elementVertices[3]];
        if ((a<0) or (b<0) or (c<0) or (d<0)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': error reading element "+stringify(i)
                             +" [bad vertices definition]",
                             ErrorHandler::normal);
        }
        __tetrahedra[tetrahedronNumber] = Tetrahedron(a, b, c, d);
        __tetrahedra_ref[tetrahedronNumber] = __references[i];
        tetrahedronNumber++; // one more tetrahedron
        break;
      }
      case  4: { // hexaredron
        int& hexahedronNumber = elementNumber[4];

        const int a = __verticesCorrepondance[elementVertices[0]];
        const int b = __verticesCorrepondance[elementVertices[1]];
        const int c = __verticesCorrepondance[elementVertices[2]];
        const int d = __verticesCorrepondance[elementVertices[3]];
        const int e = __verticesCorrepondance[elementVertices[4]];
        const int f = __verticesCorrepondance[elementVertices[5]];
        const int g = __verticesCorrepondance[elementVertices[6]];
        const int h = __verticesCorrepondance[elementVertices[7]];
        if ((a<0) or (b<0) or (c<0) or (d<0) or (e<0) or (f<0) or (g<0) or (h<0)) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': error reading element "+stringify(i)
                             +" [bad vertices definition]",
                             ErrorHandler::normal);
        }
        __hexahedra[hexahedronNumber]
            = Hexahedron(a, b, c, d,
                         e, f, g, h);
        __hexahedra_ref[hexahedronNumber] = __references[i];
        hexahedronNumber++; // one more hexahedron
        break;
      }
        // Unsupported entities
      case 14:{// point
        int& point_number = elementNumber[14];
        const int a = __verticesCorrepondance[elementVertices[0]];
        __points[point_number] = a;
        __points_ref[point_number] = __references[i];
        point_number++;
        break;
      }
      case  5: // prism
      case  6: // pyramid
      case  7: // second order edge
      case  8: // second order triangle
      case  9: // second order quadrangle
      case 10: // second order tetrahedron
      case 11: // second order hexahedron
      case 12: // second order prism
      case 13:{// second order pyramid
      }
      default: {
        throw ErrorHandler(__FILE__,__LINE__,
                           "reading file '"+m_filename
                           +"': ff3d cannot read this kind of element",
                           ErrorHandler::normal);
      }
    }
  }

  TinyVector<15,int> dimension0_mask = zero;
  dimension0_mask[14]=1;
  TinyVector<15,int> dimension1_mask = zero;
  dimension1_mask[0]=1;
  dimension1_mask[7]=1;
  TinyVector<15,int> dimension2_mask = zero;
  dimension2_mask[1]=1;
  dimension2_mask[2]=1;
  dimension2_mask[8]=1;
  dimension2_mask[9]=1;
  TinyVector<15,int> dimension3_mask = zero;
  dimension3_mask[3]=1;
  dimension3_mask[4]=1;
  dimension3_mask[5]=1;
  dimension3_mask[6]=1;
  dimension3_mask[10]=1;
  dimension3_mask[11]=1;
  dimension3_mask[12]=1;
  dimension3_mask[13]=1;

  std::cout << "- dimension 0 entities: " << (dimension0_mask, elementNumber) << '\n';
  std::cout << "- dimension 1 entities: " << (dimension1_mask, elementNumber) << '\n';
  std::cout << "- dimension 2 entities: " << (dimension2_mask, elementNumber) << '\n';
  std::cout << "- dimension 3 entities: " << (dimension3_mask, elementNumber) << '\n';
  if ((dimension3_mask, elementNumber)>0) {
    const size_t nb_cells = (dimension3_mask, elementNumber);

    std::vector<CellType> cell_type_vector(nb_cells);

    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
    const size_t nb_tetrahedra = __tetrahedra.extent(0);
    for (size_t j=0; j<nb_tetrahedra; ++j) {
      cell_by_node_vector[j].resize(4);
      for (int r=0; r<4; ++r) {
        cell_by_node_vector[j][r] = __tetrahedra[j][r];
      }
      cell_type_vector[j] = CellType::Tetrahedron;
    }
    const size_t nb_hexahedra = __hexahedra.extent(0);
    for (size_t j=0; j<nb_hexahedra; ++j) {
      const size_t jh = nb_tetrahedra+j;
      cell_by_node_vector[jh].resize(8);
      for (int r=0; r<8; ++r) {
        cell_by_node_vector[jh][r] = __hexahedra[j][r];
      }
      cell_type_vector[jh] = CellType::Hexahedron;
    }

    std::shared_ptr<Connectivity3D> p_connectivity(new Connectivity3D(cell_by_node_vector,
                                                                      cell_type_vector));
    Connectivity3D& connectivity = *p_connectivity;

    std::map<unsigned int, std::vector<unsigned int>> ref_faces_map;
    for (unsigned int f=0; f<__triangles.extent(0); ++f) {
      const unsigned int face_number
          = connectivity.getFaceNumber({__triangles[f][0], __triangles[f][1], __triangles[f][2]});
      const unsigned int& ref = __triangles_ref[f];
      ref_faces_map[ref].push_back(face_number);
    }
    for (unsigned int f=0; f<__quadrangles.extent(0); ++f) {
      const unsigned int face_number
          = connectivity.getFaceNumber({__quadrangles[f][0], __quadrangles[f][1],
                                        __quadrangles[f][2], __quadrangles[f][3]});
      const unsigned int& ref = __quadrangles_ref[f];
      ref_faces_map[ref].push_back(face_number);
    }
    for (const auto& ref_face_list : ref_faces_map) {
      Kokkos::View<unsigned int*> face_list("face_list", ref_face_list.second.size());
      for (size_t j=0; j<ref_face_list.second.size(); ++j) {
        face_list[j]=ref_face_list.second[j];
      }
      const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_face_list.first);
      connectivity.addRefFaceList(RefFaceList(physical_ref_id.refId(), face_list));
    }

    typedef Mesh<Connectivity3D> MeshType;
    typedef TinyVector<3, double> Rd;

    Kokkos::View<Rd*> xr("xr", __vertices.extent(0));
    for (size_t i=0; i<__vertices.extent(0); ++i) {
      xr[i][0] = __vertices[i][0];
      xr[i][1] = __vertices[i][1];
      xr[i][2] = __vertices[i][2];
    }
    m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr));

  } else if ((dimension2_mask, elementNumber)>0) {
    const size_t nb_cells = (dimension2_mask, elementNumber);

    std::vector<CellType> cell_type_vector(nb_cells);

    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
    const size_t nb_triangles = __triangles.extent(0);
    for (size_t j=0; j<nb_triangles; ++j) {
      cell_by_node_vector[j].resize(3);
      for (int r=0; r<3; ++r) {
        cell_by_node_vector[j][r] = __triangles[j][r];
      }
      cell_type_vector[j] = CellType::Triangle;
    }

    const size_t nb_quadrangles = __quadrangles.extent(0);
    for (size_t j=0; j<nb_quadrangles; ++j) {
      const size_t jq = j+nb_triangles;
      cell_by_node_vector[jq].resize(4);
      for (int r=0; r<4; ++r) {
        cell_by_node_vector[jq][r] = __quadrangles[j][r];
      }
      cell_type_vector[jq] = CellType::Quadrangle;
    }

    std::shared_ptr<Connectivity2D> p_connectivity(new Connectivity2D(cell_by_node_vector,
                                                                      cell_type_vector));
    Connectivity2D& connectivity = *p_connectivity;

    std::map<unsigned int, std::vector<unsigned int>> ref_faces_map;
    for (unsigned int e=0; e<__edges.extent(0); ++e) {
      const unsigned int edge_number = connectivity.getFaceNumber({__edges[e][0], __edges[e][1]});
      const unsigned int& ref = __edges_ref[e];
      ref_faces_map[ref].push_back(edge_number);
    }

    for (const auto& ref_face_list : ref_faces_map) {
      Kokkos::View<unsigned int*> face_list("face_list", ref_face_list.second.size());
      for (size_t j=0; j<ref_face_list.second.size(); ++j) {
        face_list[j]=ref_face_list.second[j];
      }
      const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_face_list.first);
      connectivity.addRefFaceList(RefFaceList(physical_ref_id.refId(), face_list));
    }

    std::map<unsigned int, std::vector<unsigned int>> ref_points_map;
    for (unsigned int r=0; r<__points.extent(0); ++r) {
      const unsigned int point_number = __points[r];
      const unsigned int& ref = __points_ref[r];
      ref_points_map[ref].push_back(point_number);
    }

    for (const auto& ref_point_list : ref_points_map) {
      Kokkos::View<unsigned int*> point_list("point_list", ref_point_list.second.size());
      for (size_t j=0; j<ref_point_list.second.size(); ++j) {
        point_list[j]=ref_point_list.second[j];
      }
      const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_point_list.first);
      connectivity.addRefNodeList(RefNodeList(physical_ref_id.refId(), point_list));
    }

    typedef Mesh<Connectivity2D> MeshType;
    typedef TinyVector<2, double> Rd;

    Kokkos::View<Rd*> xr("xr", __vertices.extent(0));
    for (size_t i=0; i<__vertices.extent(0); ++i) {
      xr[i][0] = __vertices[i][0];
      xr[i][1] = __vertices[i][1];
    }
    m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr));

  } else if ((dimension1_mask, elementNumber)>0) {
    const size_t nb_cells = (dimension1_mask, elementNumber);

    std::vector<CellType> cell_type_vector(nb_cells);

    std::vector<std::vector<unsigned int>> cell_by_node_vector(nb_cells);
    for (size_t j=0; j<nb_cells; ++j) {
      cell_by_node_vector[j].resize(2);
      for (int r=0; r<2; ++r) {
        cell_by_node_vector[j][r] = __edges[j][r];
      }
      cell_type_vector[j] = CellType::Line;
    }

    std::shared_ptr<Connectivity1D> p_connectivity(new Connectivity1D(cell_by_node_vector,
                                                                      cell_type_vector));
    Connectivity1D& connectivity = *p_connectivity;

    std::map<unsigned int, std::vector<unsigned int>> ref_points_map;
    for (unsigned int r=0; r<__points.extent(0); ++r) {
      const unsigned int point_number = __points[r];
      const unsigned int& ref = __points_ref[r];
      ref_points_map[ref].push_back(point_number);
    }

    for (const auto& ref_point_list : ref_points_map) {
      Kokkos::View<unsigned int*> point_list("point_list", ref_point_list.second.size());
      for (size_t j=0; j<ref_point_list.second.size(); ++j) {
        point_list[j]=ref_point_list.second[j];
      }
      const PhysicalRefId& physical_ref_id = m_physical_ref_map.at(ref_point_list.first);
      connectivity.addRefNodeList(RefNodeList(physical_ref_id.refId(), point_list));
    }

    typedef Mesh<Connectivity1D> MeshType;
    typedef TinyVector<1, double> Rd;

    Kokkos::View<Rd*> xr("xr", __vertices.extent(0));
    for (size_t i=0; i<__vertices.extent(0); ++i) {
      xr[i][0] = __vertices[i][0];
    }

    m_mesh = std::shared_ptr<IMesh>(new MeshType(p_connectivity, xr));

  } else {
    std::cerr << "*** using a dimension 0 mesh is forbidden!\n";
    std::exit(0);
  }
}

GmshReader::Keyword
GmshReader::__nextKeyword()
{
  GmshReader::Keyword kw;

  std::string aKeyword;
  m_fin >> aKeyword;
  if (not m_fin) {
    kw.second = EndOfFile;
    return kw;
  }

  KeywordList::iterator i = __keywordList.find(aKeyword.c_str());

  if (i != __keywordList.end()) {
    kw.first = (*i).first;
    kw.second = (*i).second;
    return kw;
  }

  throw ErrorHandler(__FILE__,__LINE__,
                     "reading file '"+m_filename
                     +"': unknown keyword '"+aKeyword+"'",
                     ErrorHandler::normal);

  kw.first = aKeyword;
  kw.second = Unknown;
  return kw;
}

void GmshReader::
__readGmshFormat2_2()
{
  std::cout << "- Reading Gmsh format 2.2\n";
  GmshReader::Keyword kw = std::make_pair("", Unknown);
  while (kw.second != EndOfFile) {
    kw = this->__nextKeyword();
    switch (kw.second) {
      case NODES: {
        this->__readVertices();
        if (this->__nextKeyword().second != ENDNODES) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': expecting $EndNodes, '"+kw.first+"' was found",
                             ErrorHandler::normal);
        }
        break;
      }
      case ELEMENTS: {
        this->__readElements2_2();
        kw = this->__nextKeyword();
        if (kw.second != ENDELEMENTS) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': expecting $EndElements, '"+kw.first+"' was found",
                             ErrorHandler::normal);
        }
        break;
      }
      case PHYSICALNAMES: {
        this->__readPhysicalNames2_2();
        if (this->__nextKeyword().second != ENDPHYSICALNAMES) {
          throw ErrorHandler(__FILE__,__LINE__,
                             "reading file '"+m_filename
                             +"': expecting $EndNodes, '"+kw.first+"' was found",
                             ErrorHandler::normal);
        }
        break;
      }

      case EndOfFile: {
        break;
      }
      default: {
        throw ErrorHandler(__FILE__,__LINE__,
                           "reading file '"+m_filename
                           +"': unexpected '"+kw.first+"'",
                           ErrorHandler::normal);
      }
    }
  }
}