/*=========================================================================

  Program: GDCM (Grassroots DICOM). A DICOM library

  Copyright (c) 2006-2011 Mathieu Malaterre
  All rights reserved.
  See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "gdcmImageCodec.h"
#include "gdcmJPEGCodec.h"
#include "gdcmByteSwap.txx"
#include "gdcmImageChangePhotometricInterpretation.h"
#include "gdcmTrace.h"

#include <iostream>
#include <iomanip>
#include <iterator>

#include <cstring>
#include <limits.h>

namespace gdcm
{

class ImageInternals
{
public:
};

ImageCodec::ImageCodec()
{
  PlanarConfiguration = 0;
  RequestPlanarConfiguration = false;
  RequestPaddedCompositePixelCode = false;
  PI = PhotometricInterpretation::UNKNOWN;
  //LUT = LookupTable(LookupTable::UNKNOWN);
  LUT = new LookupTable;
  NeedByteSwap = false;
  NeedOverlayCleanup = false;
  Dimensions[0] = Dimensions[1] = Dimensions[2] = 0;
  NumberOfDimensions = 0;
  LossyFlag = false;
}

ImageCodec::~ImageCodec()
= default;

bool ImageCodec::GetHeaderInfo(std::istream &, TransferSyntax &)
{
  // This function should really be virtual pure.
  assert( 0 );
  return false;
}

void ImageCodec::SetLossyFlag(bool l)
{
  LossyFlag = l;
}

bool ImageCodec::GetLossyFlag() const
{
  return LossyFlag;
}

bool ImageCodec::IsLossy() const
{
  return LossyFlag;
}

void ImageCodec::SetNumberOfDimensions(unsigned int dim)
{
  NumberOfDimensions = dim;
}

unsigned int ImageCodec::GetNumberOfDimensions() const
{
  return NumberOfDimensions;
}


const PhotometricInterpretation &ImageCodec::GetPhotometricInterpretation() const
{
  return PI;
}

void ImageCodec::SetPhotometricInterpretation(
  PhotometricInterpretation const &pi)
{
  PI = pi;
}

bool ImageCodec::DoByteSwap(std::istream &is, std::ostream &os)
{
  // FIXME: Do some stupid work:
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  size_t buf_size = (size_t)is.tellg();
  //assert(buf_size < INT_MAX);
  char *dummy_buffer = new char[(unsigned int)buf_size];
  is.seekg(start, std::ios::beg);
  is.read( dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset
  //SwapCode sc = is.GetSwapCode();

  assert( !(buf_size % 2) );
#ifdef GDCM_WORDS_BIGENDIAN
  if( PF.GetBitsAllocated() == 16 )
    {
    ByteSwap<uint16_t>::SwapRangeFromSwapCodeIntoSystem((uint16_t*)
      (void*)dummy_buffer, SwapCode::LittleEndian, buf_size/2);
    }
#else
  // GE_DLX-8-MONO2-PrivateSyntax.dcm is 8bits
  //  assert( PF.GetBitsAllocated() == 16 );
  if ( PF.GetBitsAllocated() == 16 )
    {
    ByteSwap<uint16_t>::SwapRangeFromSwapCodeIntoSystem((uint16_t*)
      (void*)dummy_buffer, SwapCode::BigEndian, buf_size/2);
    }
#endif
  os.write(dummy_buffer, buf_size);
  delete[] dummy_buffer;
  return true;
}

bool ImageCodec::DoYBR(std::istream &is, std::ostream &os)
{
  // FIXME: Do some stupid work:
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  size_t buf_size = (size_t)is.tellg();
  //assert(buf_size < INT_MAX);
  char *dummy_buffer = new char[(unsigned int)buf_size];
  is.seekg(start, std::ios::beg);
  is.read( dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset
  //SwapCode sc = is.GetSwapCode();

  // Code is coming from:
  // http://lestourtereaux.free.fr/papers/data/yuvrgb.pdf
  assert( !(buf_size % 3) );
  unsigned long size = (unsigned long)buf_size/3;
  //assert(buf_size < INT_MAX);
  unsigned char *copy = new unsigned char[ (unsigned int)buf_size ];
  memmove( copy, dummy_buffer, (size_t)buf_size);
assert(0); // Do not use this code !
  // FIXME FIXME FIXME
  // The following is bogus: we are doing two operation at once:
  // Planar configuration AND YBR... doh !
  const unsigned char *a = copy + 0;
  const unsigned char *b = copy + size;
  const unsigned char *c = copy + size + size;
  int R, G, B;

  unsigned char *p = (unsigned char*)dummy_buffer;
  for (unsigned long j = 0; j < size; ++j)
    {
    R = 38142 *(*a-16) + 52298 *(*c -128);
    G = 38142 *(*a-16) - 26640 *(*c -128) - 12845 *(*b -128);
    B = 38142 *(*a-16) + 66093 *(*b -128);

    R = (R+16384)>>15;
    G = (G+16384)>>15;
    B = (B+16384)>>15;

    if (R < 0)   R = 0;
    if (G < 0)   G = 0;
    if (B < 0)   B = 0;
    if (R > 255) R = 255;
    if (G > 255) G = 255;
    if (B > 255) B = 255;

    *(p++) = (unsigned char)R;
    *(p++) = (unsigned char)G;
    *(p++) = (unsigned char)B;
    a++;
    b++;
    c++;
    }
  delete[] copy;

  os.write(dummy_buffer, buf_size);
  delete[] dummy_buffer;
  return true;
}

bool ImageCodec::DoYBRFull422(std::istream &is, std::ostream &os)
{
  // FIXME: Do some stupid work:
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  const size_t buf_size = (size_t)is.tellg();
  const size_t rgb_buf_size = buf_size * 3 / 2;
  unsigned char *dummy_buffer = new unsigned char[buf_size];
  is.seekg(start, std::ios::beg);
  is.read( (char*)dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset

  assert( !(rgb_buf_size % 3) );
  assert( !(buf_size % 2) );
  unsigned char *copy = new unsigned char[ rgb_buf_size ];
  const size_t size = buf_size/4;

  for (size_t j = 0; j < size; ++j)
    {
    unsigned char ybr422[4];
    ybr422[0] = dummy_buffer[4*j+0];
    ybr422[1] = dummy_buffer[4*j+1];
    ybr422[2] = dummy_buffer[4*j+2];
    ybr422[3] = dummy_buffer[4*j+3];

#if 0
    unsigned char ybr1[3];
    unsigned char ybr2[3];
    ybr1[0] = ybr422[0];
    ybr1[1] = ybr422[2];
    ybr1[2] = ybr422[3];
    ybr2[0] = ybr422[1];
    ybr2[1] = ybr422[2];
    ybr2[2] = ybr422[3];

    unsigned char rgb[6];
    ImageChangePhotometricInterpretation::YBR2RGB(rgb + 0, ybr1);
    ImageChangePhotometricInterpretation::YBR2RGB(rgb + 3, ybr2);
    memcpy(copy + 6 * j, rgb, 6 );
#else
    unsigned char ybr[6]; // ybr1 + ybr2
    ybr[0] = ybr422[0];
    ybr[1] = ybr422[2];
    ybr[2] = ybr422[3];
    ybr[3] = ybr422[1];
    ybr[4] = ybr422[2];
    ybr[5] = ybr422[3];
    memcpy(copy + 6 * j, ybr, 6 );
#endif
    }
  os.write((char*)copy, rgb_buf_size);
  delete[] copy;
  delete[] dummy_buffer;
  return true;
}

bool ImageCodec::DoPlanarConfiguration(std::istream &is, std::ostream &os)
{
  // FIXME: Do some stupid work:
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  size_t buf_size = (size_t)is.tellg();
  //assert(buf_size < INT_MAX);
  char *dummy_buffer = new char[(unsigned int)buf_size];
  is.seekg(start, std::ios::beg);
  is.read( dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset
  //SwapCode sc = is.GetSwapCode();

  // US-RGB-8-epicard.dcm
  //assert( image.GetNumberOfDimensions() == 3 );
  assert( buf_size % 3 == 0 );
  unsigned long size = (unsigned long)buf_size/3;
  char *copy = new char[ (unsigned int)buf_size ];
  //memmove( copy, dummy_buffer, buf_size);

  const char *r = dummy_buffer /*copy*/;
  const char *g = dummy_buffer /*copy*/ + size;
  const char *b = dummy_buffer /*copy*/ + size + size;

  char *p = copy /*dummy_buffer*/;
  for (unsigned long j = 0; j < size; ++j)
    {
    *(p++) = *(r++);
    *(p++) = *(g++);
    *(p++) = *(b++);
    }
  delete[] dummy_buffer /*copy*/;

  os.write(copy /*dummy_buffer*/, buf_size);
  delete[] copy;
  return true;
}

bool ImageCodec::DoSimpleCopy(std::istream &is, std::ostream &os)
{
#if 1
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  size_t buf_size = (size_t)is.tellg();
  //assert(buf_size < INT_MAX);
  char *dummy_buffer = new char[(unsigned int)buf_size];
  is.seekg(start, std::ios::beg);
  is.read( dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset
  os.write( dummy_buffer, buf_size);
  delete[] dummy_buffer ;
#else
  // This code is ideal but is failing on an RLE image...need to figure out
  // what is wrong to reactivate this code.
  os.rdbuf( is.rdbuf() );
#endif

  return true;
}

bool ImageCodec::DoPaddedCompositePixelCode(std::istream &is, std::ostream &os)
{
  // FIXME: Do some stupid work:
  std::streampos start = is.tellg();
  assert( 0 - start == 0 );
  is.seekg( 0, std::ios::end);
  size_t buf_size = (size_t)is.tellg();
  //assert(buf_size < INT_MAX);
  char *dummy_buffer = new char[(unsigned int)buf_size];
  is.seekg(start, std::ios::beg);
  is.read( dummy_buffer, buf_size);
  is.seekg(start, std::ios::beg); // reset
  //SwapCode sc = is.GetSwapCode();

  assert( !(buf_size % 2) );
  bool ret = true;
  if( GetPixelFormat().GetBitsAllocated() == 16 )
    {
    for(size_t i = 0; i < buf_size/2; ++i)
      {
#ifdef GDCM_WORDS_BIGENDIAN
      os.write( dummy_buffer+i, 1 );
      os.write( dummy_buffer+i+buf_size/2, 1 );
#else
      os.write( dummy_buffer+i+buf_size/2, 1 );
      os.write( dummy_buffer+i, 1 );
#endif
      }
    }
  else if( GetPixelFormat().GetBitsAllocated() == 32 )
    {
  assert( !(buf_size % 4) );
    for(size_t i = 0; i < buf_size/4; ++i)
      {
#ifdef GDCM_WORDS_BIGENDIAN
      os.write( dummy_buffer+i, 1 );
      os.write( dummy_buffer+i+1*buf_size/4, 1 );
      os.write( dummy_buffer+i+2*buf_size/4, 1 );
      os.write( dummy_buffer+i+3*buf_size/4, 1 );
#else
      os.write( dummy_buffer+i+3*buf_size/4, 1 );
      os.write( dummy_buffer+i+2*buf_size/4, 1 );
      os.write( dummy_buffer+i+1*buf_size/4, 1 );
      os.write( dummy_buffer+i, 1 );
#endif
      }
    }
  else
    {
    ret = false;
    }
  delete[] dummy_buffer;
  return ret;
}

bool ImageCodec::DoInvertMonochrome(std::istream &is, std::ostream &os)
{
  if ( PF.GetPixelRepresentation() )
    {
    if ( PF.GetBitsAllocated() == 8 )
      {
      uint8_t c;
      while( is.read((char*)&c,1) )
        {
        c = (uint8_t)(255 - c);
        os.write((char*)&c, 1 );
        }
      }
    else if ( PF.GetBitsAllocated() == 16 )
      {
      assert( PF.GetBitsStored() != 12 );
      uint16_t smask16 = 65535;
      uint16_t c;
      while( is.read((char*)&c,2) )
        {
        c = (uint16_t)(smask16 - c);
        os.write((char*)&c, 2);
        }
      }
    }
  else
    {
    if ( PF.GetBitsAllocated() == 8 )
      {
      uint8_t c;
      while( is.read((char*)&c,1) )
        {
        c = (uint8_t)(255 - c);
        os.write((char*)&c, 1);
        }
      }
    else if ( PF.GetBitsAllocated() == 16 )
      {
      uint16_t mask = 1;
      for (int j=0; j<PF.GetBitsStored()-1; ++j)
        {
        mask = (uint16_t)((mask << 1) + 1); // will be 0x0fff when BitsStored = 12
        }

      uint16_t c;
      while( is.read((char*)&c,2) )
        {
        if( c > mask )
          {
          // IMAGES/JPLY/RG3_JPLY aka CompressedSamples^RG3/1.3.6.1.4.1.5962.1.1.11.1.5.20040826185059.5457
          // gdcmData/D_CLUNIE_RG3_JPLY.dcm
          // stores a 12bits JPEG stream with scalar value [0,1024], however
          // the DICOM header says the data are stored on 10bits [0,1023], thus this HACK:
          gdcmWarningMacro( "Bogus max value: "<< c << " max should be at most: " << mask
            << " results will be truncated. Use at own risk");
          c = mask;
          }
        assert( c <= mask );
        c = (uint16_t)(mask - c);
        assert( c <= mask );
        os.write((char*)&c, 2);
        }
      }

    }
  return true;
}

struct ApplyMask
{
  uint16_t operator()(uint16_t c) const {
    return (uint16_t)((c >> (BitsStored - HighBit - 1)) & pmask);
  }
  unsigned short BitsStored;
  unsigned short HighBit;
  uint16_t pmask;
};

bool ImageCodec::CleanupUnusedBits(char * data8, size_t datalen)
{
  if( !NeedOverlayCleanup ) return true;
  void * data = data8;
  assert( PF.GetBitsAllocated() > 8 );
  if( PF.GetBitsAllocated() == 16 )
    {
    // pmask : to mask the 'unused bits' (may contain overlays)
    uint16_t pmask = 0xffff;
    pmask = (uint16_t)(pmask >> ( PF.GetBitsAllocated() - PF.GetBitsStored() ));

    if( PF.GetPixelRepresentation() )
      {
      // smask : to check the 'sign' when BitsStored != BitsAllocated
      uint16_t smask = 0x0001;
      smask = (uint16_t)(
        smask << ( 16 - (PF.GetBitsAllocated() - PF.GetBitsStored() + 1) ));
      // nmask : to propagate sign bit on negative values
      int16_t nmask = (int16_t)0x8000;
      nmask = (int16_t)(nmask >> ( PF.GetBitsAllocated() - PF.GetBitsStored() - 1 ));

      uint16_t *start = (uint16_t*)data;
      for( uint16_t *p = start ; p != start + datalen / 2; ++p )
        {
        uint16_t c = *p;
        c = (uint16_t)(c >> (PF.GetBitsStored() - PF.GetHighBit() - 1));
        if ( c & smask )
          {
          c = (uint16_t)(c | nmask);
          }
        else
          {
          c = c & pmask;
          }
        *p = c;
        }
      }
    else // Pixel are unsigned
      {
      uint16_t *start = (uint16_t*)data;
      for( uint16_t *p = start ; p != start + datalen / 2; ++p )
        {
        uint16_t c = *p;
        c = (uint16_t)(
          (c >> (PF.GetBitsStored() - PF.GetHighBit() - 1)) & pmask);
        *p = c;
        }
      }
    }
  else
    {
    assert(0); // TODO
    return false;
    }
  return true;
}

// Cleanup the unused bits
bool ImageCodec::DoOverlayCleanup(std::istream &is, std::ostream &os)
{
  assert( PF.GetBitsAllocated() > 8 );
  if( PF.GetBitsAllocated() == 16 )
    {
    // pmask : to mask the 'unused bits' (may contain overlays)
    uint16_t pmask = 0xffff;
    pmask = (uint16_t)(pmask >> ( PF.GetBitsAllocated() - PF.GetBitsStored() ));

    if( PF.GetPixelRepresentation() )
      {
      // smask : to check the 'sign' when BitsStored != BitsAllocated
      uint16_t smask = 0x0001;
      smask = (uint16_t)(
        smask << ( 16 - (PF.GetBitsAllocated() - PF.GetBitsStored() + 1) ));
      // nmask : to propagate sign bit on negative values
      int16_t nmask = (int16_t)0x8000;
      nmask = (int16_t)(nmask >> ( PF.GetBitsAllocated() - PF.GetBitsStored() - 1 ));

      uint16_t c;
      while( is.read((char*)&c,2) )
        {
        c = (uint16_t)(c >> (PF.GetBitsStored() - PF.GetHighBit() - 1));
        if ( c & smask )
          {
          c = (uint16_t)(c | nmask);
          }
        else
          {
          c = c & pmask;
          }
        os.write((char*)&c, 2 );
        }
      }
    else // Pixel are unsigned
      {
#if 1
      // On Windows, is.read and os.write are expensive operations.
      // If we called it for each value then conversion would be extremely slow.
      // Therefore we read/mask/write 1000 values at once.
      const unsigned int bufferSize = 1000;
      std::vector<uint16_t> buffer(bufferSize);
      while (is)
        {
        is.read((char *)&buffer[0], bufferSize * sizeof(uint16_t));
        std::streamsize bytesRead = is.gcount();
        std::vector<uint16_t>::iterator validBufferEnd = buffer.begin() + bytesRead / sizeof(uint16_t);
        for (std::vector<uint16_t>::iterator it = buffer.begin(); it != validBufferEnd; ++it)
          {
          *it = ((*it >> (PF.GetBitsStored() - PF.GetHighBit() - 1)) & pmask);
          }
        os.write((char *)&buffer[0], bytesRead);
        };
#else
      //std::ostreambuf_iterator<char> end_of_stream_iterator;
      //std::ostreambuf_iterator<char> out_iter(os.rdbuf());
      //while( out_iter != end_of_stream_iterator )
      //  {
      //  *out_iter =
      //    (*out_iter >> (PF.GetBitsStored() - PF.GetHighBit() - 1)) & pmask;
      //  }
      std::istreambuf_iterator<int> it_in(is);
      std::istreambuf_iterator<int> eos;
      std::ostreambuf_iterator<int> it_out(os);
      ApplyMask am;
      am.BitsStored = PF.GetBitsStored();
      am.HighBit = PF.GetHighBit();
      am.pmask = pmask;

      std::transform(it_in, eos, it_out, am);
#endif
      }
    }
  else
    {
    assert(0); // TODO
    return false;
    }
  return true;
}

bool ImageCodec::Decode(DataElement const &, DataElement &)
{
  return true;
}

bool ImageCodec::DecodeByStreams(std::istream &is, std::ostream &os)
{
  assert( PlanarConfiguration == 0 || PlanarConfiguration == 1);
  assert( PI != PhotometricInterpretation::UNKNOWN );
  std::stringstream bs_os; // ByteSwap
  std::stringstream pcpc_os; // Padded Composite Pixel Code
  std::stringstream pi_os; // PhotometricInterpretation
  std::stringstream pl_os; // PlanarConf
  std::istream *cur_is = &is;

  // First thing do the byte swap:
  if( NeedByteSwap )
    {
    // MR_GE_with_Private_Compressed_Icon_0009_1110.dcm
    DoByteSwap(*cur_is,bs_os);
    cur_is = &bs_os;
    }
  if ( RequestPaddedCompositePixelCode )
    {
    // D_CLUNIE_CT2_RLE.dcm
    DoPaddedCompositePixelCode(*cur_is,pcpc_os);
    cur_is = &pcpc_os;
    }

  // Second thing do palette color.
  // This way PALETTE COLOR will be applied before we do
  // Planar Configuration
  switch(PI)
    {
  case PhotometricInterpretation::MONOCHROME2:
  case PhotometricInterpretation::RGB:
  case PhotometricInterpretation::ARGB:
    break;
  case PhotometricInterpretation::MONOCHROME1:
    // CR-MONO1-10-chest.dcm
    //DoInvertMonochrome(*cur_is, pi_os);
    //cur_is = &pi_os;
    break;
  case PhotometricInterpretation::YBR_FULL:
    //DoYBR(*cur_is,pi_os);
    //cur_is = &pi_os;
    {
      const JPEGCodec *c = dynamic_cast<const JPEGCodec*>(this);
      if( c )
        {
        // The following is required for very special case of color space conversion
        // dcmdrle ACUSON-24-YBR_FULL-RLE.dcm bla.dcm
        // dcmcjpeg bla.dcm foo.dcm
        // foo.dcm would be not displayed correctly
        //this->SetPhotometricInterpretation( PhotometricInterpretation::RGB );
        }
    }
    break;
  case PhotometricInterpretation::PALETTE_COLOR:
    //assert( LUT );
    // Nothing needs to be done
    break;
  case PhotometricInterpretation::YBR_FULL_422:
      {
      // US-GE-4AICL142.dcm
      // Hopefully it has been done by the JPEG decoder itself...
      const JPEGCodec *c = dynamic_cast<const JPEGCodec*>(this);
      if( !c )
        {
        //gdcmErrorMacro( "YBR_FULL_422 is not implemented in GDCM. Image will be displayed incorrectly" );
        //this->SetPhotometricInterpretation( PhotometricInterpretation::RGB );
        DoYBRFull422(*cur_is,pl_os);
        cur_is = &pl_os;
        }
      }
    break;
  case PhotometricInterpretation::YBR_ICT:
    break;
  case PhotometricInterpretation::YBR_RCT:
    break;
  default:
    gdcmErrorMacro( "Unhandled PhotometricInterpretation: " << PI );
    return false;
    assert(0);
    }

  if( /*PlanarConfiguration ||*/ RequestPlanarConfiguration )
    {
    DoPlanarConfiguration(*cur_is,pl_os);
    cur_is = &pl_os;
    }

  // Do the overlay cleanup (cleanup the unused bits)
  // must be the last operation (duh!)
  if ( PF.GetBitsAllocated() != PF.GetBitsStored()
    && PF.GetBitsAllocated() != 8 )
    {
    // Technically we should only run this operation if the image declares it has overlay AND
    // there is no (0x60xx,0x3000) element, for example:
    // - XA_GE_JPEG_02_with_Overlays.dcm
    // - SIEMENS_GBS_III-16-ACR_NEMA_1.acr
    // Sigh, I finally found someone not declaring that unused bits where not zero:
    // gdcmConformanceTests/dcm4chee_unusedbits_not_zero.dcm
    if( NeedOverlayCleanup )
      DoOverlayCleanup(*cur_is,os);
    else
      {
      // Once the issue with IMAGES/JPLY/RG3_JPLY aka gdcmData/D_CLUNIE_RG3_JPLY.dcm is solved the previous
      // code will be replace with a simple call to:
      DoSimpleCopy(*cur_is,os);
      }
    }
  else
    {
    assert( PF.GetBitsAllocated() == PF.GetBitsStored() );
    DoSimpleCopy(*cur_is,os);
    }

  return true;
}

bool ImageCodec::IsValid(PhotometricInterpretation const &)
{
  return false;
}

void ImageCodec::SetDimensions(const unsigned int d[3])
{
  Dimensions[0] = d[0];
  Dimensions[1] = d[1];
  Dimensions[2] = d[2];
}

void ImageCodec::SetDimensions(const std::vector<unsigned int> & d)
{
  size_t theSize = d.size();
  assert(theSize<= 3);
  for (size_t i = 0; i < 3; i++)
    {
    if (i < theSize)
      Dimensions[i] = d[i];
    else
      Dimensions[i] = 1;
    }
}

bool ImageCodec::StartEncode( std::ostream & )
{
  assert(0);
  return false;
}
bool ImageCodec::IsRowEncoder()
{
  return false;
}
bool ImageCodec::IsFrameEncoder()
{
  return false;
}
bool ImageCodec::AppendRowEncode( std::ostream & , const char * , size_t )
{
  assert(0);
  return false;
}
// TODO: technically the frame encoder could use the row encoder when present
// this could reduce code duplication
bool ImageCodec::AppendFrameEncode( std::ostream & , const char * , size_t )
{
  assert(0);
  return false;
}
bool ImageCodec::StopEncode( std::ostream & )
{
  assert(0);
  return false;
}

} // end namespace gdcm
