/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkMeshFileReader_hxx
#define itkMeshFileReader_hxx

#include "itkConvertPixelBuffer.h"
#include "itkConvertArrayPixelBuffer.h"
#include "itkConvertVariableLengthVectorPixelBuffer.h"
#include "itkMeshIOFactory.h"
#include "itkMeshFileReader.h"
#include "itkMeshRegion.h"
#include "itkObjectFactory.h"
#include "itkPixelTraits.h"

#include <itksys/SystemTools.hxx>
#include <fstream>

namespace itk
{
template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::MeshFileReader()
{
  m_MeshIO = nullptr;
  m_FileName = "";
  m_UserSpecifiedMeshIO = false;
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  if ( m_MeshIO )
    {
    os << indent << "MeshIO: \n";
    m_MeshIO->Print( os, indent.GetNextIndent() );
    }
  else
    {
    os << indent << "MeshIO: (null)" << "\n";
    }

  os << indent << "UserSpecifiedMeshIO flag: " << m_UserSpecifiedMeshIO << "\n";
  os << indent << "FileName: " << m_FileName << "\n";
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::SetMeshIO(MeshIOBase *meshIO)
{
  itkDebugMacro("setting MeshIO to " << meshIO);

  if ( this->m_MeshIO != meshIO )
    {
    this->m_MeshIO = meshIO;
    this->Modified();
    }

  m_UserSpecifiedMeshIO = true;
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::TestFileExistanceAndReadability()
{
  // Test if the file exists.
  if ( !itksys::SystemTools::FileExists( m_FileName.c_str() ) )
    {
    MeshFileReaderException e(__FILE__, __LINE__);
    std::ostringstream      msg;
    msg << "The file doesn't exist. "
        << std::endl << "Filename = " << m_FileName
        << std::endl;
    e.SetDescription( msg.str().c_str() );
    throw e;
    return;
    }

  // Test if the file can be open for reading access.
  std::ifstream readTester;
  readTester.open( m_FileName.c_str() );
  if ( readTester.fail() )
    {
    readTester.close();
    std::ostringstream msg;
    msg << "The file couldn't be opened for reading. "
        << std::endl << "Filename: " << m_FileName
        << std::endl;
    MeshFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
    throw e;
    return;
    }
  readTester.close();
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
template< typename T >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ReadPoints(T *buffer)
{
  typename TOutputMesh::Pointer output = this->GetOutput();
  output->GetPoints()->Reserve( m_MeshIO->GetNumberOfPoints() );
  OutputPointType point;

  for ( OutputPointIdentifier id = 0; id < output->GetNumberOfPoints(); id++ )
    {
    for ( OutputPointIdentifier ii = 0; ii < OutputPointDimension; ii++ )
      {
      point[ii] = static_cast< typename OutputPointType::ValueType >( buffer[id * OutputPointDimension + ii] );
      }

    output->SetPoint(id, point);
    }
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
template< typename T >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ReadCells(T *buffer)
{
  typename TOutputMesh::Pointer output = this->GetOutput();

  SizeValueType        index = NumericTraits< SizeValueType >::ZeroValue();
  OutputCellIdentifier id = NumericTraits< OutputCellIdentifier >::ZeroValue();
  while ( index < m_MeshIO->GetCellBufferSize() )
    {
    auto type = static_cast< MeshIOBase::CellGeometryType >( static_cast< int >( buffer[index++] ) );
    switch ( type )
      {
      case MeshIOBase::VERTEX_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputVertexCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Vertex Cell with number of points = " << numberOfPoints);
          }
        OutputCellAutoPointer cell;
        auto * vertexCell = new OutputVertexCellType;
        for ( unsigned int jj = 0; jj < OutputVertexCellType::NumberOfPoints; jj++ )
          {
          vertexCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(vertexCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::LINE_CELL:
        {
        // for polylines will be loaded as individual edges.
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints < 2 )
          {
          itkExceptionMacro(<< "Invalid Line Cell with number of points = " << numberOfPoints);
          }
        auto pointIDBuffer = static_cast< OutputPointIdentifier >( buffer[index++] );
        for ( unsigned int jj = 1; jj < numberOfPoints; ++jj )
          {
          OutputCellAutoPointer cell;
          auto * lineCell = new OutputLineCellType;
          lineCell->SetPointId(0, pointIDBuffer);
          pointIDBuffer = static_cast< OutputPointIdentifier >( buffer[index++] );
          lineCell->SetPointId(1, pointIDBuffer);
          cell.TakeOwnership(lineCell);
          output->SetCell(id++, cell);
          }
        break;
        }
      case MeshIOBase::TRIANGLE_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputTriangleCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Triangle Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer   cell;
        auto * triangleCell = new OutputTriangleCellType;
        for ( unsigned int jj = 0; jj < OutputTriangleCellType::NumberOfPoints; jj++ )
          {
          triangleCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(triangleCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::QUADRILATERAL_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputQuadrilateralCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Quadrilateral Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer        cell;
        auto * quadrilateralCell = new OutputQuadrilateralCellType;
        for ( unsigned int jj = 0; jj < OutputQuadrilateralCellType::NumberOfPoints; jj++ )
          {
          quadrilateralCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(quadrilateralCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::POLYGON_CELL:
        {
        // For polyhedron, if the number of points is 3, then we treat it as
        // triangle cell
        OutputCellAutoPointer cell;
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints == OutputTriangleCellType::NumberOfPoints )
          {
          auto * triangleCell = new OutputTriangleCellType;
          for ( unsigned int jj = 0; jj < OutputTriangleCellType::NumberOfPoints; jj++ )
            {
            triangleCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
            }
          cell.TakeOwnership(triangleCell);
          }
        else
          {
          auto * polygonCell = new OutputPolygonCellType;
          for ( unsigned int jj = 0; jj < numberOfPoints; jj++ )
            {
            polygonCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
            }
          cell.TakeOwnership(polygonCell);
          }

        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::TETRAHEDRON_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputTetrahedronCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Tetrahedron Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer      cell;
        auto * tetrahedronCell = new OutputTetrahedronCellType;
        for ( unsigned int jj = 0; jj < OutputTetrahedronCellType::NumberOfPoints; jj++ )
          {
          tetrahedronCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(tetrahedronCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::HEXAHEDRON_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputHexahedronCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Hexahedron Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer     cell;
        auto * hexahedronCell = new OutputHexahedronCellType;
        for ( unsigned int jj = 0; jj < OutputHexahedronCellType::NumberOfPoints; jj++ )
          {
          hexahedronCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(hexahedronCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::QUADRATIC_EDGE_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputQuadraticEdgeCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Quadratic edge Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer        cell;
        auto * quadraticEdgeCell = new OutputQuadraticEdgeCellType;
        for ( unsigned int jj = 0; jj < OutputQuadraticEdgeCellType::NumberOfPoints; jj++ )
          {
          quadraticEdgeCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(quadraticEdgeCell);
        output->SetCell(id++, cell);
        break;
        }
      case MeshIOBase::QUADRATIC_TRIANGLE_CELL:
        {
        auto numberOfPoints = static_cast< unsigned int >( buffer[index++] );
        if ( numberOfPoints != OutputQuadraticTriangleCellType::NumberOfPoints )
          {
          itkExceptionMacro(<< "Invalid Quadratic triangle Cell with number of points = " << numberOfPoints);
          }

        OutputCellAutoPointer            cell;
        auto * quadraticTriangleCell = new OutputQuadraticTriangleCellType;
        for ( unsigned int jj = 0; jj < OutputQuadraticTriangleCellType::NumberOfPoints; jj++ )
          {
          quadraticTriangleCell->SetPointId( jj, static_cast< OutputPointIdentifier >( buffer[index++] ) );
          }

        cell.TakeOwnership(quadraticTriangleCell);
        output->SetCell(id++, cell);
        break;
        }
      default:
        {
        itkExceptionMacro(<< "Unknown cell type");
        }
      }
    }
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ReadPointData()
{
  typename TOutputMesh::Pointer output = this->GetOutput();

  char *                inputPointDataBuffer = nullptr;
  auto * outputPointDataBuffer = new OutputPointPixelType[m_MeshIO->GetNumberOfPointPixels()];

  try
    {
    if ( ( m_MeshIO->GetPointPixelComponentType() !=
           MeshIOBase::MapComponentType< typename ConvertPointPixelTraits::ComponentType >::CType )
        || ( m_MeshIO->GetNumberOfPointPixelComponents() != ConvertPointPixelTraits::GetNumberOfComponents() ) )
      {
      // the point pixel types don't match a type conversion needs to be
      // performed
      itkDebugMacro( << "Buffer conversion required from: "
                     << m_MeshIO->GetComponentTypeAsString( m_MeshIO->GetPointPixelComponentType() )
                     << " to: "
                     << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< typename ConvertPointPixelTraits::
                                                                                        ComponentType >::CType)
                     << "ConvertPointPixelTraits::NumberOfComponents "
                     << ConvertPointPixelTraits::GetNumberOfComponents()
                     << " m_MeshIO->NumberOfComponents "
                     << m_MeshIO->GetNumberOfPointPixelComponents() );

      inputPointDataBuffer =
        new char[m_MeshIO->GetNumberOfPointPixelComponents() * m_MeshIO->GetComponentSize( m_MeshIO->GetPointPixelComponentType() )
                 * m_MeshIO->GetNumberOfPointPixels()];
      m_MeshIO->ReadPointData( static_cast< void * >( inputPointDataBuffer ) );

      this->ConvertPointPixelBuffer( static_cast< void * >( inputPointDataBuffer ), outputPointDataBuffer, m_MeshIO->GetNumberOfPointPixels() );
      }
    else
      {
      itkDebugMacro(<< "No buffer conversion required.");
      m_MeshIO->ReadPointData( static_cast< void * >( outputPointDataBuffer ) );
      }
    }
  catch ( ... )
    {
    // if an exception is thrown catch it

    // clean up
    delete[] inputPointDataBuffer;
    inputPointDataBuffer = nullptr;

    delete[] outputPointDataBuffer;
    outputPointDataBuffer = nullptr;

    // then rethrow
    throw;
    }

  // clean up
  delete[] inputPointDataBuffer;
  inputPointDataBuffer = nullptr;

  for ( OutputPointIdentifier id = 0; id < m_MeshIO->GetNumberOfPointPixels(); id++ )
    {
    output->SetPointData(id, outputPointDataBuffer[id]);
    }

  delete[] outputPointDataBuffer;
  outputPointDataBuffer = nullptr;
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ReadCellData()
{
  typename TOutputMesh::Pointer output = this->GetOutput();

  char *               inputCellDataBuffer = nullptr;
  auto * outputCellDataBuffer = new OutputCellPixelType[m_MeshIO->GetNumberOfCellPixels()];

  try
    {
    if ( ( m_MeshIO->GetCellPixelComponentType() !=
           MeshIOBase::MapComponentType< typename ConvertCellPixelTraits::ComponentType >::CType )
        || ( m_MeshIO->GetNumberOfCellPixelComponents() != ConvertCellPixelTraits::GetNumberOfComponents() ) )
      {
      // the cell pixel types don't match a type conversion needs to be
      // performed
      itkDebugMacro( << "Buffer conversion required from: "
                     << m_MeshIO->GetComponentTypeAsString( m_MeshIO->GetCellPixelComponentType() )
                     << " to: "
                     << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< typename ConvertCellPixelTraits::ComponentType >
                                                          ::CType)
                     << "ConvertCellPixelTraits::NumberOfComponents "
                     << ConvertCellPixelTraits::GetNumberOfComponents()
                     << " m_MeshIO->NumberOfComponents "
                     << m_MeshIO->GetNumberOfCellPixelComponents() );

      inputCellDataBuffer =
        new char[m_MeshIO->GetNumberOfCellPixelComponents() * m_MeshIO->GetComponentSize( m_MeshIO->GetCellPixelComponentType() )
                 * m_MeshIO->GetNumberOfCellPixels()];
      m_MeshIO->ReadCellData( static_cast< void * >( inputCellDataBuffer ) );

      this->ConvertCellPixelBuffer( static_cast< void * >( inputCellDataBuffer ), outputCellDataBuffer, m_MeshIO->GetNumberOfCellPixels() );
      }
    else
      {
      itkDebugMacro(<< "No buffer conversion required.");
      m_MeshIO->ReadCellData( static_cast< void * >( outputCellDataBuffer ) );
      }
    }
  catch ( ... )
    {
    // if an exception is thrown catch it

    // clean up
    delete[] inputCellDataBuffer;
    inputCellDataBuffer = nullptr;

    delete[] outputCellDataBuffer;
    outputCellDataBuffer = nullptr;

    // then rethrow
    throw;
    }

  // clean up
  delete[] inputCellDataBuffer;
  inputCellDataBuffer = nullptr;

  for ( OutputCellIdentifier id = 0; id < m_MeshIO->GetNumberOfCellPixels(); id++ )
    {
    output->SetCellData(id, outputCellDataBuffer[id]);
    }

  delete[] outputCellDataBuffer;
  outputCellDataBuffer = nullptr;
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::GenerateOutputInformation()
{
  if ( m_FileName.empty() )
    {
    throw MeshFileReaderException(__FILE__, __LINE__, "FileName must be specified", ITK_LOCATION);
    }

  try
    {
    m_ExceptionMessage = "";
    this->TestFileExistanceAndReadability();
    }
  catch ( itk::ExceptionObject & err )
    {
    m_ExceptionMessage = err.GetDescription();
    }

  if ( m_UserSpecifiedMeshIO == false ) // try creating via factory
    {
    m_MeshIO = MeshIOFactory::CreateMeshIO(m_FileName.c_str(), MeshIOFactory::ReadMode);
    }

  if ( m_MeshIO.IsNull() )
    {
    std::ostringstream msg;
    msg << " Could not create IO object for file " << m_FileName.c_str() << std::endl;
    if ( !m_ExceptionMessage.empty() )
      {
      msg << m_ExceptionMessage;
      }
    else
      {
      msg << "  Tried to create one of the following:" << std::endl;
      for (auto & allobject : ObjectFactoryBase::CreateAllInstance("itkMeshIOBase") )
        {
        auto * io = dynamic_cast< MeshIOBase * >( allobject.GetPointer() );
        msg << "    " << io->GetNameOfClass() << std::endl;
        }
      msg << "  You probably failed to set a file suffix, or" << std::endl;
      msg << "  set the suffix to an unsupported type." << std::endl;
      }

    MeshFileReaderException e(__FILE__, __LINE__, msg.str().c_str(), ITK_LOCATION);
    throw e;
    return;
    }
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::GenerateData()
{
  typename TOutputMesh::Pointer output = this->GetOutput();

  // Test if the file exists and if it can be opened.
  // An exception will be thrown otherwise, since we can't
  // successfully read the file. We catch the exception because some
  // MeshIO's may not actually open a file. Still
  // reports file error if no MeshIO is loaded.

  output->Initialize();
  output->SetBufferedRegion( output->GetRequestedRegion() );

  // Test existence and readability of input file
  try
    {
    m_ExceptionMessage = "";
    this->TestFileExistanceAndReadability();
    }
  catch ( itk::ExceptionObject & err )
    {
    m_ExceptionMessage = err.GetDescription();
    }

  // Tell the MeshIO to read the file
  m_MeshIO->SetFileName( m_FileName.c_str() );

  // Get mesh information
  m_MeshIO->ReadMeshInformation();

  // Read points
  if ( m_MeshIO->GetUpdatePoints() )
    {
    switch ( m_MeshIO->GetPointComponentType() )
      {
      case MeshIOBase::CHAR:
        {
        auto * pointsBuffer = new char[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::UCHAR:
        {
        auto * pointsBuffer = new unsigned char[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::SHORT:
        {
        auto * pointsBuffer = new short[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::USHORT:
        {
        auto * pointsBuffer = new unsigned short[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::INT:
        {
        auto * pointsBuffer = new int[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::UINT:
        {
        auto * pointsBuffer = new unsigned int[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::LONG:
        {
        auto * pointsBuffer = new long[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::ULONG:
        {
        auto * pointsBuffer = new unsigned long[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::LONGLONG:
        {
        auto * pointsBuffer = new long long[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::ULONGLONG:
        {
        auto * pointsBuffer = new unsigned long long[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::FLOAT:
        {
        auto * pointsBuffer = new float[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::DOUBLE:
        {
        auto * pointsBuffer = new double[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::LDOUBLE:
        {
        auto * pointsBuffer = new long double[m_MeshIO->GetNumberOfPoints() * OutputPointDimension];
        m_MeshIO->ReadPoints( static_cast< void * >( pointsBuffer ) );
        ReadPoints(pointsBuffer);
        delete[] pointsBuffer;
        break;
        }
      case MeshIOBase::UNKNOWNCOMPONENTTYPE:
      default:
        {
        itkExceptionMacro(<< "Unknown point component type");
        }
      }
    }

  // Read cells
  if ( m_MeshIO->GetUpdateCells() )
    {
    switch ( m_MeshIO->GetCellComponentType() )
      {
      case MeshIOBase::CHAR:
        {
        auto * cellsBuffer = new char[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::UCHAR:
        {
        auto * cellsBuffer = new unsigned char[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::SHORT:
        {
        auto * cellsBuffer = new short[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::USHORT:
        {
        auto * cellsBuffer = new unsigned short[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::INT:
        {
        auto * cellsBuffer = new int[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::UINT:
        {
        auto * cellsBuffer = new unsigned int[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::LONG:
        {
        auto * cellsBuffer = new long[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::ULONG:
        {
        auto * cellsBuffer = new unsigned long[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::LONGLONG:
        {
        auto * cellsBuffer = new long long[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::ULONGLONG:
        {
        auto * cellsBuffer = new unsigned long long[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::FLOAT:
        {
        auto * cellsBuffer = new float[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::DOUBLE:
        {
        auto * cellsBuffer = new double[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::LDOUBLE:
        {
        auto * cellsBuffer = new long double[m_MeshIO->GetCellBufferSize()];
        m_MeshIO->ReadCells( static_cast< void * >( cellsBuffer ) );
        ReadCells(cellsBuffer);
        delete[] cellsBuffer;
        break;
        }
      case MeshIOBase::UNKNOWNCOMPONENTTYPE:
      default:
        {
        itkExceptionMacro(<< "Unknown cell component type");
        }
      }
    }

  // Read Point Data
  if ( m_MeshIO->GetUpdatePointData() )
    {
    ReadPointData();
    }

  // Read Cell Data
  if ( m_MeshIO->GetUpdateCellData() )
    {
    ReadCellData();
    }
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
template< typename T >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ConvertPointPixelBuffer(void *inputData, T *outputData, size_t numberOfPixels)
{
  // TODO:
  // Pass down the PixelType (RGB, VECTOR, etc.) so that any vector to
  // scalar conversion be type specific. i.e. RGB to scalar would use
  // a formula to convert to luminance, VECTOR to scalar would use
  // vector magnitude.

  // Create a macro as this code is a bit lengthy and repetitive
  // if the MeshIO pixel type is typeid(type) then use the ConvertPixelBuffer
  // class to convert the data block to TOutputMesh's pixel type
  // see DefaultConvertPixelTraits and ConvertPixelBuffer

#define ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(CType, type)           \
  else if ( m_MeshIO->GetPointPixelComponentType() == CType )          \
    {                                                                  \
    ConvertPixelBuffer<                                                \
      type,                                                            \
      OutputPointPixelType,                                            \
      ConvertPointPixelTraits                                          \
      >                                                                \
    ::Convert(                                                         \
      static_cast< type * >( inputData ),                              \
      m_MeshIO->GetNumberOfPointPixelComponents(),                     \
      outputData,                                                      \
      numberOfPixels);                                                 \
    }

  if ( false )
     {}
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::UCHAR, unsigned char)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::CHAR, char)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::USHORT, unsigned short)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::SHORT, short)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::UINT, unsigned int)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::INT, int)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::ULONG, unsigned long)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LONG, long)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::ULONGLONG, unsigned long long)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LONGLONG, long long)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::FLOAT, float)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::DOUBLE, double)
  ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LDOUBLE, long double)
  else
    {
    MeshFileReaderException e(__FILE__, __LINE__);
    std::ostringstream           msg;
    msg << "Couldn't convert component type: "
        << std::endl << "    "
        << m_MeshIO->GetComponentTypeAsString( m_MeshIO->GetPointPixelComponentType() )
        << std::endl << "to one of: "
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned char >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< char >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned short >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< short >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned int >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< int >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned long long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< float >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< double >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long double >::CType)
        << std::endl;
    e.SetDescription( msg.str().c_str() );
    e.SetLocation(ITK_LOCATION);
    throw e;
    return;
    }
#undef ITK_CONVERT_POINT_PIXEL_BUFFER_IF_BLOCK
}

template< typename TOutputMesh, typename ConvertPointPixelTraits, typename ConvertCellPixelTraits >
template< typename T >
void
MeshFileReader< TOutputMesh, ConvertPointPixelTraits, ConvertCellPixelTraits >
::ConvertCellPixelBuffer(void *inputData, T *outputData, size_t numberOfPixels)
{
  // TODO:
  // Pass down the PixelType (RGB, VECTOR, etc.) so that any vector to
  // scalar conversion be type specific. i.e. RGB to scalar would use
  // a formula to convert to luminance, VECTOR to scalar would use
  // vector magnitude.

  // Create a macro as this code is a bit lengthy and repetitive
  // if the MeshIO pixel type is typeid(type) then use the ConvertPixelBuffer
  // class to convert the data block to TOutputMesh's pixel type
  // see DefaultConvertPixelTraits and ConvertPixelBuffer

#define ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(CType, type)            \
  else if ( m_MeshIO->GetCellPixelComponentType() == CType )          \
    {                                                                  \
    ConvertPixelBuffer<                                                \
      type,                                                            \
      OutputCellPixelType,                                             \
      ConvertCellPixelTraits                                           \
      >                                                                \
    ::Convert(                                                         \
      static_cast< type * >( inputData ),                              \
      m_MeshIO->GetNumberOfCellPixelComponents(),                      \
      outputData,                                                      \
      numberOfPixels);                                                 \
    }

  if ( false )
     {}
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::UCHAR, unsigned char)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::CHAR, char)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::USHORT, unsigned short)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::SHORT, short)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::UINT, unsigned int)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::INT, int)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::ULONG, unsigned long)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LONG, long)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::ULONGLONG, unsigned long long)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LONGLONG, long long)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::FLOAT, float)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::DOUBLE, double)
  ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK(MeshIOBase::LDOUBLE, long double)
  else
    {
    MeshFileReaderException e(__FILE__, __LINE__);
    std::ostringstream           msg;
    msg << "Couldn't convert component type: "
        << std::endl << "    "
        << m_MeshIO->GetComponentTypeAsString( m_MeshIO->GetCellPixelComponentType() )
        << std::endl << "to one of: "
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned char >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< char >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned short >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< short >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned int >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< int >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< unsigned long long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long long >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< float >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< double >::CType)
        << std::endl << "    " << m_MeshIO->GetComponentTypeAsString(MeshIOBase::MapComponentType< long double >::CType)
        << std::endl;
    e.SetDescription( msg.str().c_str() );
    e.SetLocation(ITK_LOCATION);
    throw e;
    return;
    }
#undef ITK_CONVERT_CELL_PIXEL_BUFFER_IF_BLOCK
}
} // namespace itk end
#endif
