/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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
 *
 *         https://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.
 *
 *=========================================================================*/
#include "itkVoxBoCUBImageIO.h"
#include "itkIOCommon.h"
#include "itkMetaDataObject.h"
#include "itkByteSwapper.h"
#include "itksys/SystemTools.hxx"
#include <iostream>
#include <sstream>
#include <list>
#include <string>
#include <cmath>
#include <ctime>

#include "itk_zlib.h"
#include "itkSpatialOrientationAdapter.h"

namespace itk
{
/**
 *
 * \class GenericCUBFileAdaptor
 *
 * \brief Reader and Writer for the VoxBo file format.
 *
 * A generic reader and writer object for VoxBo files. Basically it
 * provides uniform access to gzip and normal files
 *
 * \author Burstein, Pablo D.; Yushkevich, Paul; Gee, James C.
 *
 * This implementation was contributed as a paper to the Insight Journal
 * https://doi.org/10.54294/k9nj7c
 *
 */
class GenericCUBFileAdaptor
{
public:
  GenericCUBFileAdaptor() = default;
  virtual ~GenericCUBFileAdaptor() = default;

  using SizeType = ImageIOBase::SizeType;

  virtual unsigned char
  ReadByte() = 0;

  virtual void
  ReadData(void * data, SizeType bytes) = 0;

  virtual void
  WriteData(const void * data, SizeType bytes) = 0;

  std::string
  ReadHeader()
  {
    // Read everything up to the \f symbol
    std::ostringstream oss;
    unsigned char      byte = ReadByte();

    while (byte != '\f')
    {
      oss << byte;
      byte = ReadByte();
    }

    // Read the next byte
    unsigned char term = ReadByte();
    if (term == '\r')
    {
      term = ReadByte();
    }
    // Throw exception if term is not there
    if (term != '\n')
    {
      ExceptionObject exception;
      exception.SetDescription("Header is not terminated by newline.");
      throw exception;
    }

    // Return the header string
    return oss.str();
  }
};

/**
 * A reader for gzip files
 */

class CompressedCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  CompressedCUBFileAdaptor(const char * file, const char * mode)
    : m_GzFile(gzopen(file, mode))
  {
    if (m_GzFile == nullptr)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be accessed");
      throw exception;
    }
  }

  ~CompressedCUBFileAdaptor() override
  {
    if (m_GzFile)
    {
      gzflush(m_GzFile, Z_FINISH);
      gzclose(m_GzFile);
    }
  }

  unsigned char
  ReadByte() override
  {
    int byte = gzgetc(m_GzFile);

    if (byte < 0)
    {
      std::ostringstream oss;
      oss << "Error reading byte from file at position: " << gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
    }
    return static_cast<unsigned char>(byte);
  }

  void
  ReadData(void * data, SizeType bytes) override
  {
    if (m_GzFile == nullptr)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
    }

    auto     numberOfBytesToRead = Math::CastWithRangeCheck<unsigned int, SizeType>(bytes);
    SizeType bread = gzread(m_GzFile, data, numberOfBytesToRead);
    if (bread != bytes)
    {
      std::ostringstream oss;
      oss << "File size does not match header: " << bytes << " bytes requested but only " << bread
          << " bytes available!" << std::endl
          << "At file position " << gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
    }
  }

  void
  WriteData(const void * data, SizeType bytes) override
  {
    if (m_GzFile == nullptr)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
    }

    auto     numberOfBytesToWrite = Math::CastWithRangeCheck<unsigned int, SizeType>(bytes);
    SizeType bwritten = gzwrite(m_GzFile, const_cast<void *>(data), numberOfBytesToWrite);
    if (bwritten != bytes)
    {
      ExceptionObject exception;
      exception.SetDescription("Could not write all bytes to file");
      std::cout << "Could not write all bytes to file" << std::endl;
      throw exception;
    }
    gzflush(m_GzFile, Z_SYNC_FLUSH);
  }

private:
  gzFile m_GzFile;
};

/**
 * A reader for non-gzip files
 */
class DirectCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  DirectCUBFileAdaptor(const char * file, const char * mode)
    : m_File(fopen(file, mode))
  {
    if (!m_File)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
    }
  }

  ~DirectCUBFileAdaptor() override
  {
    if (m_File)
    {
      fclose(m_File);
    }
  }

  unsigned char
  ReadByte() override
  {
    int byte = fgetc(m_File);

    if (byte == EOF)
    {
      std::ostringstream oss;
      oss << "Error reading byte from file at position: " << ::ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
    }
    return static_cast<unsigned char>(byte);
  }

  void
  ReadData(void * data, SizeType bytes) override
  {
    if (m_File == nullptr)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
    }

    const auto numberOfBytesToRead = Math::CastWithRangeCheck<SizeValueType, SizeType>(bytes);
    SizeType   bread = fread(data, NumericTraits<SizeValueType>::OneValue(), numberOfBytesToRead, m_File);
    if (bread != bytes)
    {
      std::ostringstream oss;
      oss << "File size does not match header: " << bytes << " bytes requested but only " << bread
          << " bytes available!" << std::endl
          << "At file position " << ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
    }
  }

  void
  WriteData(const void * data, SizeType bytes) override
  {
    if (m_File == nullptr)
    {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
    }

    const auto numberOfBytesToWrite = Math::CastWithRangeCheck<SizeValueType, SizeType>(bytes);
    SizeType   bwritten = fwrite(data, NumericTraits<SizeValueType>::OneValue(), numberOfBytesToWrite, m_File);
    if (bwritten != bytes)
    {
      ExceptionObject exception;
      exception.SetDescription("Could not write all bytes to file");
      throw exception;
    }
  }

private:
  FILE * m_File;
};

/**
 * \class VoxBoCUBImageIOSwapHelper
 *
 * \brief A swap helper class, used to perform swapping for any input
 * data type.
 *
 */
template <typename TPixel>
class VoxBoCUBImageIOSwapHelper
{
public:
  using ByteOrder = IOByteOrderEnum;
  using BufferSizeType = ImageIOBase::BufferSizeType;

  static void
  SwapIfNecessary(void * buffer, BufferSizeType numberOfBytes, ByteOrder dataByteOrder)
  {
    if (dataByteOrder == IOByteOrderEnum::LittleEndian)
    {
      ByteSwapper<TPixel>::SwapRangeFromSystemToLittleEndian((TPixel *)buffer, numberOfBytes / sizeof(TPixel));
    }
    else if (dataByteOrder == IOByteOrderEnum::BigEndian)
    {
      ByteSwapper<TPixel>::SwapRangeFromSystemToBigEndian((TPixel *)buffer, numberOfBytes / sizeof(TPixel));
    }
  }
};

// Strings
const char * VoxBoCUBImageIO::m_VB_IDENTIFIER_SYSTEM = "VB98";
const char * VoxBoCUBImageIO::m_VB_IDENTIFIER_FILETYPE = "CUB1";
const char * VoxBoCUBImageIO::m_VB_DIMENSIONS = "VoxDims(XYZ)";
const char * VoxBoCUBImageIO::m_VB_SPACING = "VoxSizes(XYZ)";
const char * VoxBoCUBImageIO::m_VB_ORIGIN = "Origin(XYZ)";
const char * VoxBoCUBImageIO::m_VB_DATATYPE = "DataType";
const char * VoxBoCUBImageIO::m_VB_BYTEORDER = "Byteorder";
const char * VoxBoCUBImageIO::m_VB_ORIENTATION = "Orientation";
const char * VoxBoCUBImageIO::m_VB_BYTEORDER_MSB = "msbfirst";
const char * VoxBoCUBImageIO::m_VB_BYTEORDER_LSB = "lsbfirst";
const char * VoxBoCUBImageIO::m_VB_DATATYPE_BYTE = "Byte";
const char * VoxBoCUBImageIO::m_VB_DATATYPE_INT = "Integer";
const char * VoxBoCUBImageIO::m_VB_DATATYPE_FLOAT = "Float";
const char * VoxBoCUBImageIO::m_VB_DATATYPE_DOUBLE = "Double";

/** Constructor */
VoxBoCUBImageIO::VoxBoCUBImageIO()

{
  InitializeOrientationMap();
  m_ByteOrder = IOByteOrderEnum::BigEndian;
}

/** Destructor */
VoxBoCUBImageIO::~VoxBoCUBImageIO()
{
  delete m_Reader;
  delete m_Writer;
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateReader(const char * filename)
{
  try
  {
    bool compressed = false;
    if (CheckExtension(filename, compressed))
    {
      if (compressed)
      {
        return new CompressedCUBFileAdaptor(filename, "rb");
      }
      else
      {
        return new DirectCUBFileAdaptor(filename, "rb");
      }
    }
    else
    {
      return nullptr;
    }
  }
  catch (...)
  {
    return nullptr;
  }
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateWriter(const char * filename)
{
  try
  {
    bool compressed = false;
    if (CheckExtension(filename, compressed))
    {
      if (compressed)
      {
        return new CompressedCUBFileAdaptor(filename, "wb");
      }
      else
      {
        return new DirectCUBFileAdaptor(filename, "wb");
      }
    }
    else
    {
      return nullptr;
    }
  }
  catch (...)
  {
    return nullptr;
  }
}

bool
VoxBoCUBImageIO::CanReadFile(const char * filename)
{
  // First check if the file can be read
  GenericCUBFileAdaptor * reader = CreateReader(filename);

  if (reader == nullptr)
  {
    itkDebugMacro("The file is not a valid CUB file");
    return false;
  }

  // Now check the content
  bool iscub = true;
  try
  {
    // Get the header
    std::istringstream iss(reader->ReadHeader());

    // Read the first two words
    std::string word;

    // Read the first line from the file
    iss >> word;
    if (word != m_VB_IDENTIFIER_SYSTEM)
    {
      iscub = false;
    }

    // Read the second line
    iss >> word;
    if (word != m_VB_IDENTIFIER_FILETYPE)
    {
      iscub = false;
    }
  }
  catch (...)
  {
    iscub = false;
  }

  delete reader;
  return iscub;
}

bool
VoxBoCUBImageIO::CanWriteFile(const char * name)
{
  bool compressed = false;

  return CheckExtension(name, compressed);
}

void
VoxBoCUBImageIO::Read(void * buffer)
{
  if (m_Reader == nullptr)
  {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
  }

  auto numberOfBytesToRead = Math::CastWithRangeCheck<BufferSizeType, SizeType>(this->GetImageSizeInBytes());
  m_Reader->ReadData(buffer, numberOfBytesToRead);
  this->SwapBytesIfNecessary(buffer, numberOfBytesToRead);
}

/**
 *  Read Information about the VoxBoCUB file
 *  and put the cursor of the stream just before the first data pixel
 */
void
VoxBoCUBImageIO::ReadImageInformation()
{
  // Make sure there is no other reader
  delete m_Reader;

  // Create a reader
  m_Reader = CreateReader(m_FileName.c_str());
  if (m_Reader == nullptr)
  {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
  }

  // Set the number of dimensions to three
  SetNumberOfDimensions(3);

  // Read the file header
  std::istringstream issHeader(m_Reader->ReadHeader());

  // Read every string in the header. Parse the strings that are special
  while (issHeader.good())
  {
    // Read a line from the stream
    char linebuffer[512];
    issHeader.getline(linebuffer, 512);

    // Get the key string
    std::istringstream iss(linebuffer);
    std::string        key;

    // Read the key and strip the colon from it
    iss >> key;

    const std::string::size_type keysize = key.size();

    if ((keysize > 0) && (key.back() == ':'))
    {
      // Strip the colon off the key
      key = key.substr(0, key.size() - 1);

      // Check if this is a relevant key
      if (key == m_VB_DIMENSIONS)
      {
        iss >> m_Dimensions[0];
        iss >> m_Dimensions[1];
        iss >> m_Dimensions[2];
      }

      else if (key == m_VB_SPACING)
      {
        iss >> m_Spacing[0];
        iss >> m_Spacing[1];
        iss >> m_Spacing[2];
      }

      else if (key == m_VB_ORIGIN)
      {
        double ox = NAN, oy = NAN, oz = NAN;
        iss >> ox;
        iss >> oy;
        iss >> oz;
        m_Origin[0] = -ox * m_Spacing[0];
        m_Origin[1] = -oy * m_Spacing[1];
        m_Origin[2] = -oz * m_Spacing[2];
      }

      else if (key == m_VB_DATATYPE)
      {
        std::string type;
        iss >> type;
        m_PixelType = IOPixelEnum::SCALAR;
        if (type == m_VB_DATATYPE_BYTE)
        {
          m_ComponentType = IOComponentEnum::UCHAR;
        }
        else if (type == m_VB_DATATYPE_INT)
        {
          m_ComponentType = IOComponentEnum::USHORT;
        }
        else if (type == m_VB_DATATYPE_FLOAT)
        {
          m_ComponentType = IOComponentEnum::FLOAT;
        }
        else if (type == m_VB_DATATYPE_DOUBLE)
        {
          m_ComponentType = IOComponentEnum::DOUBLE;
        }
      }

      else if (key == m_VB_BYTEORDER)
      {
        std::string type;
        iss >> type;
        if (type == m_VB_BYTEORDER_MSB)
        {
          SetByteOrderToBigEndian();
        }
        else if (type == m_VB_BYTEORDER_LSB)
        {
          SetByteOrderToLittleEndian();
        }
        else
        {
          ExceptionObject exception(__FILE__, __LINE__);
          exception.SetDescription("Unknown byte order constant");
          throw exception;
        }
      }

      else if (key == m_VB_ORIENTATION)
      {
        std::string code;
        iss >> code;

        // Set the orientation code in the data dictionary
        auto it = m_OrientationMap.find(code);
        if (it != m_OrientationMap.end())
        {
          // NOTE:  The itk::ImageIOBase direction is a std::vector<std::vector > >, and threeDDirection is a 3x3 matrix
          itk::SpatialOrientationAdapter                soAdaptor;
          itk::SpatialOrientationAdapter::DirectionType threeDDirection = soAdaptor.ToDirectionCosines(it->second);
          this->m_Direction[0][0] = threeDDirection[0][0];
          this->m_Direction[0][1] = threeDDirection[0][1];
          this->m_Direction[0][2] = threeDDirection[0][2];
          this->m_Direction[1][0] = threeDDirection[1][0];
          this->m_Direction[1][1] = threeDDirection[1][1];
          this->m_Direction[1][2] = threeDDirection[1][2];
          this->m_Direction[2][0] = threeDDirection[2][0];
          this->m_Direction[2][1] = threeDDirection[2][1];
          this->m_Direction[2][2] = threeDDirection[2][2];
        }
      }

      else
      {
        // Encode the right hand side of the string in the meta-data dic
        std::string        word;
        std::ostringstream oss;
        while (iss >> word)
        {
          if (!oss.str().empty())
          {
            oss << ' ';
          }
          oss << word;
        }
        MetaDataDictionary & dic = this->GetMetaDataDictionary();
        EncapsulateMetaData<std::string>(dic, key, oss.str());
      }
    }
  }
}

void
VoxBoCUBImageIO::WriteImageInformation()
{
  if (m_Writer == nullptr)
  {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
  }

  // Check that the number of dimensions is correct
  if (GetNumberOfDimensions() != 3)
  {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("Unsupported number of dimensions");
    throw exception;
  }

  // Put together a header
  std::ostringstream header;

  // Write the identifiers
  header << m_VB_IDENTIFIER_SYSTEM << std::endl << m_VB_IDENTIFIER_FILETYPE << std::endl;

  // Write the data type
  switch (m_ComponentType)
  {
    case IOComponentEnum::CHAR:
    case IOComponentEnum::UCHAR:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_BYTE << std::endl;
      break;
    case IOComponentEnum::SHORT:
    case IOComponentEnum::USHORT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_INT << std::endl;
      break;
    case IOComponentEnum::FLOAT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_FLOAT << std::endl;
      break;
    case IOComponentEnum::DOUBLE:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_DOUBLE << std::endl;
      break;
    default:
      ExceptionObject exception(__FILE__, __LINE__);
      exception.SetDescription("Unsupported pixel component type");
      throw exception;
  }

  // Write the image dimensions
  header << m_VB_DIMENSIONS << ":\t" << m_Dimensions[0] << '\t' << m_Dimensions[1] << '\t' << m_Dimensions[2]
         << std::endl;

  // Write the spacing
  header << m_VB_SPACING << ":\t" << m_Spacing[0] << '\t' << m_Spacing[1] << '\t' << m_Spacing[2] << std::endl;

  // Write the origin (have to convert to bytes)

  double x = -m_Origin[0] / m_Spacing[0];
  double y = -m_Origin[1] / m_Spacing[1];
  double z = -m_Origin[2] / m_Spacing[2];
  header << m_VB_ORIGIN << ":\t" << ((x >= 0) ? static_cast<int>(x + .5) : static_cast<int>(x - .5)) << '\t'
         << ((y >= 0) ? static_cast<int>(y + .5) : static_cast<int>(y - .5)) << '\t'
         << ((z >= 0) ? static_cast<int>(z + .5) : static_cast<int>(z - .5)) << std::endl;

  // Write the byte order
  header << m_VB_BYTEORDER << ":\t"
         << ((ByteSwapper<short>::SystemIsBigEndian()) ? m_VB_BYTEORDER_MSB : m_VB_BYTEORDER_LSB) << std::endl;

  // Write the orientation code
  // NOTE:  The itk::ImageIOBase direction is a std::vector<std::vector > >, and threeDDirection is a 3x3 matrix
  itk::SpatialOrientationAdapter                soAdaptor;
  itk::SpatialOrientationAdapter::DirectionType threeDDirection;
  threeDDirection[0][0] = this->m_Direction[0][0];
  threeDDirection[0][1] = this->m_Direction[0][1];
  threeDDirection[0][2] = this->m_Direction[0][2];
  threeDDirection[1][0] = this->m_Direction[1][0];
  threeDDirection[1][1] = this->m_Direction[1][1];
  threeDDirection[1][2] = this->m_Direction[1][2];
  threeDDirection[2][0] = this->m_Direction[2][0];
  threeDDirection[2][1] = this->m_Direction[2][1];
  threeDDirection[2][2] = this->m_Direction[2][2];
  OrientationFlags oflag = soAdaptor.FromDirectionCosines(threeDDirection);
  {
    auto it = m_InverseOrientationMap.find(oflag);
    if (it != m_InverseOrientationMap.end())
    {
      header << m_VB_ORIENTATION << ":\t" << it->second << std::endl;
    }
  }

  // Add CUB specific parameters to header from MetaDictionary
  MetaDataDictionary & dic = GetMetaDataDictionary();
  for (const auto & key : dic.GetKeys())
  {
    std::string word;
    ExposeMetaData<std::string>(dic, key, word);
    if (!strcmp(key.c_str(), "resample_date"))
    {
      time_t rawtime = 0;
      time(&rawtime);
      word = ctime(&rawtime);
      header << key << ":\t" << word;
    }
    else
    {
      header << key << ":\t" << word << std::endl;
    }
  }
  // Write the terminating characters
  header << "\f\n";

  // Write the header to the file as data
  m_Writer->WriteData(header.str().c_str(), header.str().size());
}

/** The write function is not implemented */
void
VoxBoCUBImageIO::Write(const void * buffer)
{
  m_Writer = CreateWriter(m_FileName.c_str());
  WriteImageInformation();
  m_Writer->WriteData(buffer, this->GetImageSizeInBytes());
  delete m_Writer;
  m_Writer = nullptr;
}

/** Print Self Method */
void
VoxBoCUBImageIO::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "PixelType " << m_PixelType << '\n';
}

bool
VoxBoCUBImageIO::CheckExtension(const char * filename, bool & isCompressed)
{
  std::string fname = filename;

  if (fname.empty())
  {
    itkDebugMacro("No filename specified.");
    return false;
  }

  bool extensionFound = false;
  isCompressed = false;

  std::string::size_type giplPos = fname.rfind(".cub");
  if ((giplPos != std::string::npos) && (giplPos == fname.length() - 4))
  {
    extensionFound = true;
  }

  giplPos = fname.rfind(".cub.gz");
  if ((giplPos != std::string::npos) && (giplPos == fname.length() - 7))
  {
    extensionFound = true;
    isCompressed = true;
  }

  return extensionFound;
}

void
VoxBoCUBImageIO::InitializeOrientationMap()
{
  m_OrientationMap["RIP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIP;
  m_OrientationMap["LIP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIP;
  m_OrientationMap["RSP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSP;
  m_OrientationMap["LSP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSP;
  m_OrientationMap["RIA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RIA;
  m_OrientationMap["LIA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LIA;
  m_OrientationMap["RSA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RSA;
  m_OrientationMap["LSA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LSA;
  m_OrientationMap["IRP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRP;
  m_OrientationMap["ILP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILP;
  m_OrientationMap["SRP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRP;
  m_OrientationMap["SLP"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLP;
  m_OrientationMap["IRA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IRA;
  m_OrientationMap["ILA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ILA;
  m_OrientationMap["SRA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SRA;
  m_OrientationMap["SLA"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SLA;
  m_OrientationMap["RPI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPI;
  m_OrientationMap["LPI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPI;
  m_OrientationMap["RAI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAI;
  m_OrientationMap["LAI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAI;
  m_OrientationMap["RPS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RPS;
  m_OrientationMap["LPS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LPS;
  m_OrientationMap["RAS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_RAS;
  m_OrientationMap["LAS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_LAS;
  m_OrientationMap["PRI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRI;
  m_OrientationMap["PLI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLI;
  m_OrientationMap["ARI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARI;
  m_OrientationMap["ALI"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALI;
  m_OrientationMap["PRS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PRS;
  m_OrientationMap["PLS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PLS;
  m_OrientationMap["ARS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ARS;
  m_OrientationMap["ALS"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ALS;
  m_OrientationMap["IPR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPR;
  m_OrientationMap["SPR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPR;
  m_OrientationMap["IAR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAR;
  m_OrientationMap["SAR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAR;
  m_OrientationMap["IPL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IPL;
  m_OrientationMap["SPL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SPL;
  m_OrientationMap["IAL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_IAL;
  m_OrientationMap["SAL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_SAL;
  m_OrientationMap["PIR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIR;
  m_OrientationMap["PSR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSR;
  m_OrientationMap["AIR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIR;
  m_OrientationMap["ASR"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASR;
  m_OrientationMap["PIL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PIL;
  m_OrientationMap["PSL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_PSL;
  m_OrientationMap["AIL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_AIL;
  m_OrientationMap["ASL"] = SpatialOrientationEnums::ValidCoordinateOrientations::ITK_COORDINATE_ORIENTATION_ASL;

  for (auto & orientationElement : m_OrientationMap)
  {
    m_InverseOrientationMap[orientationElement.second] = orientationElement.first;
  }
}

void
VoxBoCUBImageIO::SwapBytesIfNecessary(void * buffer, BufferSizeType numberOfBytes)
{
  if (m_ComponentType == IOComponentEnum::CHAR)
  {
    VoxBoCUBImageIOSwapHelper<char>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::UCHAR)
  {
    VoxBoCUBImageIOSwapHelper<unsigned char>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::SHORT)
  {
    VoxBoCUBImageIOSwapHelper<short>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::USHORT)
  {
    VoxBoCUBImageIOSwapHelper<unsigned short>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::INT)
  {
    VoxBoCUBImageIOSwapHelper<int>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::UINT)
  {
    VoxBoCUBImageIOSwapHelper<unsigned int>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::LONG)
  {
    VoxBoCUBImageIOSwapHelper<long>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::ULONG)
  {
    VoxBoCUBImageIOSwapHelper<unsigned long>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::FLOAT)
  {
    VoxBoCUBImageIOSwapHelper<float>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else if (m_ComponentType == IOComponentEnum::DOUBLE)
  {
    VoxBoCUBImageIOSwapHelper<double>::SwapIfNecessary(buffer, numberOfBytes, m_ByteOrder);
  }
  else
  {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("Pixel Type Unknown");
    throw exception;
  }
}
} // end namespace itk
