/***************************************************************************
                          VTKTraits.h  -  description
                             -------------------
    begin                : Nov 22, 2016
    copyright            : (C) 2016 by Tomas Oberhuber et al.
    email                : tomas.oberhuber@fjfi.cvut.cz
 ***************************************************************************/

/* See Copyright Notice in tnl/Copyright */

#pragma once

#include <stdexcept>
#include <string>
#include <cstdint>

#include <TNL/Meshes/Topologies/Edge.h>
#include <TNL/Meshes/Topologies/Triangle.h>
#include <TNL/Meshes/Topologies/Quadrangle.h>
#include <TNL/Meshes/Topologies/Tetrahedron.h>
#include <TNL/Meshes/Topologies/Hexahedron.h>
#include <TNL/Meshes/Topologies/Polygon.h>

namespace TNL {
namespace Meshes {
namespace VTK {

// VTK file formats
enum class FileFormat
: std::uint8_t
{
   ascii,
   binary,
   zlib_compressed
};

// VTK data types
enum class DataType
: std::uint8_t
{
   CellData,
   PointData
};

// VTK entity shapes
enum class EntityShape
: std::uint8_t
{
   Vertex = 1,
   PolyVertex = 2,
   Line = 3,
   PolyLine = 4,
   Triangle = 5,
   TriangleStrip = 6,
   Polygon = 7,
   Pixel = 8,
   Quad = 9,
   Tetra = 10,
   Voxel = 11,
   Hexahedron = 12,
   Wedge = 13,
   Pyramid = 14
};

inline std::string getShapeName( EntityShape shape )
{
   switch( shape )
   {
      case EntityShape::Vertex:
         return "Vertex";
      case EntityShape::PolyVertex:
         return "PolyVertex";
      case EntityShape::Line:
         return "Line";
      case EntityShape::PolyLine:
         return "PolyLine";
      case EntityShape::Triangle:
         return "Triangle";
      case EntityShape::TriangleStrip:
         return "TriangleStrip";
      case EntityShape::Polygon:
         return "Polygon";
      case EntityShape::Pixel:
         return "Pixel";
      case EntityShape::Quad:
         return "Quad";
      case EntityShape::Tetra:
         return "Tetra";
      case EntityShape::Voxel:
         return "Voxel";
      case EntityShape::Hexahedron:
         return "Hexahedron";
      case EntityShape::Wedge:
         return "Wedge";
      case EntityShape::Pyramid:
         return "Pyramid";
   }
   return "<unknown entity shape>";
}

inline int getEntityDimension( EntityShape shape )
{
   switch( shape )
   {
      case EntityShape::Vertex:         return 0;
      case EntityShape::PolyVertex:     return 0;
      case EntityShape::Line:           return 1;
      case EntityShape::PolyLine:       return 1;
      case EntityShape::Triangle:       return 2;
      case EntityShape::TriangleStrip:  return 2;
      case EntityShape::Polygon:        return 2;
      case EntityShape::Pixel:          return 2;
      case EntityShape::Quad:           return 2;
      case EntityShape::Tetra:          return 3;
      case EntityShape::Voxel:          return 3;
      case EntityShape::Hexahedron:     return 3;
      case EntityShape::Wedge:          return 3;
      case EntityShape::Pyramid:        return 3;
   }
   // this can actually happen when an invalid uint8_t value is converted to EntityShape
   throw std::runtime_error( "VTK::getEntityDimension: invalid entity shape value " + std::to_string(int(shape)) );
}

// static mapping of TNL entity topologies to EntityShape
template< typename Topology > struct TopologyToEntityShape {};
template<> struct TopologyToEntityShape< Topologies::Vertex >      { static constexpr EntityShape shape = EntityShape::Vertex; };
template<> struct TopologyToEntityShape< Topologies::Edge >        { static constexpr EntityShape shape = EntityShape::Line; };
template<> struct TopologyToEntityShape< Topologies::Triangle >    { static constexpr EntityShape shape = EntityShape::Triangle; };
template<> struct TopologyToEntityShape< Topologies::Quadrangle >  { static constexpr EntityShape shape = EntityShape::Quad; };
template<> struct TopologyToEntityShape< Topologies::Tetrahedron > { static constexpr EntityShape shape = EntityShape::Tetra; };
template<> struct TopologyToEntityShape< Topologies::Hexahedron >  { static constexpr EntityShape shape = EntityShape::Hexahedron; };
template<> struct TopologyToEntityShape< Topologies::Polygon >     { static constexpr EntityShape shape = EntityShape::Polygon; };

// mapping used in VTKWriter
template< typename GridEntity >
struct GridEntityShape
{
private:
   static constexpr int dim = GridEntity::getEntityDimension();
   static_assert( dim >= 0 && dim <= 3, "unexpected dimension of the grid entity" );

public:
   static constexpr EntityShape shape =
      (dim == 0) ? EntityShape::Vertex :
      (dim == 1) ? EntityShape::Line :
      (dim == 2) ? EntityShape::Pixel :
                   EntityShape::Voxel;
};

// type names used in the VTK library (for the XML formats)
inline std::string getTypeName( std::int8_t ) { return "Int8"; }
inline std::string getTypeName( std::uint8_t ) { return "UInt8"; }
inline std::string getTypeName( std::int16_t ) { return "Int16"; }
inline std::string getTypeName( std::uint16_t ) { return "UInt16"; }
inline std::string getTypeName( std::int32_t ) { return "Int32"; }
inline std::string getTypeName( std::uint32_t ) { return "UInt32"; }
inline std::string getTypeName( std::int64_t ) { return "Int64"; }
inline std::string getTypeName( std::uint64_t ) { return "UInt64"; }
inline std::string getTypeName( float ) { return "Float32"; }
inline std::string getTypeName( double ) { return "Float64"; }

/**
 * Ghost points and ghost cells
 *
 * The following bit fields are consistent with the corresponding VTK enums [1], which in turn
 * are consistent with VisIt ghost zones specification [2].
 *
 * - [1] https://github.com/Kitware/VTK/blob/060f626b8df0b8144ec8f10c41f936b712c0330b/Common/DataModel/vtkDataSetAttributes.h#L118-L138
 * - [2] http://www.visitusers.org/index.php?title=Representing_ghost_data
 */
enum class CellGhostTypes
: std::uint8_t
{
   DUPLICATECELL = 1,        // the cell is present on multiple processors
   HIGHCONNECTIVITYCELL = 2, // the cell has more neighbors than in a regular mesh
   LOWCONNECTIVITYCELL = 4,  // the cell has less neighbors than in a regular mesh
   REFINEDCELL = 8,          // other cells are present that refines it
   EXTERIORCELL = 16,        // the cell is on the exterior of the data set
   HIDDENCELL = 32           // the cell is needed to maintain connectivity, but the data values should be ignored
};

enum class PointGhostTypes
: std::uint8_t
{
   DUPLICATEPOINT = 1,  // the cell is present on multiple processors
   HIDDENPOINT = 2      // the point is needed to maintain connectivity, but the data values should be ignored
};

/**
 * A DataArray with this name is used in the parallel VTK files to indicate ghost regions.
 * Each value must be assigned according to the bit fields PointGhostTypes or CellGhostType.
 *
 * For details, see https://blog.kitware.com/ghost-and-blanking-visibility-changes/
 */
inline const char* ghostArrayName()
{
   return "vtkGhostType";
}

} // namespace VTK
} // namespace Meshes
} // namespace TNL
