// SpaFEDTe, a Template based C++ library for creating 
// Discontinuous Finite Element Spaces,
// Copyright (C) 2012 Lorenzo Alessio Botti

/* 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 3.0 of the License, or (at your option) any later version. */

/* This software is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU */
/* Lesser General Public License for more details. */

/* You should have received a copy of the GNU Lesser General Public */
/* License along with this software; if not, a copy of the full */
/* GNU Lesser General Public License can be found at */
/* http://www.gnu.org/licenses/. */

// This implementation is mostly borrowed from the MOAB VtkUtil class 

#include "VtuMeshUtil.hpp"
#include <string.h>
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <assert.h>

// Define node ordering for those types for which
// the VTK and MOAB ordering doesn't match.  The values
// contained in the array are MOAB connectivity indices,
// and the values used to index the arrays are VTK 
// connectivity indices.
const unsigned pixel[] = { 0, 1, 3, 2 };
const unsigned voxel[] = { 0, 1, 3, 2, 4, 5, 7, 6 };
const unsigned wedge[] = { 0, 2, 1,   // bottom corners
                           3, 5, 4,   // top corners
                           8, 7, 6,   // bottom edges
                          14,13,12,   // top edges
                           9,11,10,   // lateral edges
                          17,16,15 }; // quadrilateral faces
const unsigned  qhex[] = {  0,  1,  2,  3, // corners (same)
                            4,  5,  6,  7, 
                            8,  9, 10, 11, // mid-edge (top before lateral)
                           16, 17, 18, 19,
                           12, 13, 14, 15,
                           23, 21, 20, 22, // mid-face (mixed up) & mid-region (same)
                           24, 25, 26 };

// List of MeshVtuElemType structs, indexed by the VTK type number.
const MeshVtuElemType VtuMeshUtil::vtuElemTypes[] = {
      { 0,                         0, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "vertex",                  1, ElemTypesMesh::NumberOfElemTypesMesh, 1, 0 },
      { "polyvertex",              2, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "line",                    3, ElemTypesMesh::line,    2, 0 },
      { "polyline",                4, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "triangle",                5, ElemTypesMesh::tri,     3, 0 },
      { "triangle strip",          6, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "polygon",                 7, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "pixel",                   8, ElemTypesMesh::quad,    4, pixel },
      { "quadrilateral",           9, ElemTypesMesh::quad,    4, 0 }, 
      { "tetrahedron",            10, ElemTypesMesh::tet,     4, 0 }, 
      { "voxel",                  11, ElemTypesMesh::hex,     8, voxel }, 
      { "hexahedron",             12, ElemTypesMesh::hex,     8, 0 }, 
      { "wedge",                  13, ElemTypesMesh::prism,   6, wedge }, 
      { "pyramid",                14, ElemTypesMesh::pyram, 5, 0 },
      { "pentagonal prism",       15, ElemTypesMesh::NumberOfElemTypesMesh,10, 0 }, // not supported
      { "hexagonal prism",        16, ElemTypesMesh::NumberOfElemTypesMesh,12, 0 }, // not supported
      { 0,                        17, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { 0,                        18, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { 0,                        19, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { 0,                        20, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 },
      { "quadratic edge",         21, ElemTypesMesh::line,    3, 0 },
      { "quadratic tri",          22, ElemTypesMesh::tri,     6, 0 },
      { "quadratic quad",         23, ElemTypesMesh::quad,    8, 0 },
      { "quadratic tet",          24, ElemTypesMesh::tet,    10, 0 },
      { "quadratic hex",          25, ElemTypesMesh::hex,    20, qhex },
      { "quadratic wedge",        26, ElemTypesMesh::prism,  15, wedge },
      { "quadratic pyramid",      27, ElemTypesMesh::pyram,13, 0 },
      { "bi-quadratic quad",      28, ElemTypesMesh::quad,    9, 0 },
      { "tri-quadratic hex",      29, ElemTypesMesh::hex,    27, qhex },
      { "quadratic-linear quad",  30, ElemTypesMesh::NumberOfElemTypesMesh, 6, 0 },     // not supported
      { "quadratic-linear wedge", 31, ElemTypesMesh::NumberOfElemTypesMesh,12, wedge }, // not supported
      { "bi-quadratic wedge",     32, ElemTypesMesh::NumberOfElemTypesMesh,18, wedge }, // not supported
      { "bi-quadratic hex",       33, ElemTypesMesh::NumberOfElemTypesMesh,24, qhex },  // not supported
      { 0,                        34, ElemTypesMesh::NumberOfElemTypesMesh, 0, 0 }
    };

const unsigned VtuMeshUtil::numVtuElemType = sizeof(VtuMeshUtil::vtuElemTypes) / sizeof(VtuMeshUtil::vtuElemTypes[0]);

// Define an array, indexed by EntityType containing the corresponding 
// VTK element type numbers for the linear, quadratic (mid-edge),
// and full (mid-face & mid-region node) elements.
// Zero is used to indicate an invalid type (not supported by VTK.)  The
// VTK element type number may be used as an index into vtuElemTypes[].
const int spa_to_vtu_type[][3] = {
  {  1,  0,  0 },  // spaVERTEX
  {  3, 21,  0 },  // spaEDGE
  {  5, 22,  0 },  // spaTRI
  {  9, 23, 28 },  // spaQUAD
  { 10, 24,  0 },  // spaTET
  { 14, 27,  0 },  // spaPYRAMID
  { 13, 26,  0 },  // spaPRIMS
  { 12, 25, 29 },  // spaHEX
  {  0,  0,  0 },  // spaPOLYHEDRON
  {  0,  0,  0 } };// spaMAXTYPE

const MeshVtuElemType* VtuMeshUtil::get_vtu_type( ElemTypesMesh type, unsigned num_nodes )
{
  const int i = spa_to_vtu_type[type][0]; // Index for linear type
  const int j = spa_to_vtu_type[type][1]; // Index for quadratic type
  const int k = spa_to_vtu_type[type][2]; // Index for full quadratic type
  if (i) // If element type is supported at all (if not linear then not quadratic either)
  {
      // If the linear type is requested (all polygons are linear
      // irrespective of the number of nodes), return that.
    if (vtuElemTypes[i].num_nodes == num_nodes) //|| type == poly
      return vtuElemTypes + i;
      // Otherwise if there is a quadratic type and the number of
      // nodes specified corresponds to the quadratic type, return that.
    else if (j && vtuElemTypes[j].num_nodes == num_nodes)
      return vtuElemTypes + j;
      // Otherwise if there is a full quadratic type and the number of
      // nodes specified corresponds to the quadratic type, return that.
    else if (k && vtuElemTypes[k].num_nodes == num_nodes)
      return vtuElemTypes + k;
  }
  
  return 0;
}

const char* const vtu_data_types[] = { "Int8",
                                       "UInt8",
                                       "Int16",
                                       "UInt16",
                                       "Int32",
                                       "UInt32",
                                       "Int64",
                                       "UInt64",
                                       "Float32",
                                       "Float64",
                                       0 };


bool VtuMeshUtil::check_vtu_data_type (const char* vtuType, const char* expected_type)
{
  if (!vtuType)
    return false;

    // Check if it matches any data type 
  int match = 0;
  for (const char* const* ptr = vtu_data_types; *ptr; ++ptr)
    if (0 == strcmp( vtuType, *ptr ))
      match = ptr - vtu_data_types + 1;

  if (match == 0)
    return false;

  if (strcmp(expected_type,"Int") == 0 && match <= 8)
  {
    return true;
  }
  if (strcmp(expected_type,"UInt") == 0 && match <= 8)
  {
    return true;
  }
  if (strcmp(expected_type,"Float") == 0 && (match == 9 || match == 10))
  {
    return true;
  }
  return false;
}

bool VtuMeshUtil::checkStringValuesSize(const char *& string, long nValues)
{
  char * endToken;
  long offset = 0;
  long i = 0;
  while (*(string+offset) != '\0')
  {
    double number = strtod (string+offset, &endToken);
    offset = endToken - string;
    assert(offset>0 || *(string+offset) == '\0');
    assert(offset<= strlen(string));
    i++;
  }
  assert (*(string+offset) == '\0');
  if (i == nValues)
    return true;
  return false;
}

bool VtuMeshUtil::checkConnectivityStringSize(const char *& connectivity, const char *& connOffset)
{
  char * endToken;
  long offset = 0;
  long connectivitySize;
  for(;;)
  {
    connectivitySize = strtoul (connOffset+offset, &endToken, 10);
    offset = endToken - connOffset;
    assert (offset<=strlen(connOffset));
    assert (offset>0 || *(connOffset+offset) == '\0');
    if (*(connOffset+offset) == '\0')
      break;
  }
  long i = 0;
  offset = 0;
  while (*(connectivity+offset) != '\0')
  {
    double number = strtod (connectivity+offset, &endToken);
    offset = endToken - connectivity;
    assert (offset<=strlen(connectivity));
    assert (offset>0 || *(connectivity+offset) == '\0');
    i++;
  }
  assert (*(connectivity+offset) == '\0');
  if (i == connectivitySize)
    return true;
  return false;
}


void VtuMeshUtil::setmem( void* mem, const void* value, unsigned value_size, size_t num_elem )
{
  if (!num_elem)
    return;
  
  char* array = reinterpret_cast<char*>(mem);
  memcpy( array, value, value_size );
  size_t count;
  for (count = 1; count*2 < num_elem; count *= 2)
    memcpy( array + count * value_size, array, count * value_size );
  memcpy( array + count * value_size, array, (num_elem - count) * value_size );
}


