/*============================================================================
  MetaIO
  Copyright 2000-2010 Insight Software Consortium

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
#include "metaImage.h"

#include <stdio.h>
#include <ctype.h>
#include <string>
#include <string.h> // for memcpy
#include <stdlib.h> // for atoi
#include <math.h>
#include <algorithm> // for std::min & std::max

#if defined (__BORLANDC__) && (__BORLANDC__ >= 0x0580)
#include <mem.h>
#endif

#if defined(_WIN32)
# include <io.h>
#endif

// support for access
#ifndef _WIN32
#include <limits.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <pwd.h>
#include <termios.h>
#include <signal.h>    /* sigprocmask */
#endif

namespace {

void openReadStream(METAIO_STREAM::ifstream & inputStream, const char * fname)
{
#ifdef __sgi
  inputStream.open( fname, METAIO_STREAM::ios::in );
#else
  inputStream.open( fname, METAIO_STREAM::ios::in |
                           METAIO_STREAM::ios::binary );
#endif
}

void openWriteStream(METAIO_STREAM::ofstream & outputStream, const char * fname, bool append)
{
// Some older sgi compilers have a error in the ofstream constructor
// that requires a file to exist for output
#ifdef __sgi
  {
  METAIO_STREAM::ofstream tFile(fname, METAIO_STREAM::ios::out);
  tFile.close();
  }
#endif

  if(!append)
    {
#ifdef __sgi
    outputStream.open(fname, METAIO_STREAM::ios::out);
#else
    outputStream.open(fname, METAIO_STREAM::ios::binary |
                             METAIO_STREAM::ios::out);
#endif
    }
  else
    {
#ifdef __sgi
    outputStream.open(fname, METAIO_STREAM::ios::app |
                             METAIO_STREAM::ios::out);
#else
    outputStream.open(fname, METAIO_STREAM::ios::binary |
                             METAIO_STREAM::ios::app |
                             METAIO_STREAM::ios::out);
#endif
    }
}

const unsigned int MAXPATHLENGHT = 2048;

} // end anonymous namespace

#if (METAIO_USE_NAMESPACE)
namespace METAIO_NAMESPACE {
#endif

// 1 Gigabyte is the maximum chunk to read/write in on function call
static const METAIO_STL::streamoff MaxIOChunk = 1024*1024*1024;

//
// MetaImage Constructors
//
MetaImage::
MetaImage()
:MetaObject()
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->compressedStream = NULL;
  m_CompressionTable->buffer = NULL;
  Clear();
  }

//
MetaImage::
MetaImage(const char *_headerName)
:MetaObject()
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->compressedStream = NULL;
  m_CompressionTable->buffer = NULL;
  Clear();

  Read(_headerName);
  }

//
MetaImage::
MetaImage(MetaImage *_im)
:MetaObject()
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->compressedStream = NULL;
  m_CompressionTable->buffer = NULL;
  Clear();

  InitializeEssential(_im->NDims(),
                      _im->DimSize(),
                      _im->ElementSpacing(),
                      _im->ElementType(),
                      _im->ElementNumberOfChannels(),
                      _im->ElementData(),
                      false);
  CopyInfo(_im);
  }

//
void MetaImage::
InitHelper(int _nDims,
          const int * _dimSize,
          const double * _elementSpacing,
          MET_ValueEnumType _elementType,
          int _elementNumberOfChannels,
          void *_elementData)
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->buffer = NULL;
  m_CompressionTable->compressedStream = NULL;
  Clear();

  if(_elementData == NULL)
    {
    InitializeEssential(_nDims,
                        _dimSize,
                        _elementSpacing,
                        _elementType,
                        _elementNumberOfChannels,
                        NULL, true);
    }
  else
    {
    InitializeEssential(_nDims,
                        _dimSize,
                        _elementSpacing,
                        _elementType,
                        _elementNumberOfChannels,
                        _elementData, false);
    }

  }

//
MetaImage::
MetaImage(int _nDims,
          const int * _dimSize,
          const float * _elementSpacing,
          MET_ValueEnumType _elementType,
          int _elementNumberOfChannels,
          void *_elementData)
:MetaObject()
  {
  // Only consider at most 10 element of spacing:
  // See MetaObject::InitializeEssential(_nDims)
  double tmpElementSpacing[10];
  int ndims = std::max( std::min( _nDims, 10 ), 0);
  for( int i = 0; i < ndims; ++i )
    {
    tmpElementSpacing[i] = static_cast<double>(_elementSpacing[i]);
    }
   InitHelper(_nDims, _dimSize, tmpElementSpacing, _elementType,
     _elementNumberOfChannels, _elementData);
  }

//
MetaImage::
MetaImage(int _nDims,
          const int * _dimSize,
          const double * _elementSpacing,
          MET_ValueEnumType _elementType,
          int _elementNumberOfChannels,
          void *_elementData)
:MetaObject()
  {
  InitHelper(_nDims, _dimSize, _elementSpacing, _elementType,
    _elementNumberOfChannels, _elementData);
  }

//
MetaImage::
MetaImage(int _x, int _y,
          double _elementSpacingX, double _elementSpacingY,
          MET_ValueEnumType _elementType,
          int _elementNumberOfChannels, void *_elementData)
:MetaObject()
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->compressedStream = NULL;
  m_CompressionTable->buffer = NULL;
  Clear();

  int ds[2];
  ds[0] = _x;
  ds[1] = _y;

  double es[2];
  es[0] = _elementSpacingX;
  es[1] = _elementSpacingY;

  if(_elementData == NULL)
    {
    InitializeEssential(2,
                        ds,
                        es,
                        _elementType,
                        _elementNumberOfChannels,
                        NULL,
                        true);
    }
  else
    {
    InitializeEssential(2,
                        ds,
                        es,
                        _elementType,
                        _elementNumberOfChannels,
                        _elementData,
                        false);
    }
  }

//
MetaImage::
MetaImage(int _x, int _y, int _z,
          double _elementSpacingX,
          double _elementSpacingY,
          double _elementSpacingZ,
          MET_ValueEnumType _elementType,
          int _elementNumberOfChannels,
          void *_elementData)
:MetaObject()
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage()" << METAIO_STREAM::endl;
    }

  m_CompressionTable = new MET_CompressionTableType;
  m_CompressionTable->compressedStream = NULL;
  m_CompressionTable->buffer = NULL;
  Clear();

  int ds[3];
  ds[0] = _x;
  ds[1] = _y;
  ds[2] = _z;

  double es[3];
  es[0] = _elementSpacingX;
  es[1] = _elementSpacingY;
  es[2] = _elementSpacingZ;

  if(_elementData == NULL)
    {
    InitializeEssential(3,
                        ds,
                        es,
                        _elementType,
                        _elementNumberOfChannels,
                        NULL,
                        true);
    }
  else
    {
    InitializeEssential(3,
                        ds,
                        es,
                        _elementType,
                        _elementNumberOfChannels,
                        _elementData,
                        false);
    }
  }

//
MetaImage::
~MetaImage()
  {
  M_Destroy();
  }

//
void MetaImage::
PrintInfo() const
  {
  int i;

  MetaObject::PrintInfo();

  char s[MAXPATHLENGHT];
  MET_ImageModalityToString(m_Modality, s);
  METAIO_STREAM::cout << "Modality = " << s << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "DimSize = ";
  for(i=0; i<m_NDims; i++)
    {
    METAIO_STREAM::cout << m_DimSize[i] << " ";
    }
  METAIO_STREAM::cout << METAIO_STREAM::endl;
  METAIO_STREAM::cout << "SubQuantity = ";
  for(i=0; i<m_NDims; i++)
    {
    METAIO_STREAM::cout << m_SubQuantity[i] << " ";
    }
  METAIO_STREAM::cout << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "Quantity = " << m_Quantity << METAIO_STREAM::endl;


  METAIO_STREAM::cout << "HeaderSize = " << m_HeaderSize << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "SequenceID = ";
  for(i=0; i<m_NDims; i++)
    {
    METAIO_STREAM::cout << m_SequenceID[i] << " ";
    }
  METAIO_STREAM::cout << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "ElementSizeValid = " << (int)m_ElementSizeValid
            << METAIO_STREAM::endl;
  METAIO_STREAM::cout << "ElementSize = ";
  for(i=0; i<m_NDims; i++)
    {
    METAIO_STREAM::cout << m_ElementSize[i] << " ";
    }
  METAIO_STREAM::cout << METAIO_STREAM::endl;

  char str[MAXPATHLENGHT];
  MET_TypeToString(m_ElementType, str);
  METAIO_STREAM::cout << "ElementType = " << str << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "ElementNumberOfChannels = "
                      << m_ElementNumberOfChannels << METAIO_STREAM::endl;

  if(m_ElementMinMaxValid)
    {
    METAIO_STREAM::cout << "Min and Max are valid" << METAIO_STREAM::endl;
    METAIO_STREAM::cout << "   Min = " << m_ElementMin << METAIO_STREAM::endl;
    METAIO_STREAM::cout << "   Max = " << m_ElementMax << METAIO_STREAM::endl;
    }
  else
    {
    METAIO_STREAM::cout << "Min and Max are not valid" << METAIO_STREAM::endl;
    }

  METAIO_STREAM::cout << "ElementToIntensityFunctionSlope = "
                      << m_ElementToIntensityFunctionSlope
                      << METAIO_STREAM::endl;
  METAIO_STREAM::cout << "ElementToIntensityFunctionOffset = "
                      << m_ElementToIntensityFunctionOffset
                      << METAIO_STREAM::endl;


  METAIO_STREAM::cout << "AutoFreeElementData = "
                      << ((m_AutoFreeElementData)?"True":"False")
                      << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "ElementData = "
                      << ((m_ElementData==NULL)?"NULL":"Valid")
                      << METAIO_STREAM::endl;

  METAIO_STREAM::cout << "ElementDataFileName = "
                      << m_ElementDataFileName << METAIO_STREAM::endl;

  }

void MetaImage::
CopyInfo(const MetaObject * _object)
  {
  MetaObject::CopyInfo(_object);

  if(_object)
    {
    const MetaImage * im;
    try
      {
      im = (const MetaImage *)(_object);
      }
    catch( ... )
      {
      return;
      }

    if( im )
      {
      Modality(im->Modality());

      HeaderSize(im->HeaderSize());

      SequenceID(im->SequenceID());

      ElementSizeValid(im->ElementSizeValid());
      if(im->ElementSizeValid())
        {
        ElementSize(im->ElementSize());
        }

      ElementMinMaxValid(im->ElementMinMaxValid());
      if(im->ElementMinMaxValid())
        {
        ElementMin(im->ElementMin());
        ElementMax(im->ElementMax());
        }

      ElementToIntensityFunctionSlope(im->ElementToIntensityFunctionSlope());
      ElementToIntensityFunctionOffset(im->ElementToIntensityFunctionOffset());
      }
    }
  }

/** Clear function */
void MetaImage::Clear(void)
{
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: Clear" << METAIO_STREAM::endl;
    }

  m_Modality = MET_MOD_UNKNOWN;

  m_DimSize[0] = 0;
  m_SubQuantity[0] = 0;
  m_Quantity = 0;

  m_HeaderSize = 0;

  memset(m_SequenceID, 0, sizeof(m_SequenceID));

  m_ElementSizeValid = false;
  memset(m_ElementSize, 0, sizeof(m_ElementSize));

  m_ElementType = MET_NONE;

  m_ElementNumberOfChannels = 1;

  m_ElementMinMaxValid = false;
  m_ElementMin = 0;
  m_ElementMax = 0;

  m_ElementToIntensityFunctionSlope = 1;
  m_ElementToIntensityFunctionOffset = 0;

  m_AutoFreeElementData = true;

  m_ElementData = NULL;

  strcpy(m_ElementDataFileName, "");

  MetaObject::Clear();

  // Change the default for this object
  m_BinaryData = true;

  if(m_CompressionTable)
    {
    if(m_CompressionTable->compressedStream)
      {
      inflateEnd(m_CompressionTable->compressedStream);
      delete m_CompressionTable->compressedStream;
      delete [] m_CompressionTable->buffer;
      m_CompressionTable->buffer = NULL;
      }
    m_CompressionTable->compressedStream = NULL;
    m_CompressionTable->offsetList.clear();
    }
  else
    {
    m_CompressionTable = new MET_CompressionTableType;
    m_CompressionTable->compressedStream = NULL;
    }

}

bool MetaImage::
InitializeEssential(int _nDims,
                    const int * _dimSize,
                    const float * _elementSpacing,
                    MET_ValueEnumType _elementType,
                    int _elementNumberOfChannels,
                    void * _elementData,
                    bool _allocElementMemory)
{
  // Only consider at most 10 element of spacing:
  // See MetaObject::InitializeEssential(_nDims)
  double tmpElementSpacing[10];
  int ndims = std::max( std::min( _nDims, 10 ), 0);
  for( int i = 0; i < ndims; ++i )
    {
    tmpElementSpacing[i] = static_cast<double>(_elementSpacing[i]);
    }
  return InitializeEssential(_nDims, _dimSize, tmpElementSpacing, _elementType,
    _elementNumberOfChannels, _elementData, _allocElementMemory);
}

bool MetaImage::
InitializeEssential(int _nDims,
                    const int * _dimSize,
                    const double * _elementSpacing,
                    MET_ValueEnumType _elementType,
                    int _elementNumberOfChannels,
                    void * _elementData,
                    bool _allocElementMemory)
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: Initialize" << METAIO_STREAM::endl;
    }

  MetaObject::InitializeEssential(_nDims);

  int i;
  if(!m_CompressionTable)
    {
    m_CompressionTable = new MET_CompressionTableType;
    m_CompressionTable->buffer = NULL;
    m_CompressionTable->compressedStream = NULL;
    }
  m_SubQuantity[0] = 1;
  m_Quantity = 1;
  m_ElementSizeValid = false;
  for(i=0; i<m_NDims; i++)
    {
    m_DimSize[i] = _dimSize[i];
    m_Quantity *= _dimSize[i];
    if(i>0)
      {
      m_SubQuantity[i] = m_SubQuantity[i-1]*m_DimSize[i-1];
      }
    m_ElementSpacing[i] = _elementSpacing[i];
    if(m_ElementSize[i] == 0)
      {
      m_ElementSize[i] = m_ElementSpacing[i];
      }
    else
      {
      m_ElementSizeValid = true;
      }
    }

  m_ElementType = _elementType;

  m_ElementNumberOfChannels = _elementNumberOfChannels;

  if(_elementData != NULL)
    {
    m_AutoFreeElementData = false;
    m_ElementData = (void *)_elementData;
    }
  else if(_allocElementMemory)
    {
    m_AutoFreeElementData = true;
    MET_SizeOfType(m_ElementType, &i);
    m_ElementData = new char[static_cast<size_t>(m_Quantity*m_ElementNumberOfChannels*i)];
    if(m_ElementData == NULL)
      {
      METAIO_STREAM::cerr << "MetaImage:: M_Allocate:: Insufficient memory"
                          << METAIO_STREAM::endl;
      return false;
      }
    }
  else
    {
    m_AutoFreeElementData = true;
    m_ElementData = NULL;
    }

  return true;
  }


//
//
//
int MetaImage::
HeaderSize(void) const
  {
  return m_HeaderSize;
  }

void MetaImage::
HeaderSize(int _headerSize)
  {
  m_HeaderSize = _headerSize;
  }

//
//
//
MET_ImageModalityEnumType MetaImage::
Modality(void) const
  {
  return m_Modality;
  }

void MetaImage::
Modality(MET_ImageModalityEnumType _modality)
  {
  m_Modality = _modality;
  }

//
//
//
const int * MetaImage::
DimSize(void) const
  {
  return m_DimSize;
  }

int MetaImage::
DimSize(int _i) const
  {
  return m_DimSize[_i];
  }

//
//
//
METAIO_STL::streamoff MetaImage::
Quantity(void) const
  {
  return m_Quantity;
  }

//
//
//
const METAIO_STL::streamoff * MetaImage::
SubQuantity(void) const
  {
  return m_SubQuantity;
  }

METAIO_STL::streamoff MetaImage::
SubQuantity(int _i) const
  {
  return m_SubQuantity[_i];
  }

//
//
//
const float * MetaImage::
SequenceID(void) const
  {
  return m_SequenceID;
  }

float MetaImage::
SequenceID(int _i) const
  {
  return m_SequenceID[_i];
  }

void MetaImage::
SequenceID(const float *_sequenceID)
  {
  memcpy(m_SequenceID, _sequenceID, m_NDims*sizeof(float));
  }

void MetaImage::
SequenceID(int _i, float _value)
  {
  m_SequenceID[_i] = _value;
  }

//
//
//
bool MetaImage::
ElementSizeValid(void) const
  {
  return m_ElementSizeValid;
  }

void MetaImage::
ElementSizeValid(bool _elementSizeValid)
  {
  m_ElementSizeValid = _elementSizeValid;
  }

const double * MetaImage::
ElementSize(void) const
  {
  return m_ElementSize;
  }

double MetaImage::
ElementSize(int _i) const
  {
  return m_ElementSize[_i];
  }

void MetaImage::
ElementSize(const double *_elementSize)
  {
  memcpy(m_ElementSize, _elementSize, m_NDims*sizeof(*m_ElementSize));
  m_ElementSizeValid = true;
  }

void MetaImage::
ElementSize(const float *_elementSize)
  {
  for(int i = 0; i < m_NDims; ++i)
    {
    m_ElementSize[i] = static_cast<double>(_elementSize[i]);
    }
  m_ElementSizeValid = true;
  }


void MetaImage::
ElementSize(int _i, double _value)
  {
  m_ElementSize[_i] = _value;
  m_ElementSizeValid = true;
  }

//
//
//
MET_ValueEnumType MetaImage::
ElementType(void) const
  {
  return m_ElementType;
  }

void MetaImage::
ElementType(MET_ValueEnumType _elementType)
  {
  m_ElementType = _elementType;
  }

//
//
//
int MetaImage::
ElementNumberOfChannels(void) const
  {
  return m_ElementNumberOfChannels;
  }

void MetaImage::
ElementNumberOfChannels(int _elementNumberOfChannels)
  {
  m_ElementNumberOfChannels = _elementNumberOfChannels;
  }

//
//
//
void MetaImage::
ElementByteOrderSwap(METAIO_STL::streamoff _quantity)
  {

  // use the user provided value if provided or the internal ivar
  METAIO_STL::streamoff quantity = _quantity ? _quantity : m_Quantity;

  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: ElementByteOrderSwap"
                        << METAIO_STREAM::endl;
    }

  int eSize;
  MET_SizeOfType(m_ElementType, &eSize);
  switch(eSize)
    {
    default:
    case 0:
    case 1:
      {
      break;
      }
    case 2:
      {
      for(size_t i=0;
        i<static_cast<size_t>(quantity*m_ElementNumberOfChannels); i++)
        {
        ((MET_USHORT_TYPE *)m_ElementData)[i] =
              MET_ByteOrderSwapShort(((MET_USHORT_TYPE *)m_ElementData)[i]);
        }
      break;
      }
    case 4:
      {
      for(size_t i=0;
        i<static_cast<size_t>(quantity*m_ElementNumberOfChannels); i++)
        {
        ((MET_UINT_TYPE *)m_ElementData)[i] =
              MET_ByteOrderSwapLong(((MET_UINT_TYPE *)m_ElementData)[i]);
        }
      break;
      }
    case 8:
      {
      char* data = (char*)m_ElementData;
      for(size_t i=0;
        i<static_cast<size_t>(quantity*m_ElementNumberOfChannels); i++)
        {
        MET_ByteOrderSwap8(data);
        data += 8;
        }
      break;
      }
    }
  m_BinaryDataByteOrderMSB = !m_BinaryDataByteOrderMSB;
  }

bool MetaImage::
ElementByteOrderFix(METAIO_STL::streamoff _quantity)
  {
  if(m_BinaryDataByteOrderMSB != MET_SystemByteOrderMSB())
    {
    ElementByteOrderSwap(_quantity);
    return true;
    }
  return true;
  }

//
//
//
bool MetaImage::
ElementMinMaxValid(void) const
  {
  return m_ElementMinMaxValid;
  }

void MetaImage::
ElementMinMaxValid(bool _elementMinMaxValid)
  {
  m_ElementMinMaxValid = _elementMinMaxValid;
  }

void MetaImage::
ElementMinMaxRecalc(void)
  {
  double tf;

  if(m_ElementData == NULL)
    return;

  ElementByteOrderFix();

  MET_ValueToDouble(m_ElementType, m_ElementData, 0, &tf);
  m_ElementMin = tf;
  m_ElementMax = tf;
  for(size_t i=1;
    i<static_cast<size_t>(m_Quantity*m_ElementNumberOfChannels); i++)
    {
    MET_ValueToDouble(m_ElementType, m_ElementData, i, &tf);
    if(tf<m_ElementMin)
      {
      m_ElementMin = tf;
      }
    else if(tf>m_ElementMax)
      {
      m_ElementMax = tf;
      }
    }

  m_ElementMinMaxValid = true;
  }

double MetaImage::
ElementMin(void) const
  {
  return m_ElementMin;
  }

void MetaImage::
ElementMin(double _elementMin)
  {
  m_ElementMin = _elementMin;
  }

double MetaImage::
ElementMax(void) const
  {
  return m_ElementMax;
  }

void MetaImage::
ElementMax(double _elementMax)
  {
  m_ElementMax = _elementMax;
  }

//
//
//
double MetaImage::
ElementToIntensityFunctionSlope(void) const
  {
  return m_ElementToIntensityFunctionSlope;
  }

void MetaImage::
ElementToIntensityFunctionSlope(double _elementToIntensityFunctionSlope)
  {
  m_ElementToIntensityFunctionSlope = _elementToIntensityFunctionSlope;
  }

double MetaImage::
ElementToIntensityFunctionOffset(void) const
  {
  return m_ElementToIntensityFunctionOffset;
  }

void MetaImage::
ElementToIntensityFunctionOffset(double _elementOffset)
  {
  m_ElementToIntensityFunctionOffset = _elementOffset;
  }

//
//
//
bool MetaImage::
AutoFreeElementData(void) const
  {
  return m_AutoFreeElementData;
  }

void MetaImage::
AutoFreeElementData(bool _autoFreeElementData)
  {
  m_AutoFreeElementData = _autoFreeElementData;
  }

//
//
//
const char * MetaImage::
ElementDataFileName(void) const
  {
  return m_ElementDataFileName;
  }

void MetaImage::
ElementDataFileName(const char * _elementDataFileName)
  {
  strcpy(m_ElementDataFileName, _elementDataFileName);
  }

//
//
//
void * MetaImage::
ElementData(void)
  {
  return m_ElementData;
  }

double MetaImage::
ElementData(METAIO_STL::streamoff _i) const
  {
  double tf = 0;
  MET_ValueToDouble(m_ElementType, m_ElementData, _i, &tf);

  return tf;
  }

bool MetaImage::
ElementData(METAIO_STL::streamoff _i, double _v)
  {
  if(_i<m_Quantity)
    {
    MET_DoubleToValue(_v, m_ElementType, m_ElementData, _i);
    return true;
    }
  return false;
  }

void MetaImage::
ElementData(void * _elementData, bool _autoFreeElementData)
  {
  if(m_AutoFreeElementData)
    {
    delete [] (char *)m_ElementData;
    }
  m_ElementData = _elementData;
  m_AutoFreeElementData = _autoFreeElementData;
  }

//
//
//
bool MetaImage::
ConvertElementDataTo(MET_ValueEnumType _elementType,
                     double _toMin, double _toMax)
  {
  int eSize;
  MET_SizeOfType(_elementType, &eSize);
  void * newElementData = new char[ static_cast<size_t>(
    m_Quantity*m_ElementNumberOfChannels*eSize) ];

  ElementByteOrderFix();
  if(!ElementMinMaxValid())
    {
    ElementMinMaxRecalc();
    }

  for(size_t i=0;
    i<static_cast<size_t>(m_Quantity*m_ElementNumberOfChannels); i++)
    {
    MET_ValueToValue(m_ElementType, m_ElementData, i, _elementType,
                     newElementData, m_ElementMin, m_ElementMax,
                     _toMin, _toMax);
    }

  if(m_AutoFreeElementData)
    {
    delete [] (char *)m_ElementData;
    }
  m_ElementData = newElementData;
  m_ElementType = _elementType;
  m_ElementMinMaxValid = true;
  m_ElementMin = _toMin;
  m_ElementMax = _toMax;
  m_AutoFreeElementData = true;

  return true;
  }

bool MetaImage::
ConvertElementDataToIntensityData(MET_ValueEnumType _elementType)
  {
  ElementByteOrderFix();
  if(!ElementMinMaxValid())
    {
    ElementMinMaxRecalc();
    }

  double toMin = m_ElementMin + m_ElementToIntensityFunctionOffset;
  double toMax = (m_ElementMax-m_ElementMin)
                   * m_ElementToIntensityFunctionSlope
                   + m_ElementMin;

  return ConvertElementDataTo(_elementType, toMin, toMax);
  }

bool MetaImage::
ConvertIntensityDataToElementData(MET_ValueEnumType _elementType)
  {
  ElementByteOrderFix();
  if(!ElementMinMaxValid())
    {
    ElementMinMaxRecalc();
    }

  double toMin = m_ElementMin - m_ElementToIntensityFunctionOffset;
  double toMax = (m_ElementMax - m_ElementMin)
                   / m_ElementToIntensityFunctionSlope
                   + toMin;

  return ConvertElementDataTo(_elementType, toMin, toMax);
  }

// return true if the file exists
bool MetaImage::M_FileExists(const char* filename) const
{
#ifdef _MSC_VER
# define access _access
#endif
#ifndef R_OK
# define R_OK 04
#endif
  if ( access(filename, R_OK) != 0 )
    {
    return false;
    }
  else
    {
    return true;
    }
}

bool MetaImage::FileIsFullPath(const char* in_name) const
{
#if defined(_WIN32) || defined(__CYGWIN__)
  // On Windows, the name must be at least two characters long.
  if(strlen(in_name) < 2)
    {
    return false;
    }
  if(in_name[1] == ':')
    {
    return true;
    }
  if(in_name[0] == '\\')
    {
    return true;
    }
#else
  // On UNIX, the name must be at least one character long.
  if(strlen(in_name) < 1)
    {
    return false;
    }
#endif
#if !defined(_WIN32)
  if(in_name[0] == '~')
    {
    return true;
    }
#endif
  // On UNIX, the name must begin in a '/'.
  // On Windows, if the name begins in a '/', then it is a full
  // network path.
  if(in_name[0] == '/')
    {
    return true;
    }
  return false;
}

// Return the value of a tag
METAIO_STL::string
MetaImage::
M_GetTagValue(const METAIO_STL::string & buffer, const char* tag) const
{
  size_t stringPos = buffer.find(tag);
  if( stringPos == METAIO_STL::string::npos )
    {
    return "";
    }

  size_t pos2 = buffer.find("=",stringPos);
  if(pos2 == METAIO_STL::string::npos )
    {
    pos2 = buffer.find(":",stringPos);
    }

  if(pos2 == METAIO_STL::string::npos )
    {
    return "";
    }

  size_t posend = buffer.find('\r',pos2);
  if(posend == METAIO_STL::string::npos )
    {
    posend = buffer.find('\n',pos2);
    }

  // Get the element data filename
  METAIO_STL::string value = "";
  bool firstspace = true;
  size_t index = pos2+1;
  while(index<buffer.size()
        && buffer[index] != '\r'
        && buffer[index] != '\n'
        )
    {
    if(buffer[index] != ' ')
      {
      firstspace = false;
      }
    if(!firstspace)
      {
      value += buffer[index];
      }
    index++;
    }

  return value;
}

//
//
//
bool MetaImage::
CanRead(const char *_headerName) const
  {
  // First check the extension
  METAIO_STL::string fname = _headerName;
  if(  fname == "" )
    {
    return false;
    }

  bool extensionFound = false;

  METAIO_STL::string::size_type stringPos = fname.rfind(".mhd");
  if ((stringPos != METAIO_STL::string::npos)
      && (stringPos == fname.length() - 4))
    {
    extensionFound = true;
    }

  stringPos = fname.rfind(".mha");
  if ((stringPos != METAIO_STL::string::npos)
      && (stringPos == fname.length() - 4))
    {
    extensionFound = true;
    }

  if( !extensionFound )
    {
    return false;
    }

  // Now check the file content
  METAIO_STREAM::ifstream inputStream;

  openReadStream(inputStream, fname.c_str());

  if( inputStream.fail() )
    {
    return false;
    }

  char* buf = new char[8001];
  inputStream.read(buf,8000);
  unsigned long fileSize = static_cast<unsigned long>(inputStream.gcount());
  buf[fileSize] = 0;
  METAIO_STL::string header(buf);
  header.resize(fileSize);
  delete [] buf;
  inputStream.close();

  stringPos = header.find("NDims");
  if( stringPos == METAIO_STL::string::npos )
    {
    return false;
    }

  METAIO_STL::string elementDataFileName = M_GetTagValue(header,"ElementDataFile");

  return true;
  }

bool MetaImage::
Read(const char *_headerName, bool _readElements, void * _buffer)
  {
  M_Destroy();

  Clear();

  M_SetupReadFields();

  if(_headerName != NULL)
    {
    strcpy(m_FileName, _headerName);
    }

  M_PrepareNewReadStream();

  METAIO_STREAM::ifstream * tmpReadStream = new METAIO_STREAM::ifstream;

  openReadStream(*tmpReadStream, m_FileName);

  if(!tmpReadStream->is_open())
    {
    delete tmpReadStream;
    return false;
    }

  if( !this->ReadStream(0, tmpReadStream, _readElements, _buffer) )
    {
    tmpReadStream->close();
    delete tmpReadStream;
    return false;
    }

  tmpReadStream->close();

  delete tmpReadStream;

  return true;
  }

bool MetaImage::
CanReadStream(METAIO_STREAM::ifstream * _stream) const
  {
  if(!strncmp(MET_ReadType(*_stream).c_str(), "Image", 5))
    {
    return true;
    }
  return false;
  }


bool MetaImage::
ReadStream(int _nDims,
           METAIO_STREAM::ifstream * _stream,
           bool _readElements,
           void * _buffer)
  {
  if(!MetaObject::ReadStream(_nDims, _stream))
    {
    METAIO_STREAM::cerr << "MetaImage: Read: Cannot parse file"
                        << METAIO_STREAM::endl;
    return false;
    }

  if(_readElements)
    {
    if(_buffer == NULL)
      {
      InitializeEssential(m_NDims,
                          m_DimSize,
                          m_ElementSpacing,
                          m_ElementType,
                          m_ElementNumberOfChannels,
                          NULL, true);
      }
    else
      {
      InitializeEssential(m_NDims,
                          m_DimSize,
                          m_ElementSpacing,
                          m_ElementType,
                          m_ElementNumberOfChannels,
                          _buffer, false);
      }

    int i;
    size_t j;
    bool usePath;
    char pathName[MAXPATHLENGHT];
    char fName[MAXPATHLENGHT];
    usePath = MET_GetFilePath(m_FileName, pathName);

    if(!strcmp("Local", m_ElementDataFileName) ||
       !strcmp("LOCAL", m_ElementDataFileName) ||
       !strcmp("local", m_ElementDataFileName))
      {
      M_ReadElements(_stream, m_ElementData, m_Quantity);
      }
    else if(!strncmp("LIST", m_ElementDataFileName,4))
      {
      int fileImageDim = m_NDims - 1;
      int nWrds;
      char **wrds;
      MET_StringToWordArray(m_ElementDataFileName, &nWrds, &wrds);
      if(nWrds > 1)
        {
        fileImageDim = (int)atof(wrds[1]);
        }
      for(i=0; i<nWrds; i++)
        {
        delete [] wrds[i];
        }
      delete [] wrds;
      if ( (fileImageDim == 0) || (fileImageDim > m_NDims) )
        {
        // if optional file dimension size is not given or is larger than
        // overall dimension then default to a size of m_NDims - 1.
        fileImageDim = m_NDims-1;
        }
      char s[1024];
      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      elementSize *= m_ElementNumberOfChannels;
      int totalFiles = 1;
      for(i = m_NDims; i > fileImageDim; i--)
        {
        totalFiles *= m_DimSize[i-1];
        }
      for(i=0; i< totalFiles && !_stream->eof(); i++)
        {
        _stream->getline(s, 1024);
        if(!_stream->eof())
          {
          j = strlen(s)-1;
          while(j>0 && (isspace(s[j]) || !isprint(s[j])))
            {
            s[j--] = '\0';
            }
          if(usePath && !FileIsFullPath(s))
            {
            sprintf(fName, "%s%s", pathName, s);
            }
          else
            {
            strcpy(fName, s);
            }

          openReadStream(*readStreamTemp, fName);
          if(!readStreamTemp->is_open())
            {
            METAIO_STREAM::cerr << "MetaImage: Read: cannot open slice"
                                << METAIO_STREAM::endl;
            continue;
            }
          M_ReadElements(readStreamTemp,
                       &(((char *)m_ElementData)[i*m_SubQuantity[fileImageDim]*
                                                 elementSize]),
                       m_SubQuantity[fileImageDim]);
          readStreamTemp->close();
          }
        }
      delete readStreamTemp;
      }
    else if(strstr(m_ElementDataFileName, "%"))
      {
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      elementSize *= m_ElementNumberOfChannels;

      int nWrds;
      char **wrds;
      int minV = 1;
      int maxV = m_DimSize[m_NDims-1];
      int stepV = 1;
      char s[MAXPATHLENGHT];
      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;
      MET_StringToWordArray(m_ElementDataFileName, &nWrds, &wrds);
      if(nWrds >= 2)
        {
        minV = (int)atof(wrds[1]);
        maxV = minV + m_DimSize[m_NDims-1] - 1;
        }
      if(nWrds >= 3)
        {
        maxV = (int)atof(wrds[2]);
        stepV = (maxV-minV)/(m_DimSize[m_NDims-1]);
        }
      if(nWrds >= 4)
        {
        stepV = (int)atof(wrds[3]);
        }
      if(nWrds >= 5 )
      {
        // In this case, the filename must have had spaces in the
        // name.  The filename was parsed into multiple pieces by the
        // MET_StringToWordArray, which parses based on spaces.
        // Thus, we need to reconstruct the filename in this case.
        // The last three wrds must be numbers.  If they are not, we give an error.
        for( i = nWrds-3; i < nWrds; i++ )
        {
          for( j = 0; j < strlen(wrds[i]); j++ )
          {
            if( !isdigit(wrds[i][j]) )
            {
              METAIO_STREAM::cerr << "MetaImage: Read: Last three arguments must be numbers!"
                  << METAIO_STREAM::endl;
              continue;
            }
          }
        }
        stepV = (int)atof(wrds[nWrds-1]);
        maxV =  (int)atof(wrds[nWrds-2]);
        minV =  (int)atof(wrds[nWrds-3]);
        for( i = 1; i < nWrds-3; i++ )
        {
          strcat(wrds[0]," ");
          strcat(wrds[0],wrds[i]);
        }
      }
      // If the specified size of the third dimension is less than the size
      // specified by the regular expression, we should only read a volume with the specified
      // size.  Otherwise, the code will crash when trying to fill m_ElementData more than it can hold.
      // Therefore, we modify maxV to ensure that the images spanned by minV:stepV:maxV are less than or equal
      // to the size in the last dimension.
      int numberOfImages = 1 + (maxV - minV)/stepV;
      if( numberOfImages > m_DimSize[m_NDims-1] )
      {
        maxV = (m_DimSize[m_NDims-1]-1)*stepV + minV;
      }
      int cnt = 0;
      for(i=minV; i<=maxV; i += stepV)
        {
        sprintf(s, wrds[0], i);
        if(usePath && !FileIsFullPath(s))
          {
          sprintf(fName, "%s%s", pathName, s);
          }
        else
          {
          strcpy(fName, s);
          }
        openReadStream(*readStreamTemp,fName);
        if(!readStreamTemp->is_open())
          {
          METAIO_STREAM::cerr << "MetaImage: Read: cannot construct file"
                              << METAIO_STREAM::endl;
          continue;
          }

        M_ReadElements(readStreamTemp,
                       &(((char *)m_ElementData)[cnt*m_SubQuantity[m_NDims-1]*
                                                 elementSize]),
                       m_SubQuantity[m_NDims-1]);
        cnt++;

        readStreamTemp->close();
        }
      delete readStreamTemp;
      for(i=0; i<nWrds; i++)
        {
        delete [] wrds[i];
        }
      delete [] wrds;
      }
    else
      {
      if(usePath && !FileIsFullPath(m_ElementDataFileName))
        {
        sprintf(fName, "%s%s", pathName, m_ElementDataFileName);
        }
      else
        {
        strcpy(fName, m_ElementDataFileName);
        }

      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;

      const char *extensions[] = { "", ".gz", ".Z", 0 };
      for(unsigned ii = 0; extensions[ii] != 0; ii++)
        {
        METAIO_STL::string tempFName(fName);
        tempFName += extensions[ii];
        openReadStream(*readStreamTemp,tempFName.c_str());
        if(readStreamTemp->is_open())
          {
          if(ii > 0)
            {
            this->CompressedData(true);
            this->BinaryData(true);
            }
          break;
          }
        }

      if(!readStreamTemp->is_open())
        {
        METAIO_STREAM::cerr << "MetaImage: Read: Cannot open data file"
                            << METAIO_STREAM::endl;
        if(m_ReadStream)
          {
          m_ReadStream->close();
          }
        return false;
        }
      M_ReadElements(readStreamTemp, m_ElementData, m_Quantity);

      readStreamTemp->close();
      delete readStreamTemp;
      }
    }

  return true;
  }


//
//
//
bool MetaImage::
Write(const char *_headName,
      const char *_dataName,
      bool _writeElements,
      const void * _constElementData,
      bool _append)
  {
  if(_headName != NULL)
    {
    FileName(_headName);
    }

  bool userDataFileName = true;
  if(_dataName == NULL && strlen(m_ElementDataFileName) == 0)
    {
    userDataFileName = false;
    int sPtr = 0;
    MET_GetFileSuffixPtr(m_FileName, &sPtr);
    if(!strcmp(&m_FileName[sPtr], "mha"))
      {
      ElementDataFileName("LOCAL");
      }
    else
      {
      if(!_append)
        {
        MET_SetFileSuffix(m_FileName, "mhd");
        }
      strcpy(m_ElementDataFileName, m_FileName);
      if(m_CompressedData)
        {
        MET_SetFileSuffix(m_ElementDataFileName, "zraw");
        }
      else
        {
        MET_SetFileSuffix(m_ElementDataFileName, "raw");
        }
      }
    }
  else if(_dataName != NULL)
    {
    userDataFileName = false;
    ElementDataFileName(_dataName);
    }

  // make sure suffix is valid
  if(!_append)
    {
    if(!strcmp(m_ElementDataFileName, "LOCAL"))
      {
      MET_SetFileSuffix(m_FileName, "mha");
      }
     else
      {
      MET_SetFileSuffix(m_FileName, "mhd");
      }
    }

  char pathName[MAXPATHLENGHT];
  bool usePath = MET_GetFilePath(m_FileName, pathName);
  if(usePath)
    {
    char elementPathName[MAXPATHLENGHT];
    MET_GetFilePath(m_ElementDataFileName, elementPathName);
    if(!strcmp(pathName, elementPathName))
      {
      strcpy(elementPathName, &m_ElementDataFileName[strlen(pathName)]);
      strcpy(m_ElementDataFileName, elementPathName);
      }
    }

  METAIO_STREAM::ofstream * tmpWriteStream = new METAIO_STREAM::ofstream;

  openWriteStream(*tmpWriteStream, m_FileName, _append);

  if(!tmpWriteStream->is_open())
    {
    if(!userDataFileName)
      {
      ElementDataFileName("");
      }

    delete tmpWriteStream;

    return false;
    }

  bool result = MetaImage::WriteStream(tmpWriteStream,
                                       _writeElements,
                                       _constElementData);

  if(!userDataFileName)
    {
    ElementDataFileName("");
    }

  tmpWriteStream->close();
  delete tmpWriteStream;

  return result;
  }

bool MetaImage::
WriteStream(METAIO_STREAM::ofstream * _stream,
            bool _writeElements,
            const void * _constElementData)
  {
  if(m_WriteStream != NULL)
    {
    METAIO_STREAM::cerr << "MetaArray: WriteStream: two files open?"
                        << METAIO_STREAM::endl;
    delete m_WriteStream;
    }

  m_WriteStream = _stream;

  unsigned char * compressedElementData = NULL;
  if(m_BinaryData && m_CompressedData && !strstr(m_ElementDataFileName, "%"))
    // compressed & !slice/file
    {
    int elementSize;
    MET_SizeOfType(m_ElementType, &elementSize);
    int elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

    if(_constElementData == NULL)
      {
      compressedElementData = MET_PerformCompression(
                                  (const unsigned char *)m_ElementData,
                                  m_Quantity * elementNumberOfBytes,
                                  & m_CompressedDataSize );
      }
    else
      {
      compressedElementData = MET_PerformCompression(
                                  (const unsigned char *)_constElementData,
                                  m_Quantity * elementNumberOfBytes,
                                  & m_CompressedDataSize );
      }
    }

  M_SetupWriteFields();

  M_Write();

  if(_writeElements)
    {
    if(m_BinaryData && m_CompressedData && !strstr(m_ElementDataFileName, "%"))
      // compressed & !slice/file
      {
      M_WriteElements(m_WriteStream,
                      compressedElementData,
                      m_CompressedDataSize);

      delete [] compressedElementData;
      m_CompressedDataSize = 0;
      }
    else
      {
      if(_constElementData == NULL)
        {
        M_WriteElements(m_WriteStream,
                        m_ElementData,
                        m_Quantity);
        }
      else
        {
        M_WriteElements(m_WriteStream,
                        _constElementData,
                        m_Quantity);
        }
      }
    }

  m_WriteStream = NULL;

  return true;
  }


/** Write a portion of an image */
bool MetaImage::WriteROI( int * _indexMin, int * _indexMax,
                          const char *_headName,
                          const char *_dataName,
                          bool _writeElements,
                          const void * _constElementData,
                          bool _append
                          )
{
  if( _headName != NULL )
    {
    FileName( _headName );
    }

  if( !_writeElements )
    {
    return false;
    }

  // Check if the file exists
  if( M_FileExists(_headName) )
    {
    char* elementData = const_cast<char*>(
                            static_cast<const char*>(_constElementData) );
    if( elementData == NULL )
      {
      elementData = (char*)m_ElementData;
      }
    if( elementData == NULL )
      {
      METAIO_STREAM::cerr << "Element data is NULL" << METAIO_STREAM::endl;
      return false;
      }

    // Find the start of the data
    METAIO_STREAM::ifstream * readStream = new METAIO_STREAM::ifstream;
    readStream->open( m_FileName, METAIO_STREAM::ios::binary |
                                  METAIO_STREAM::ios::in);

    // File must be readable
    if( !MetaObject::ReadStream( m_NDims, readStream ) )
      {
      METAIO_STREAM::cerr << "MetaImage: Read: Cannot parse file"
                          << METAIO_STREAM::endl;
      delete readStream;
      return false;
      }

    // File must not be compressed
    if(m_CompressedData)
      {
      METAIO_STREAM::cerr
               << "MetaImage cannot insert ROI into a compressed file."
               << METAIO_STREAM::endl;
      readStream->close();
      delete readStream;
      return false;
      }

    InitializeEssential( m_NDims,
                         m_DimSize,
                         m_ElementSpacing,
                         m_ElementType,
                         m_ElementNumberOfChannels,
                         NULL, false ); // no memory allocation

    METAIO_STL::string  filename = ElementDataFileName();
    METAIO_STL::streampos dataPos = 0;

    // local file
    if( filename == "LOCAL" )
      {
      filename = m_FileName;
      dataPos = readStream->tellg();
      }
    else if( filename == "LIST"
             || strstr(filename.c_str(), "%") )
      {
      METAIO_STREAM::cerr
               << "MetaImage cannot insert ROI into a list of files."
               << METAIO_STREAM::endl;
      readStream->close();
      delete readStream;
      return false;
      }

    readStream->close();
    delete readStream;

    // Write the region
    if( !M_FileExists(filename.c_str()) )
      {
      char pathName[MAXPATHLENGHT];
      MET_GetFilePath(_headName, pathName);
      filename = pathName+filename;
      }

    METAIO_STREAM::ofstream * tmpWriteStream = new METAIO_STREAM::ofstream;
    tmpWriteStream->open( filename.c_str(),
                          METAIO_STREAM::ios::binary |
                          METAIO_STREAM::ios::in |
                          METAIO_STREAM::ios::out );

    if( !tmpWriteStream->is_open() )
      {
      METAIO_STREAM::cerr << "Cannot open ROI file: "
                          << filename.c_str()
                          << METAIO_STREAM::endl;
      delete tmpWriteStream;
      return false;
      }

    int elementSize;
    MET_SizeOfType( m_ElementType, &elementSize );
    int elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

    // seek to the end and write one byte to allocate the entire file size
    METAIO_STL::streamoff seekoff = m_Quantity*elementNumberOfBytes;
    tmpWriteStream->seekp(0, METAIO_STREAM::ios::end);
    if (tmpWriteStream->tellp() != (dataPos+seekoff))
      {
      seekoff = seekoff - 1;
      tmpWriteStream->seekp(dataPos+seekoff, METAIO_STREAM::ios::beg);
      const char zerobyte = 0;
      tmpWriteStream->write(&zerobyte, 1);
      }

    if( !elementData )
      {
      METAIO_STREAM::cerr << "Element data is NULL" << METAIO_STREAM::endl;
      delete tmpWriteStream;
      return false;
      }

    M_WriteElementsROI(tmpWriteStream, elementData, dataPos,
                       _indexMin, _indexMax);

    tmpWriteStream->close();
    delete tmpWriteStream;
    }
  else // the file doesn't exist
    {
    if(m_CompressedData)
      {
      METAIO_STREAM::cerr
               << "MetaImage cannot write an ROI using compression."
               << METAIO_STREAM::endl;
      return false;
      }

    // Get the data filename right...
    bool userDataFileName = true;
    if( _dataName == NULL && strlen(m_ElementDataFileName) == 0 )
      {
      userDataFileName = false;
      int sPtr = 0;
      MET_GetFileSuffixPtr(m_FileName, &sPtr);
      if( !strcmp(&m_FileName[sPtr], "mha") )
        {
        ElementDataFileName( "LOCAL" );
        }
      else
        {
        if(!_append)
          {
          MET_SetFileSuffix(m_FileName, "mhd");
          }
        strcpy(m_ElementDataFileName, m_FileName);
        if(m_CompressedData)
          {
          MET_SetFileSuffix(m_ElementDataFileName, "zraw");
          }
        else
          {
          MET_SetFileSuffix(m_ElementDataFileName, "raw");
          }
        }
      }
    else if(_dataName != NULL)
      {
      userDataFileName = false;
      ElementDataFileName(_dataName);
      }

    if( !strcmp(m_ElementDataFileName, "LIST")
        || strstr(m_ElementDataFileName, "%") )
      {
      METAIO_STREAM::cerr
               << "MetaImage cannot insert ROI into a list of files."
               << METAIO_STREAM::endl;
      return false;
      }

    // make sure the header suffix is valid, unless forcing to match an
    // existing file via the append bool argument.
    if(!_append)
      {
      if(!strcmp(m_ElementDataFileName, "LOCAL"))
        {
        MET_SetFileSuffix(m_FileName, "mha");
        }
       else
        {
        MET_SetFileSuffix(m_FileName, "mhd");
        }
      }

    char pathName[MAXPATHLENGHT];
    bool usePath = MET_GetFilePath(m_FileName, pathName);
    if(usePath)
      {
      char elementPathName[MAXPATHLENGHT];
      MET_GetFilePath(m_ElementDataFileName, elementPathName);
      if(!strcmp(pathName, elementPathName))
        {
        strcpy(elementPathName, &m_ElementDataFileName[strlen(pathName)]);
        strcpy(m_ElementDataFileName, elementPathName);
        }
      }

    METAIO_STREAM::ofstream * tmpWriteStream = new METAIO_STREAM::ofstream;

    openWriteStream(*tmpWriteStream, m_FileName, _append);

    if(!tmpWriteStream->is_open())
      {
      if(!userDataFileName)
        {
        ElementDataFileName("");
        }
      delete tmpWriteStream;
      return false;
      }

    // Write the ROI header file
    char* elementData = const_cast<char*>(
                          static_cast<const char*>(_constElementData) );
    if(elementData == NULL)
      {
      elementData = (char*)m_ElementData;
      }

    m_WriteStream = tmpWriteStream;
    M_SetupWriteFields();
    M_Write();

    METAIO_STL::streampos dataPos = m_WriteStream->tellp();

    // If data is in a separate file, set dataPos and point to that file.
    //   ( we've already verified the name isn't LIST and doesn't
    //     contain % )
    if( strcmp( m_ElementDataFileName, "LOCAL" ) )
      {
      m_WriteStream = NULL;
      tmpWriteStream->close();

      dataPos = 0;

      char dataFileName[MAXPATHLENGHT];
      if(usePath&& !FileIsFullPath(m_ElementDataFileName))
        {
        sprintf(dataFileName, "%s%s", pathName, m_ElementDataFileName);
        }
      else
        {
        strcpy(dataFileName, m_ElementDataFileName);
        }

      openWriteStream(*tmpWriteStream, dataFileName, _append);
      m_WriteStream = tmpWriteStream;
      }

    int elementSize;
    MET_SizeOfType( m_ElementType, &elementSize );
    int elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

    // write the last byte in the file to allocate it
    METAIO_STL::streamoff seekoff = m_Quantity * elementNumberOfBytes;
    seekoff -= 1;
    m_WriteStream->seekp(seekoff, METAIO_STREAM::ios::cur);
    const char zerobyte = 0;
    m_WriteStream->write(&zerobyte, 1);

    M_WriteElementsROI(m_WriteStream, elementData, dataPos,
                       _indexMin, _indexMax);

    m_WriteStream = NULL;

    if(!userDataFileName)
      {
      ElementDataFileName("");
      }

    tmpWriteStream->close();
    delete tmpWriteStream;
    }

  return true;
}

bool  MetaImage::
M_WriteElementsROI(METAIO_STREAM::ofstream * _fstream,
                   const void * _data,
                   METAIO_STL::streampos _dataPos,
                   int * _indexMin,
                   int* _indexMax )
{
  const char* data = static_cast<const char*>(_data);

  int elementSize;
  MET_SizeOfType(m_ElementType, &elementSize);
  const int elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

  // Write the IO region line by line
  int * currentIndex = new int[m_NDims];
  for(int i=0; i<m_NDims; i++)
    {
    currentIndex[i] = _indexMin[i];
    }

  // Optimize the size of the buffer to written depending on the
  // region shape
  // This calculate the number of continuous bytes in the file
  // which can be written
  METAIO_STL::streamoff elementsToWrite = 1;
  int movingDirection = 0;
  do
    {
    elementsToWrite *= _indexMax[movingDirection] - _indexMin[movingDirection] + 1;
    ++movingDirection;
    }
  while(movingDirection < m_NDims
        && _indexMin[movingDirection-1] == 0
        && _indexMax[movingDirection-1] == m_DimSize[movingDirection-1]-1);

  // write line by line
  bool done = false;
  while(!done)
    {
    // Seek to the right position
    METAIO_STL::streamoff seekoff = _dataPos;
    for(int i=0; i<m_NDims; i++)
      {
      seekoff += m_SubQuantity[i] * currentIndex[i] * elementNumberOfBytes;
      }
    _fstream->seekp( seekoff, METAIO_STREAM::ios::beg );

    M_WriteElementData( _fstream, data, elementsToWrite );
    data += elementsToWrite * elementNumberOfBytes;

    // check if there is only one write needed
    if( movingDirection >= m_NDims )
      {
      break;
      }

    ++currentIndex[movingDirection];

    // Check if we are still in the region
    for( int j=movingDirection; j<m_NDims; j++ )
      {
      if( currentIndex[j] > _indexMax[j] )
        {
        if( j == m_NDims-1 )
          {
          done = true;
          break;
          }
        else
          {
          currentIndex[j] = _indexMin[j];
          currentIndex[j+1]++;
          }
        }
      }
    } // end writing  loop

  delete [] currentIndex;

  return true;
}

bool MetaImage::
Append(const char *_headName)
  {
  if(META_DEBUG)
   {
   METAIO_STREAM::cout << "MetaImage: Append" << METAIO_STREAM::endl;
   }

  return this->Write(_headName, NULL, true, NULL, true);
  }

void MetaImage::
M_Destroy(void)
  {
  if(m_AutoFreeElementData && m_ElementData != NULL)
    {
    delete [] (char *)m_ElementData;
    }

  m_ElementData = NULL;

  if(m_CompressionTable && m_CompressionTable->compressedStream)
    {
    inflateEnd(m_CompressionTable->compressedStream);
    delete m_CompressionTable->compressedStream;
    delete [] m_CompressionTable->buffer;
    m_CompressionTable->buffer = NULL;
    }
  delete m_CompressionTable;
  m_CompressionTable = NULL;

  MetaObject::M_Destroy();
  }

void MetaImage::
M_SetupReadFields(void)
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_SetupReadFields"
                        << METAIO_STREAM::endl;
    }

  MetaObject::M_SetupReadFields();

  MET_FieldRecordType * mF;

  int nDimsRecNum = MET_GetFieldRecordNumber("NDims", &m_Fields);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "DimSize", MET_INT_ARRAY, true, nDimsRecNum);
  mF->required = true;
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "HeaderSize", MET_INT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "Modality", MET_STRING, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ImagePosition", MET_FLOAT_ARRAY, false, nDimsRecNum);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "SequenceID", MET_INT_ARRAY, false, nDimsRecNum);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementMin", MET_FLOAT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementMax", MET_FLOAT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementNumberOfChannels", MET_INT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementSize", MET_FLOAT_ARRAY, false, nDimsRecNum);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;  // Set but not used...
  MET_InitReadField(mF, "ElementNBits", MET_INT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;  // Used by ConvertElementToIntensity funcs
  MET_InitReadField(mF, "ElementToIntensityFunctionSlope", MET_FLOAT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;  // Used by ConvertElementToIntensity funcs
  MET_InitReadField(mF, "ElementToIntensityFunctionOffset", MET_FLOAT, false);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementType", MET_STRING, true);
  mF->required = true;
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "ElementDataFile", MET_STRING, true);
  mF->required = true;
  mF->terminateRead = true;
  m_Fields.push_back(mF);
  }

void MetaImage::
M_SetupWriteFields(void)
  {
  strcpy(m_ObjectTypeName,"Image");
  MetaObject::M_SetupWriteFields();

  MET_FieldRecordType * mF;

  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "DimSize", MET_INT_ARRAY, m_NDims, m_DimSize);
  m_Fields.push_back(mF);

  char s[MAXPATHLENGHT];
  if(m_HeaderSize > 0 || m_HeaderSize == -1)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "HeaderSize", MET_INT);
    m_Fields.push_back(mF);
    }

  int i;
  if(m_Modality != MET_MOD_UNKNOWN)
    {
    mF = new MET_FieldRecordType;
    strcpy(s, MET_ValueTypeName[m_Modality]);
    MET_InitWriteField(mF, "Modality", MET_STRING, strlen(s), s);
    m_Fields.push_back(mF);
    }

  i = MET_GetFieldRecordNumber("AnatomicalOrientation", &m_Fields);
  if(i < 0)
    {
    const char * str = AnatomicalOrientationAcronym();
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "AnatomicalOrientation",
                       MET_STRING, strlen(str), str);
    m_Fields.push_back(mF);
    }

  bool valid = false;
  for(i=0; i<4; i++)
    {
    if(m_SequenceID[i] != 0)
      {
      valid = true;
      break;
      }
    }
  if(valid)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "SequenceID", MET_FLOAT_ARRAY, m_NDims,
                       m_SequenceID);
    m_Fields.push_back(mF);
    }

  if(m_ElementMinMaxValid)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementMin", MET_FLOAT, m_ElementMin);
    m_Fields.push_back(mF);

    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementMax", MET_FLOAT, m_ElementMax);
    m_Fields.push_back(mF);
    }

  if(m_ElementNumberOfChannels>1)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementNumberOfChannels", MET_INT,
                       m_ElementNumberOfChannels);
    m_Fields.push_back(mF);
    }

  if(m_ElementSizeValid)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementSize", MET_FLOAT_ARRAY, m_NDims,
                       m_ElementSize);
    m_Fields.push_back(mF);
    }

  if(m_ElementToIntensityFunctionSlope != 1 ||
     m_ElementToIntensityFunctionOffset != 0)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementToIntensityFunctionSlope",
                       MET_FLOAT, m_ElementToIntensityFunctionSlope);
    m_Fields.push_back(mF);
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "ElementToIntensityFunctionOffset",
                       MET_FLOAT, m_ElementToIntensityFunctionOffset);
    m_Fields.push_back(mF);
    }

  mF = new MET_FieldRecordType;
  MET_TypeToString(m_ElementType, s);
  MET_InitWriteField(mF, "ElementType", MET_STRING, strlen(s), s);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "ElementDataFile", MET_STRING,
                     strlen(m_ElementDataFileName),
                     m_ElementDataFileName);
  mF->terminateRead = true;
  m_Fields.push_back(mF);
  }

//
//
//
bool MetaImage::
M_Read(void)
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_Read: Loading Header"
                        << METAIO_STREAM::endl;
    }
  if(!MetaObject::M_Read())
    {
    METAIO_STREAM::cerr << "MetaImage: M_Read: Error parsing file"
                        << METAIO_STREAM::endl;
    return false;
    }

  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_Read: Parsing Header"
                        << METAIO_STREAM::endl;
    }
  MET_FieldRecordType * mF;

  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "metaImage: M_Read: elementSpacing[" << 0 << "] = "
                        << m_ElementSpacing[0] << METAIO_STREAM::endl;
    }
  mF = MET_GetFieldRecord("DimSize", &m_Fields);
  if(mF && mF->defined)
    {
    int i;
    for(i=0; i<m_NDims; i++)
      {
      m_DimSize[i] = (int)mF->value[i];
      }
    }

  mF = MET_GetFieldRecord("HeaderSize", &m_Fields);
  if(mF && mF->defined)
    {
    m_HeaderSize = (int)mF->value[0];
    }

  mF = MET_GetFieldRecord("Modality", &m_Fields);
  if(mF && mF->defined)
    {
    MET_StringToImageModality((char *)mF->value, &m_Modality);
    }

  mF = MET_GetFieldRecord("SequenceID", &m_Fields);
  if(mF && mF->defined)
    {
    int i;
    for(i=0; i<m_NDims; i++)
      {
      m_SequenceID[i] = (float)(mF->value[i]);
      }
    }

  mF = MET_GetFieldRecord("ImagePosition", &m_Fields);
  if(mF && mF->defined)
    {
    int i;
    for(i=0; i<m_NDims; i++)
      {
      m_Offset[i] = static_cast<double>(mF->value[i]);
      }
    }

  mF = MET_GetFieldRecord("ElementMin", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementMin = mF->value[0];
    }

  mF = MET_GetFieldRecord("ElementMax", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementMax = mF->value[0];
    }

  mF = MET_GetFieldRecord("ElementNumberOfChannels", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementNumberOfChannels = (int)mF->value[0];
    }


  mF = MET_GetFieldRecord("ElementSize", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementSizeValid = true;
    int i;
    for(i=0; i<m_NDims; i++)
      {
      m_ElementSize[i] = mF->value[i];
      }
    mF = MET_GetFieldRecord("ElementSpacing", &m_Fields);
    if(mF && !mF->defined)
      {
      for(i=0; i<m_NDims; i++)
        {
        m_ElementSpacing[i] = m_ElementSize[i];
        }
      }
    }
  else
    {
    int i;
    m_ElementSizeValid = false;
    for(i=0; i<m_NDims; i++)
      {
      m_ElementSize[i] = m_ElementSpacing[i];
      }
    }

  m_ElementToIntensityFunctionSlope = 1;
  m_ElementToIntensityFunctionOffset = 0;
  mF = MET_GetFieldRecord("ElementToIntensityFunctionSlope", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementToIntensityFunctionSlope = mF->value[0];
    }
  mF = MET_GetFieldRecord("ElementToIntensityFunctionOffset", &m_Fields);
  if(mF && mF->defined)
    {
    m_ElementToIntensityFunctionOffset = mF->value[0];
    }

  mF = MET_GetFieldRecord("ElementType", &m_Fields);
  if(mF && mF->defined)
    {
    MET_StringToType((char *)(mF->value), &m_ElementType);
    }

  mF = MET_GetFieldRecord("ElementDataFile", &m_Fields);
  if(mF && mF->defined)
    {
    strcpy(m_ElementDataFileName, (char *)(mF->value));
    }

  return true;
  }

//
//
//
bool MetaImage::
M_ReadElements(METAIO_STREAM::ifstream * _fstream, void * _data,
               METAIO_STL::streamoff _dataQuantity)
  {
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_ReadElements" << METAIO_STREAM::endl;
    }

  if(m_HeaderSize>(int)0)
    {
    _fstream->seekg(m_HeaderSize, METAIO_STREAM::ios::beg);
    if(!_fstream->good())
      {
      METAIO_STREAM::cerr << "MetaImage: Read: header not read correctly"
                          << METAIO_STREAM::endl;
      return false;
      }
    }

  int elementSize;
  MET_SizeOfType(m_ElementType, &elementSize);
  METAIO_STL::streamoff readSize = _dataQuantity*m_ElementNumberOfChannels*elementSize;
  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_ReadElements: ReadSize = "
                        << readSize << METAIO_STREAM::endl;
    }

  if(m_HeaderSize == -1)
    {
    if(META_DEBUG)
      {
      METAIO_STREAM::cout << "MetaImage: M_ReadElements: Skipping header"
                          << METAIO_STREAM::endl;
      }
    _fstream->seekg(-readSize, METAIO_STREAM::ios::end);
    }

  // If compressed we inflate
  if(m_BinaryData && m_CompressedData)
    {
    // if m_CompressedDataSize is not defined we assume the size of the
    // file is the size of the compressed data
    bool compressedDataDeterminedFromFile = false;
    if(m_CompressedDataSize==0)
      {
      compressedDataDeterminedFromFile = true;
      _fstream->seekg(0, METAIO_STREAM::ios::end);
      m_CompressedDataSize = _fstream->tellg();
      _fstream->seekg(0, METAIO_STREAM::ios::beg);
      }

    unsigned char* compr = new unsigned char[static_cast<size_t>(m_CompressedDataSize)];

    M_ReadElementData( _fstream, compr, m_CompressedDataSize );

    MET_PerformUncompression(compr, m_CompressedDataSize,
                             (unsigned char *)_data, readSize);

    if (compressedDataDeterminedFromFile)
      {
      m_CompressedDataSize = 0;
      }

    delete [] compr;
    }
  else // if not compressed
    {
    if(!m_BinaryData)
      {

      M_ReadElementData( _fstream, _data, _dataQuantity );

      }
    else
      {

      if ( !M_ReadElementData( _fstream, _data, _dataQuantity ) )
        return false;

      }
    }

  return true;
  }

bool MetaImage::
M_WriteElements(METAIO_STREAM::ofstream * _fstream,
                const void * _data,
                METAIO_STL::streamoff _dataQuantity)
  {

  if(!strcmp(m_ElementDataFileName, "LOCAL"))
    {
    MetaImage::M_WriteElementData(_fstream, _data, _dataQuantity);
    }
  else // write the data in a separate file
    {
    char dataFileName[MAXPATHLENGHT];
    char pathName[MAXPATHLENGHT];
    bool usePath = MET_GetFilePath(m_FileName, pathName);
    if(usePath&& !FileIsFullPath(m_ElementDataFileName))
      {
      sprintf(dataFileName, "%s%s", pathName, m_ElementDataFileName);
      }
    else
      {
      strcpy(dataFileName, m_ElementDataFileName);
      }

    if(strstr(dataFileName, "%")) // write slice by slice
      {
      int i;
      char fName[MAXPATHLENGHT];
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      METAIO_STL::streamoff elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;
      METAIO_STL::streamoff sliceNumberOfBytes = m_SubQuantity[m_NDims-1]*elementNumberOfBytes;

      METAIO_STREAM::ofstream* writeStreamTemp = new METAIO_STREAM::ofstream;
      for(i=1; i<=m_DimSize[m_NDims-1]; i++)
        {
        sprintf(fName, dataFileName, i);

        openWriteStream(*writeStreamTemp, fName, false);

        if(!m_CompressedData)
          {
          // BUG? This looks wrong to me as the third parameter should
          // contain the number of elements/quantity, not number of bytes -BCL
          MetaImage::M_WriteElementData(writeStreamTemp,
                             &(((const char *)_data)[(i-1)*sliceNumberOfBytes]),
                             sliceNumberOfBytes);
          }
        else
          {
          unsigned char * compressedData = NULL;
          METAIO_STL::streamoff compressedDataSize = 0;

          // Compress the data slice by slice
          compressedData = MET_PerformCompression(
                  &(((const unsigned char *)_data)[(i-1)*sliceNumberOfBytes]),
                  sliceNumberOfBytes,
                  & compressedDataSize );

          // Write the compressed data
          MetaImage::M_WriteElementData( writeStreamTemp,
                              compressedData,
                              compressedDataSize );

          delete [] compressedData;
          }

        writeStreamTemp->close();
        }

      delete writeStreamTemp;
      }
    else // write the image in one unique other file
      {
      METAIO_STREAM::ofstream* writeStreamTemp = new METAIO_STREAM::ofstream;
      openWriteStream(*writeStreamTemp, dataFileName, false);

      MetaImage::M_WriteElementData(writeStreamTemp, _data, _dataQuantity);

      writeStreamTemp->close();
      delete writeStreamTemp;
      }
    }

  return true;
  }


bool MetaImage::
M_WriteElementData(METAIO_STREAM::ofstream * _fstream,
                   const void * _data,
                   METAIO_STL::streamoff _dataQuantity)
  {
  if(!m_BinaryData)
    {

    double tf;
    for(METAIO_STL::streamoff i=0; i<_dataQuantity; i++)
      {
      MET_ValueToDouble(m_ElementType, _data, i, &tf);
      if((i+1)/10 == (double)(i+1.0)/10.0)
        {
        (*_fstream) << tf << METAIO_STREAM::endl;
        }
      else
        {
        (*_fstream) << tf << " ";
        }
      }
    }
  else
    {
    if(m_CompressedData)
      {
      // the data is writen in writes no bigger then MaxIOChunk
      METAIO_STL::streamoff bytesRemaining = _dataQuantity;
      while ( bytesRemaining )
        {
        METAIO_STL::streamoff chunkToWrite = bytesRemaining > MaxIOChunk ? MaxIOChunk : bytesRemaining;
        _fstream->write( (const char *)_data, (size_t)chunkToWrite );
        _data = (const char *)(_data) + chunkToWrite; // <- Note: data is changed
        bytesRemaining -= chunkToWrite;
        }
      }
    else
      {
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      METAIO_STL::streamoff elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

      // the data is writen in writes no bigger then MaxIOChunk
      METAIO_STL::streamoff bytesRemaining = _dataQuantity * elementNumberOfBytes;
      while ( bytesRemaining )
        {
        METAIO_STL::streamoff chunkToWrite = bytesRemaining > MaxIOChunk ? MaxIOChunk : bytesRemaining;
        _fstream->write( (const char *)_data, (size_t)chunkToWrite );
        _data = (const char *)(_data) + chunkToWrite; // <- Note: _data is changed
        bytesRemaining -= chunkToWrite;
        }
      }
    }

  // check the the io stream did not fail in the process of writing
  if ( _fstream->fail() )
    {
    METAIO_STREAM::cerr
      << "MetaImage: M_WriteElementsData: file stream is fail after write"
      << METAIO_STREAM::endl;
    return false;
    }

  return true;
  }

/** Streaming related functions */
bool MetaImage::
ReadROI(int * _indexMin, int * _indexMax,
        const char *_headerName,
        bool _readElements,
        void * _buffer,
        unsigned int subSamplingFactor)
{
  M_Destroy();

  Clear();

  M_SetupReadFields();

  if(_headerName != NULL)
    {
    strcpy(m_FileName, _headerName);
    }

  M_PrepareNewReadStream();

  METAIO_STREAM::ifstream * tmpReadStream = new METAIO_STREAM::ifstream;

  openReadStream(*tmpReadStream, m_FileName);

  if(!tmpReadStream->is_open())
    {
    delete tmpReadStream;
    return false;
    }

  if( !this->ReadROIStream(_indexMin, _indexMax,
                           0, tmpReadStream, _readElements, _buffer,subSamplingFactor) )
    {
    tmpReadStream->close();
    delete tmpReadStream;
    return false;
    }

  tmpReadStream->close();

  delete tmpReadStream;

  return true;
}

/** Read the ROI Stream */
bool MetaImage::ReadROIStream(int * _indexMin, int * _indexMax,
                              int _nDims,
                              METAIO_STREAM::ifstream * _stream,
                              bool _readElements,
                              void * _buffer,
                              unsigned int subSamplingFactor)
{
  if(!MetaObject::ReadStream(_nDims, _stream))
    {
    METAIO_STREAM::cerr << "MetaImage: Read: Cannot parse file"
                        << METAIO_STREAM::endl;
    return false;
    }

  if(_readElements)
    {
    if(_buffer == NULL)
      {
      InitializeEssential(m_NDims,
                          m_DimSize,
                          m_ElementSpacing,
                          m_ElementType,
                          m_ElementNumberOfChannels,
                          NULL, true);
      }
    else
      {
      InitializeEssential(m_NDims,
                          m_DimSize,
                          m_ElementSpacing,
                          m_ElementType,
                          m_ElementNumberOfChannels,
                          _buffer, false);
      }

    // Streaming related. We need to update some of the fields
    METAIO_STL::streamoff quantity = 1;
    int i;
    size_t j;
    for(i=0; i<m_NDims; i++)
      {
      quantity *= (_indexMax[i] - _indexMin[i] + 1);
      }

    bool usePath;
    char pathName[MAXPATHLENGHT];
    char fName[MAXPATHLENGHT];
    usePath = MET_GetFilePath(m_FileName, pathName);

    if(!strcmp("Local", m_ElementDataFileName) ||
       !strcmp("LOCAL", m_ElementDataFileName) ||
       !strcmp("local", m_ElementDataFileName))
      {
      M_ReadElementsROI(_stream, m_ElementData, quantity,
                        _indexMin, _indexMax, subSamplingFactor,
                        m_Quantity);
      }
    else if(!strncmp("LIST", m_ElementDataFileName,4))
      {
      int fileImageDim = m_NDims - 1;
      int nWrds;
      char **wrds;
      MET_StringToWordArray(m_ElementDataFileName, &nWrds, &wrds);
      if(nWrds > 1)
        {
        fileImageDim = (int)atof(wrds[1]);
        }
      for(i=0; i<nWrds; i++)
        {
        delete [] wrds[i];
        }
      delete [] wrds;
      if ( (fileImageDim == 0) || (fileImageDim > m_NDims) )
        {
        // if optional file dimension size is not given or is larger than
        // overall dimension then default to a size of m_NDims - 1.
        fileImageDim = m_NDims-1;
        }
      char s[1024];
      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      elementSize *= m_ElementNumberOfChannels;

      int minV = _indexMin[m_NDims-1];
      int maxV = minV + (_indexMax[m_NDims-1]-_indexMin[m_NDims-1]);

      int cnt=0;

      // Read the previous lines
      for(i=0;i<minV;i++)
        {
        _stream->getline(s, 1024);
        }

      for(i=minV; i<=maxV; i+=1)
        {
        _stream->getline(s, 1024);
        if(!_stream->eof())
          {
          j = strlen(s)-1;
          while(j>0 && (isspace(s[j]) || !isprint(s[j])))
            {
            s[j--] = '\0';
            }
          if(usePath && !FileIsFullPath(s))
            {
            sprintf(fName, "%s%s", pathName, s);
            }
          else
            {
            strcpy(fName, s);
            }

          openReadStream(*readStreamTemp, fName);
          if(!readStreamTemp->is_open())
            {
            METAIO_STREAM::cerr << "MetaImage: Read: cannot open slice"
                                << METAIO_STREAM::endl;
            continue;
            }

          // read only one slice
          int * indexMin = new int[m_NDims];
          int * indexMax = new int[m_NDims];
          quantity = 1;
          for(int k = 0;k<m_NDims-1;k++)
            {
            quantity *= _indexMax[k]-_indexMin[k]+1;
            indexMin[k]= _indexMin[k];
            indexMax[k]= _indexMax[k];
            }
          indexMin[m_NDims-1]=0;
          indexMax[m_NDims-1]=0;

          M_ReadElementsROI(readStreamTemp,
                             &(((char *)m_ElementData)[cnt*quantity*
                                                     elementSize]),
                             quantity, indexMin, indexMax,
                             subSamplingFactor,
                             m_SubQuantity[m_NDims-1]);

          cnt++;
          readStreamTemp->close();
          }
        }
      delete readStreamTemp;
      }
    else if(strstr(m_ElementDataFileName, "%"))
      {
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      elementSize *= m_ElementNumberOfChannels;

      int nWrds;
      char **wrds;
      int minV = 1;
      int maxV = m_DimSize[m_NDims-1];
      int stepV = 1;
      char s[MAXPATHLENGHT];
      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;
      MET_StringToWordArray(m_ElementDataFileName, &nWrds, &wrds);
      if(nWrds >= 2)
        {
        minV = (int)atof(wrds[1]);
        maxV = minV + m_DimSize[m_NDims-1] - 1;
        }
      if(nWrds >= 3)
        {
        maxV = (int)atof(wrds[2]);
        stepV = (maxV-minV)/(m_DimSize[m_NDims-1]);
        }
      if(nWrds >= 4)
        {
        stepV = (int)atof(wrds[3]);
        }
      if(nWrds >= 5 )
      {
        // In this case, the filename must have had spaces in the
        // name.  The filename was parsed into multiple pieces by the
        // MET_StringToWordArray, which parses based on spaces.
        // Thus, we need to reconstruct the filename in this case.
        // The last three wrds must be numbers.  If they are not, we give an error.
        for( i = nWrds-3; i < nWrds; i++ )
        {
          for( j = 0; j < strlen(wrds[i]); j++ )
          {
            if( !isdigit(wrds[i][j]) )
            {
              METAIO_STREAM::cerr << "MetaImage: Read: Last three arguments must be numbers!"
                  << METAIO_STREAM::endl;
              continue;
            }
          }
        }
        stepV = (int)atof(wrds[nWrds-1]);
        maxV =  (int)atof(wrds[nWrds-2]);
        minV =  (int)atof(wrds[nWrds-3]);
        for( i = 1; i < nWrds-3; i++ )
        {
          strcat(wrds[0]," ");
          strcat(wrds[0],wrds[i]);
        }
      }
      // If the specified size of the third dimension is less than the size
      // specified by the regular expression, we should only read a volume with the specified
      // size.  Otherwise, the code will crash when trying to fill m_ElementData more than it can hold.
      // Therefore, we modify maxV to ensure that the images spanned by minV:stepV:maxV are less than or equal
      // to the size in the last dimension.
      int numberOfImages = 1 + (maxV - minV)/stepV;
      if( numberOfImages > m_DimSize[m_NDims-1] )
      {
        maxV = (m_DimSize[m_NDims-1]-1)*stepV + minV;
      }

      int cnt = 0;

      // Uses the _indexMin and _indexMax
      minV += _indexMin[m_NDims-1];
      maxV = minV + (_indexMax[m_NDims-1]-_indexMin[m_NDims-1])*stepV;

      for(i=minV; i<=maxV; i += stepV)
        {
        sprintf(s, wrds[0], i);
        if(usePath && !FileIsFullPath(s))
          {
          sprintf(fName, "%s%s", pathName, s);
          }
        else
          {
          strcpy(fName, s);
          }


        openReadStream(*readStreamTemp, fName);
        if(!readStreamTemp->is_open())
          {
          METAIO_STREAM::cerr << "MetaImage: Read: cannot construct file"
                              << METAIO_STREAM::endl;
          continue;
          }

        // read only one slice
        int * indexMin = new int[m_NDims];
        int * indexMax = new int[m_NDims];
        quantity = 1;
        for(int k = 0;k<m_NDims-1;k++)
          {
          quantity *= _indexMax[k]-_indexMin[k]+1;
          indexMin[k]= _indexMin[k];
          indexMax[k]= _indexMax[k];
          }
        indexMin[m_NDims-1]=0;
        indexMax[m_NDims-1]=0;

        M_ReadElementsROI(readStreamTemp,
                       &(((char *)m_ElementData)[cnt*quantity*
                                                 elementSize]),
                       quantity, indexMin, indexMax,
                       subSamplingFactor,
                       m_SubQuantity[m_NDims-1]);

        cnt++;

        delete [] indexMin;
        delete [] indexMax;

        readStreamTemp->close();
        }

      for(i=0; i<nWrds; i++)
        {
        delete [] wrds[i];
        }
      delete [] wrds;

      delete readStreamTemp;
      }
    else
      {
      if(usePath && !FileIsFullPath(m_ElementDataFileName))
        {
        sprintf(fName, "%s%s", pathName, m_ElementDataFileName);
        }
      else
        {
        strcpy(fName, m_ElementDataFileName);
        }

      METAIO_STREAM::ifstream* readStreamTemp = new METAIO_STREAM::ifstream;

      const char *extensions[] = { "", ".gz", ".Z", 0 };
      for(unsigned ii = 0; extensions[ii] != 0; ii++)
        {
        METAIO_STL::string tempFName(fName);
        tempFName += extensions[ii];
        openReadStream(*readStreamTemp,tempFName.c_str());
        if(readStreamTemp->is_open())
          {
          if(ii > 0)
            {
            this->CompressedData(true);
            this->BinaryData(true);
            }
          break;
          }
        }

      if(!readStreamTemp->is_open())
        {
        METAIO_STREAM::cerr << "MetaImage: ReadROI: Cannot open data file"
                            << METAIO_STREAM::endl;
        if(m_ReadStream)
          {
          m_ReadStream->close();
          }
        delete readStreamTemp;
        return false;
        }

      M_ReadElementsROI(readStreamTemp, m_ElementData, quantity,
                        _indexMin, _indexMax, subSamplingFactor,
                        m_Quantity);

      readStreamTemp->close();
      delete readStreamTemp;
      }
    }
  return true;
}

/** Read an ROI */
bool MetaImage::
M_ReadElementsROI(METAIO_STREAM::ifstream * _fstream, void * _data,
                  METAIO_STL::streamoff _dataQuantity,
                  int* _indexMin, int* _indexMax,unsigned int subSamplingFactor,
                  METAIO_STL::streamoff _totalDataQuantity)
{
  if(_totalDataQuantity ==0)
    {
    _totalDataQuantity = _dataQuantity;
    }

  for(int dim=0;dim<m_NDims;dim++)
    {
    _indexMin[dim] *= subSamplingFactor;
    _indexMax[dim] *= subSamplingFactor;
    }


  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_ReadElementsROI" << METAIO_STREAM::endl;
    }

  if(m_HeaderSize>(int)0)
    {
    _fstream->seekg(m_HeaderSize, METAIO_STREAM::ios::beg);
    if(!_fstream->good())
      {
      METAIO_STREAM::cerr << "MetaImage: M_ReadElementsROI: header not read correctly"
                          << METAIO_STREAM::endl;
      return false;
      }
    }

  int elementSize;
  MET_SizeOfType(m_ElementType, &elementSize);
  METAIO_STL::streamoff readSize = _dataQuantity*m_ElementNumberOfChannels*elementSize;
  int elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

  if(META_DEBUG)
    {
    METAIO_STREAM::cout << "MetaImage: M_ReadElementsROI: ReadSize = "
                        << readSize << METAIO_STREAM::endl;
    }

  if(m_HeaderSize == -1)
    {
    if(META_DEBUG)
      {
      METAIO_STREAM::cout << "MetaImage: M_ReadElementsROI: Skipping header"
                          << METAIO_STREAM::endl;
      }
    METAIO_STL::streamoff headSize = _totalDataQuantity*m_ElementNumberOfChannels*elementSize;
    _fstream->seekg(-headSize, METAIO_STREAM::ios::end);
    }

  METAIO_STL::streampos dataPos = _fstream->tellg();
  METAIO_STL::streamoff i;

  // If compressed we inflate
  if(m_BinaryData && m_CompressedData)
    {
    // if m_CompressedDataSize is not defined we assume the size of the
    // file is the size of the compressed data
    if(m_CompressedDataSize==0)
      {
      _fstream->seekg(0, METAIO_STREAM::ios::end);
      m_CompressedDataSize = _fstream->tellg();
      _fstream->seekg(0, METAIO_STREAM::ios::beg);
      }

      unsigned char* data = static_cast<unsigned char*>(_data);
      // Initialize the index
      int* currentIndex = new int[m_NDims];
      for(i=0;i<m_NDims;i++)
        {
        currentIndex[i] = _indexMin[i];
        }

      // Optimize the size of the buffer to read depending on the
      // region shape
      // This calculate the number of continuous bytes in the file
      // which can be read
      METAIO_STL::streamoff elementsToRead = 1;
      int movingDirection = 0;
      do
        {
        elementsToRead *= _indexMax[movingDirection] - _indexMin[movingDirection] + 1;
        ++movingDirection;
        }
      while(subSamplingFactor == 1
            && movingDirection < m_NDims
            && _indexMin[movingDirection-1] == 0
            && _indexMax[movingDirection-1] == m_DimSize[movingDirection-1]-1);

      METAIO_STL::streamoff bytesToRead = elementsToRead*elementNumberOfBytes;
      METAIO_STL::streamoff gc = 0;

      bool done = false;
      while(!done)
        {
        // Seek to the right position
        METAIO_STL::streamoff seekoff = 0;
        for(i=0; i<m_NDims; i++)
          {
          seekoff += m_SubQuantity[i]*elementNumberOfBytes*currentIndex[i];
          }


        if(subSamplingFactor > 1)
          {
          unsigned char* subdata = new unsigned char[static_cast<size_t>(bytesToRead)];
          METAIO_STL::streamoff rOff =
            MET_UncompressStream(_fstream, seekoff, subdata,
                                 bytesToRead, m_CompressedDataSize,
                                 m_CompressionTable);
          // if there was a read error
          if(rOff == -1)
            {
            delete [] currentIndex;
            return false;
            }

          for(METAIO_STL::streamoff p=0;
              p<bytesToRead;
              p+=(subSamplingFactor*m_ElementNumberOfChannels*elementSize))
            {
            for(int s=0; s<m_ElementNumberOfChannels*elementSize; s++)
              {
              *data = subdata[p+s];
              gc++;
              data++;
              }
            }
          delete [] subdata;
          }
        else
          {
          METAIO_STL::streamoff rOff =
            MET_UncompressStream(_fstream, seekoff, data,
                                 bytesToRead, m_CompressedDataSize,
                                 m_CompressionTable);
          if(rOff == -1)
            {
            delete [] currentIndex;
            return false;
            }
          data += bytesToRead;
          gc += rOff;
          }

        if(gc == readSize)
          {
          break;
          }

        // Go forward
        if(m_NDims == 1)
          {
          break;
          }

        currentIndex[movingDirection] += subSamplingFactor;

        // Check if we are still in the region
        for(i=1;i<m_NDims;i++)
          {
          if(currentIndex[i]>_indexMax[i])
            {
            if(i==m_NDims-1)
              {
              done = true;
              break;
              }
            else
              {
              currentIndex[i] = _indexMin[i];
              currentIndex[i+1] += subSamplingFactor;
              }
            }
          }
        }

      if(gc != readSize)
        {
        METAIO_STREAM::cerr
                  << "MetaImage: M_ReadElementsROI: data not read completely"
                  << METAIO_STREAM::endl;
        METAIO_STREAM::cerr << "   ideal = " << readSize << " : actual = " << gc
                  << METAIO_STREAM::endl;
        delete [] currentIndex;
        return false;
        }

      delete [] currentIndex;
    }
  else // if not compressed
    {
    double tf;
    MET_SizeOfType(m_ElementType, &elementSize);

    char* data = static_cast<char*>(_data);
    // Initialize the index
    int* currentIndex = new int[m_NDims];
    for(i=0;i<m_NDims;i++)
      {
      currentIndex[i] = _indexMin[i];
      }

    // Optimize the size of the buffer to read depending on the
    // region shape
    // This calculate the number of continuous bytes in the file
    // which can be read
    METAIO_STL::streamoff elementsToRead = 1;
    int movingDirection = 0;
    do
      {
      elementsToRead *= _indexMax[movingDirection] - _indexMin[movingDirection] + 1;
      ++movingDirection;
      }
    while(subSamplingFactor == 1
          && movingDirection < m_NDims
          && _indexMin[movingDirection-1] == 0
          && _indexMax[movingDirection-1] == m_DimSize[movingDirection-1]-1);

    //readLine *= m_ElementNumberOfChannels*elementSize;
    METAIO_STL::streamoff gc = 0;

    bool done = false;
    while(!done)
      {
      // Seek to the right position
      METAIO_STL::streamoff seekoff = 0;
      for(i=0;i<m_NDims;i++)
        {
        seekoff += m_SubQuantity[i]*m_ElementNumberOfChannels*elementSize*currentIndex[i];
        }

      _fstream->seekg(dataPos+seekoff, METAIO_STREAM::ios::beg);

      // Read a line
      if(subSamplingFactor > 1)
        {
        if(!m_BinaryData) // Not binary data
          {
          for(i=0; i<elementsToRead; i+=subSamplingFactor)
            {
            *_fstream >> tf;
            MET_DoubleToValue(tf, m_ElementType, _data, i);

            for(unsigned int j=0;j<subSamplingFactor;j++)
              {
              _fstream->get();
              }
            }
          }
        else // Binary data
          {
          char* subdata = new char[static_cast<size_t>(elementsToRead*elementNumberOfBytes)];

          _fstream->read(subdata, size_t(elementsToRead*elementNumberOfBytes));

          for(METAIO_STL::streamoff p=0;
              p<elementsToRead*elementNumberOfBytes;
              p+=(subSamplingFactor*elementNumberOfBytes))
            {
            for(int s=0;s<elementNumberOfBytes;s++)
              {
              *data = subdata[p+s];
              gc++;
              data++;
              }
            }
          delete [] subdata;
          }
        }
      else
        {
        if(!m_BinaryData) // Not binary data
          {
          // anyone using ROI reading of ASCII??
          // does this work? what about incrementing data?
          // what about data sizes and random access of file?
          METAIO_STL::streamoff blockSize = elementsToRead*m_ElementNumberOfChannels*elementSize;
          M_ReadElementData(  _fstream, data, (size_t)blockSize  );
          gc += blockSize;

          }
        else // binary data
          {

          M_ReadElementData(  _fstream, data, elementsToRead );
          gc += elementsToRead*elementNumberOfBytes;
          data += elementsToRead*elementNumberOfBytes;
          }
        }

      // I don't think this check is really needed -BCL
      if(gc == readSize)
        {
        break;
        }

      // check if there is only one read needed
      if ( movingDirection >= m_NDims )
        {
        break;
        }

      // Go forward
      currentIndex[movingDirection] += subSamplingFactor;

      // Check if we are still in the region
      for(i=movingDirection;i<m_NDims;i++)
        {
        if(currentIndex[i]>_indexMax[i])
          {
          if(i==m_NDims-1)
            {
            done = true;
            break;
            }
          else
            {
            currentIndex[i] = _indexMin[i];
            currentIndex[i+1] += subSamplingFactor;
            }
          }
        }
      }

    delete [] currentIndex;

    if(gc != readSize)
      {
      METAIO_STREAM::cerr
                << "MetaImage: M_ReadElementsROI: data not read completely"
                << METAIO_STREAM::endl;
      METAIO_STREAM::cerr << "   ideal = " << readSize << " : actual = " << gc
                << METAIO_STREAM::endl;
      return false;
      }
    }

  return true;
}


bool MetaImage::
M_ReadElementData(METAIO_STREAM::ifstream * _fstream,
                  void * _data,
                  METAIO_STL::streamoff _dataQuantity)
{
  // NOTE: this method is different from WriteElementData
  METAIO_STL::streamoff gc = 0;

  if(!m_BinaryData)
    {
    double tf;

    for(int i=0; i<_dataQuantity; i++)
      {
      *_fstream >> tf;
      MET_DoubleToValue(tf, m_ElementType, _data, i);
      _fstream->get();
      ++gc;
      }
    }
  else
    {
    if(m_CompressedData)
      {

      // the data is read with calls no bigger then MaxIOChunk
      METAIO_STL::streamoff bytesRemaining = _dataQuantity;
      while ( bytesRemaining )
        {
        METAIO_STL::streamoff chunkToRead = bytesRemaining > MaxIOChunk ? MaxIOChunk : bytesRemaining;
        _fstream->read( (char *)_data, (size_t)chunkToRead );
        _data = (char *)(_data) + chunkToRead;
        bytesRemaining -= chunkToRead;
        METAIO_STL::streamsize numberOfBytesRead = _fstream->gcount();
        gc += numberOfBytesRead;
        }

      }
    else
      {
      int elementSize;
      MET_SizeOfType(m_ElementType, &elementSize);
      METAIO_STL::streamoff elementNumberOfBytes = elementSize*m_ElementNumberOfChannels;

      // the data is read with calls no bigger than MaxIOChunk
      METAIO_STL::streamoff bytesRemaining = _dataQuantity * elementNumberOfBytes;
      while ( bytesRemaining )
        {
        METAIO_STL::streamoff chunkToRead = bytesRemaining > MaxIOChunk ? MaxIOChunk : bytesRemaining;
        _fstream->read( (char *)_data, (size_t)chunkToRead );
        _data = (char *)(_data) + chunkToRead;
        bytesRemaining -= chunkToRead;
        METAIO_STL::streamsize numberOfBytesRead = _fstream->gcount();
        gc += numberOfBytesRead;
        }
      // convert to number of bytes so that it'll match gc's units
      _dataQuantity *= elementNumberOfBytes;
      }
    }

  // check that we actually read the correct number of bytes
  if( gc != _dataQuantity )
    {
    METAIO_STREAM::cerr
      << "MetaImage: M_ReadElementsData: data not read completely"
      << METAIO_STREAM::endl;
    METAIO_STREAM::cerr << "   ideal = " << _dataQuantity << " : actual = " << gc
                        << METAIO_STREAM::endl;
    return false;
    }

  // check the the io stream did not fail in the process of reading
  if ( _fstream->fail() )
    {
    METAIO_STREAM::cerr
      << "MetaImage: M_ReadElementsData: file stream is fail after read"
      << METAIO_STREAM::endl;
    return false;
    }

  return true;
  }


#if (METAIO_USE_NAMESPACE)
}
#endif
