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

  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.

=========================================================================*/
/*
 * TODO: This app should be a suclass of gdcmimg application to avoid code duplication
 */
#include "vtkGDCMImageReader.h"
#include "vtkGDCMImageWriter.h"

#include "vtkVersion.h"
#include "vtkErrorCode.h"
#include "vtkStringArray.h"
//#include "vtkImageCast.h" // DEBUG
#include "vtkImageReader2Factory.h"
#include "vtkImageReader2.h"
#include "vtkImageData.h"
#include "vtkTIFFWriter.h"
#include "vtkPNGWriter.h"
#include "vtkPNMWriter.h"
#include "vtkBMPWriter.h"
#include "vtkImageChangeInformation.h"
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
#include "vtkMetaImageReader.h"
#include "vtkXMLImageDataReader.h"
#include "vtkMetaImageWriter.h"
#include "vtkDICOMImageReader.h"
#include "vtkMINCImageReader.h"
#include "vtkMINCImageAttributes.h"
#include "vtk_tiff.h" // ORIENTATION_BOTLEFT
#include "vtkImageRGBToYBR.h"
#endif
#include "vtkMedicalImageProperties.h"
#include "vtkTIFFReader.h"
#include "vtkGESignaReader.h"
#include "vtkImageExtractComponents.h"
#include "vtkJPEGReader.h"
#include "vtkBMPReader.h"
#include "vtkLookupTable.h"
#include "vtkPointData.h"
#include "vtkStructuredPointsReader.h"
#include "vtkStructuredPointsWriter.h"
#include "vtkStructuredPoints.h"
#include "vtkXMLImageDataWriter.h"

#include "gdcmFilename.h"
#include "gdcmTrace.h"
#include "gdcmVersion.h"
#include "gdcmImageHelper.h"
#include "gdcmFileMetaInformation.h"
#include "gdcmSystem.h"
#include "gdcmUIDGenerator.h"
#include "gdcmDirectory.h"
#include "gdcmIPPSorter.h"

#include <getopt.h>

#if VTK_MAJOR_VERSION >= 6
#ifndef vtkFloatingPointType
#define vtkFloatingPointType double
#endif
#endif

void PrintVersion()
{
  std::cout << "gdcm2vtk: gdcm " << gdcm::Version::GetVersion() << " ";
  const char date[] = "$Date$";
  std::cout << date << std::endl;
  //std::cout << "             VTK " << vtkVersion::GetVTKVersion() << std::endl;
  std::cout << "          " << vtkVersion::GetVTKSourceVersion() << std::endl;
}

void PrintHelp()
{
  PrintVersion();
  std::cout << "Usage: gdcm2vtk [OPTION] input output" << std::endl;
  std::cout << "Convert a vtk-supported file into DICOM.\n";
  std::cout << "Options:" << std::endl;
  std::cout << "     --force-rescale    force rescale." << std::endl;
  std::cout << "     --force-spacing    force spacing." << std::endl;
  std::cout << "     --palette-color    when supported generate a PALETTE COLOR file." << std::endl;
  std::cout << "     --argb             when supported generate a ARGB file." << std::endl;
  std::cout << "     --compress         when supported generate a compressed file." << std::endl;
  std::cout << "     --use-vtkdicom     Use vtkDICOMImageReader (instead of GDCM)." << std::endl;
  std::cout << "     --modality         set Modality." << std::endl;
  std::cout << "     --lower-left       set lower left." << std::endl;
  std::cout << "     --shift            set shift." << std::endl;
  std::cout << "     --scale            set scale." << std::endl;
  std::cout << "     --compress         set compression (MetaIO)." << std::endl;
  std::cout << "  -T --study-uid        Study UID." << std::endl;
  std::cout << "  -S --series-uid       Series UID." << std::endl;
  std::cout << "     --root-uid         Root UID." << std::endl;
  std::cout << "     --imageformat      Image Format [1-8] (aka PhotometricInterpretation)." << std::endl;
  std::cout << "     --ipp-sort         When input is directory, sort instances using IOP/IPP."<< std::endl;
  std::cout << "Compression Types (lossless):" << std::endl;
  std::cout << "  -J --jpeg                           Compress image in jpeg." << std::endl;
  std::cout << "  -K --j2k                            Compress image in j2k." << std::endl;
  std::cout << "  -L --jpegls                         Compress image in jpeg-ls." << std::endl;
  std::cout << "  -R --rle                            Compress image in rle (lossless only)." << std::endl;
  std::cout << "General Options:" << std::endl;
  std::cout << "  -V --verbose    more verbose (warning+error)." << std::endl;
  std::cout << "  -W --warning    print warning info." << std::endl;
  std::cout << "  -D --debug      print debug info." << std::endl;
  std::cout << "  -E --error      print error info." << std::endl;
  std::cout << "  -h --help       print help." << std::endl;
  std::cout << "  -v --version    print version." << std::endl;
  std::cout << "Env var:" << std::endl;
  std::cout << "  GDCM_ROOT_UID Root UID" << std::endl;
}

static inline void copy_files( const std::vector<std::string> & files, vtkStringArray * names )
{
  for( gdcm::Directory::FilenamesType::const_iterator it = files.begin(); it != files.end(); ++it )
    {
    names->InsertNextValue( it->c_str() );
    }
}

int main(int argc, char *argv[])
{
  int retcode = 0;
  int c;
  //int digit_optind = 0;

  std::string root_uid;
  int rootuid = 0;
  std::vector<std::string> filenames;
  int forcerescale = 0;
  int forcespacing = 0;
  int palettecolor = 0;
  int argb = 0;
  int modality = 0;
  std::string modality_str;
  int studyuid = 0;
  int seriesuid = 0;
  // compression
  int jpeg = 0;
  int jpegls = 0;
  int j2k = 0;
  int rle = 0;
  int usevtkdicom = 0;
  int compress = 0;
  int ippsort = 0;
  int lowerleft = 0;
  int oshift = 0;
  int oscale = 0;
  int oimageformat = 0;
  double shift = 0.;
  double scale = 1.;
  int imageformat = 0;

  int verbose = 0;
  int warning = 0;
  int debug = 0;
  int error = 0;
  int help = 0;
  int version = 0;

  gdcm::UIDGenerator uid;
  std::string series_uid = uid.Generate();
  std::string study_uid = uid.Generate();
  while (1) {
    //int this_option_optind = optind ? optind : 1;
    int option_index = 0;
    static struct option long_options[] = {
        {"force-rescale", 0, &forcerescale, 1},
        {"force-spacing", 0, &forcespacing, 1},
        {"palette-color", 0, &palettecolor, 1},
        {"argb", 0, &argb, 1},
        {"modality", 1, &modality, 1},
        {"study-uid", 1, &studyuid, 1},
        {"series-uid", 1, &seriesuid, 1},
        {"root-uid", 1, &rootuid, 1}, // specific Root (not GDCM)
        {"jpeg", 0, &jpeg, 1}, // JPEG lossy / lossless
        {"jpegls", 0, &jpegls, 1}, // JPEG-LS: lossy / lossless
        {"j2k", 0, &j2k, 1}, // J2K: lossy / lossless
        {"rle", 0, &rle, 1}, // lossless !
        {"compress", 0, &compress, 1}, // compress with using MetaIO
        {"use-vtkdicom", 0, &usevtkdicom, 1}, // use vtkDICOMImageReader
        {"lower-left", 0, &lowerleft, 1}, // use FileLowerLeftOn
        {"shift", 1, &oshift, 1}, //
        {"scale", 1, &oscale, 1}, //
        {"imageformat", 1, &oimageformat, 1}, //
        {"ipp-sort", 0, &ippsort, 1}, // use IPP sorting

// General options !
        {"verbose", 0, &verbose, 1},
        {"warning", 0, &warning, 1},
        {"debug", 0, &debug, 1},
        {"error", 0, &error, 1},
        {"help", 0, &help, 1},
        {"version", 0, &version, 1},

        {0, 0, 0, 0}
    };

    c = getopt_long (argc, argv, "JKLRT:S:VWDEhv",
      long_options, &option_index);
    if (c == -1)
      {
      break;
      }

    switch (c)
      {
    case 0:
        {
        const char *s = long_options[option_index].name; (void)s;
        //printf ("option %s", s);
        if (optarg)
          {
          if( option_index == 0 ) /* input */
            {
            assert( strcmp(s, "input") == 0 );
            //assert( filename.empty() );
            //filename = optarg;
            }
          else if( option_index == 4 ) /* modality */
            {
            assert( strcmp(s, "modality") == 0 );
            //assert( filename.empty() );
            modality_str = optarg;
            }
          else if( option_index == 5 ) /* study-uid */
            {
            assert( strcmp(s, "study-uid") == 0 );
            series_uid = optarg;
            }
          else if( option_index == 6 ) /* series-uid */
            {
            assert( strcmp(s, "series-uid") == 0 );
            study_uid = optarg;
            }
          else if( option_index == 7 ) /* root-uid */
            {
            assert( strcmp(s, "root-uid") == 0 );
            root_uid = optarg;
            }
          else if( option_index == 15 ) /* shift */
            {
            assert( strcmp(s, "shift") == 0 );
            shift = atof(optarg);
            }
          else if( option_index == 16 ) /* scale */
            {
            assert( strcmp(s, "scale") == 0 );
            scale = atof(optarg);
            }
          else if( option_index == 17 ) /* imageformat */
            {
            assert( strcmp(s, "imageformat") == 0 );
            imageformat = atoi(optarg);
            }
          //printf (" with arg %s", optarg);
          }
        //printf ("\n");
        }
      break;

    case 'J':
      jpeg = 1;
      break;

    case 'K':
      j2k = 1;
      break;

    case 'L':
      jpegls = 1;
      break;

    case 'R':
      rle = 1;
      break;

    case 'T':
      studyuid = 1;
      study_uid = optarg;
      break;

    case 'S':
      seriesuid = 1;
      series_uid = optarg;
      break;

    case 'V':
      verbose = 1;
      break;

    case 'W':
      warning = 1;
      break;

    case 'D':
      debug = 1;
      break;

    case 'E':
      error = 1;
      break;

    case 'h':
      help = 1;
      break;

    case 'v':
      version = 1;
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
      }
  }

  if (optind < argc)
    {
    //printf ("non-option ARGV-elements: ");
    while (optind < argc)
      {
      //printf ("%s ", argv[optind]);
      filenames.push_back( argv[optind++] );
      }
    //printf ("\n");
    }

  if( version )
    {
    //std::cout << "version" << std::endl;
    PrintVersion();
    return 0;
    }

  if( help )
    {
    //std::cout << "help" << std::endl;
    PrintHelp();
    return 0;
    }

  // Debug is a little too verbose
  gdcm::Trace::SetDebug( debug );
  gdcm::Trace::SetWarning( warning );
  gdcm::Trace::SetError( error );
  // when verbose is true, make sure warning+error are turned on:
  if( verbose )
    {
    gdcm::Trace::SetWarning( verbose );
    gdcm::Trace::SetError( verbose);
    }

  if( filenames.empty() )
    {
    PrintHelp();
    return 1;
    }

  int recursive = 0;
  const char *outfilename = NULL;
  double ippzspacing;
  vtkStringArray *names = vtkStringArray::New();
    {
    // Is it a single directory ? If so loop over all files contained in it:
    //const char *filename = argv[1];
    if( filenames.size() == 2 && gdcm::System::FileIsDirectory( filenames[0].c_str() ) )
      {
      if( verbose )
        std::cout << "Loading directory: " << filenames[0] << std::endl;
      gdcm::Directory d;
      d.Load(filenames[0].c_str(), recursive);
      gdcm::Directory::FilenamesType const &files = d.GetFilenames();
      if( ippsort )
      {
        gdcm::IPPSorter s;
        s.SetComputeZSpacing( true );
        s.SetZSpacingTolerance( 1e-3 );
        bool b = s.Sort( files );
        if( !b )
          {
          std::cerr << "Failed to sort files" << std::endl;
          return 1;
          }
        std::cout << "Sorting succeeded:" << std::endl;
        s.Print( std::cout );
    
        std::cout << "Found z-spacing:" << std::endl;
        std::cout << s.GetZSpacing() << std::endl;
        ippzspacing = s.GetZSpacing();
    
        const std::vector<std::string> & sorted = s.GetFilenames();
        copy_files( sorted, names );
      }
      else
        copy_files( files, names );
      outfilename = filenames[1].c_str();
      }
    else // list of files passed directly on the cmd line:
      // discard non-existing or directory
      {
      for(std::vector<std::string>::const_iterator it = filenames.begin(); it != filenames.end() - 1; ++it)
        {
        const std::string & filename = *it;
        if( gdcm::System::FileExists( filename.c_str() ) )
          {
          if( gdcm::System::FileIsDirectory( filename.c_str() ) )
            {
            if(verbose)
              std::cerr << "Discarding directory: " << filename << std::endl;
            }
          else
            {
            names->InsertNextValue( filename.c_str() );
            }
          }
        else
          {
          if(verbose)
            std::cerr << "Discarding non existing file: " << filename << std::endl;
          }
        }
      outfilename = filenames[ filenames.size() - 1 ].c_str();
      }
    }

  if( !names->GetNumberOfValues() )
    {
    std::cerr << "Missing parameters on the command line" << std::endl;
    return 1;
    }
  const char *filename = names->GetValue( 0 ).c_str();

  gdcm::ImageHelper::SetForceRescaleInterceptSlope(forcerescale);
  gdcm::ImageHelper::SetForcePixelSpacing(forcespacing);

  vtkGDCMImageReader *gdcmreader = vtkGDCMImageReader::New();

#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
  vtkDICOMImageReader *dicomreader = vtkDICOMImageReader::New();
#endif
  if( debug )
    {
    gdcmreader->DebugOn();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    dicomreader->DebugOn();
#endif
    }

  vtkImageReader2Factory* imgfactory = vtkImageReader2Factory::New();
#if VTK_MAJOR_VERSION == 5 && VTK_MINOR_VERSION <= 4
  // warning vtk 5.4 is broken metaimage are not added to factory
  // by default
  vtkMetaImageReader *d = vtkMetaImageReader::New();
  imgfactory->RegisterReader( d );
  d->Delete();
#endif
  if( usevtkdicom )
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    imgfactory->RegisterReader( dicomreader );
#else
    (void)0;
#endif
  else
    imgfactory->RegisterReader( gdcmreader );
  vtkImageReader2* imgreader =
    imgfactory->CreateImageReader2(filename);
  vtkStructuredPointsReader *datareader = vtkStructuredPointsReader::New();
  datareader->SetFileName( filename );
  vtkXMLReader *xmlreader = vtkXMLImageDataReader::New();
  int res = 0;
  if( !imgreader )
    {
    int xmlres = xmlreader->CanReadFile( filename );
    if( !xmlres )
      {
      res = datareader->IsFileStructuredPoints();
      if( !res )
        {
        std::cerr << "could not find no reader to handle file: " << filename << std::endl;
        return 1;
        }
      }
    }
  imgfactory->Delete();

  vtkImageData *imgdata = NULL;
  vtkImageChangeInformation *v16 = vtkImageChangeInformation::New();
  std::string image_comments;
  if( imgreader )
    {
    imgreader->SetFileLowerLeft( lowerleft );
    if( names->GetNumberOfValues() == 1 )
      imgreader->SetFileName( names->GetValue(0) );
    else
      imgreader->SetFileNames(names);
    imgreader->Update();
    if( imgreader->GetErrorCode() )
      {
      std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(imgreader->GetErrorCode()) << std::endl;
      return 1;
      }
    imgdata = imgreader->GetOutput();
    if( ippsort )
    {
      const vtkFloatingPointType *spacing = imgreader->GetOutput()->GetSpacing();
#if VTK_MAJOR_VERSION >= 6
      v16->SetInputConnection( imgreader->GetOutputPort() );
#else
      v16->SetInput( imgreader->GetOutput() );
#endif
      v16->SetOutputSpacing( spacing[0], spacing[1], ippzspacing );
      v16->Update();
      imgdata = v16->GetOutput();
    }
    if( verbose )
      std::cout << "imgreader classname: " << imgreader->GetClassName() << std::endl;
    }
  else if( res )
    {
    datareader->Update();
    if( datareader->GetErrorCode() )
      {
      std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(datareader->GetErrorCode()) << std::endl;
      return 1;
      }
    imgdata = datareader->GetOutput();
    if( datareader->GetHeader() )
      {
      image_comments = datareader->GetHeader();
      }
    }
  else if( xmlreader->CanReadFile( filename ) )
    {
    xmlreader->SetFileName(filename);
    xmlreader->Update();
    imgdata = vtkXMLImageDataReader::SafeDownCast(xmlreader)->GetOutput();
    }

  gdcm::Filename fn = outfilename;
  const char *outputextension = fn.GetExtension();

  if ( outputextension )
    {
    if(  gdcm::System::StrCaseCmp(outputextension,".vtk") == 0  )
      {
      vtkStructuredPointsWriter * writer = vtkStructuredPointsWriter::New();
      writer->SetFileName( outfilename );
      writer->SetFileTypeToBinary();
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
    else if(  gdcm::System::StrCaseCmp(outputextension,".bmp") == 0 )
      {
      vtkBMPWriter * writer = vtkBMPWriter::New();
      writer->SetFileName( outfilename );
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
    else if(  gdcm::System::StrCaseCmp(outputextension,".pgm") == 0
      || gdcm::System::StrCaseCmp(outputextension,".pnm") == 0
      || gdcm::System::StrCaseCmp(outputextension,".ppm") == 0 )
      {
      vtkPNMWriter * writer = vtkPNMWriter::New();
      writer->SetFileName( outfilename );
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
    else if(  gdcm::System::StrCaseCmp(outputextension,".png") == 0 )
      {
      vtkPNGWriter * writer = vtkPNGWriter::New();
      writer->SetFileName( outfilename );
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
    else if(  gdcm::System::StrCaseCmp(outputextension,".tif") == 0
      ||  gdcm::System::StrCaseCmp(outputextension,".tiff") == 0  ) //
      {
      vtkTIFFWriter * writer = vtkTIFFWriter::New();
      writer->SetFileName( outfilename );
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
    else if(  gdcm::System::StrCaseCmp(outputextension,".vti") == 0  ) // vtkXMLImageDataWriter::GetDefaultFileExtension()
      {
      vtkXMLImageDataWriter * writer = vtkXMLImageDataWriter::New();
      writer->SetFileName( outfilename );
      writer->SetDataModeToBinary();
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
#endif
      writer->Delete();
      goto cleanup;
      }
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    else if(  gdcm::System::StrCaseCmp(outputextension,".mha") == 0 ||
              gdcm::System::StrCaseCmp(outputextension,".mhd") == 0  ) // vtkMetaImageReader::GetFileExtensions()
      {
      // Weird, the writer does not offer the same API as the Reader, for instance
      // One cannot set the patient name to store (see vtkMetaImageReader::GetPatientName ...)
      // worse I cannot even set the transform ! Which means Reader -> Writer just breaks everything -sigh-
      vtkMetaImageWriter * writer = vtkMetaImageWriter::New();
      writer->SetFileName( outfilename );
#if VTK_MAJOR_VERSION >= 6
      writer->SetInputData( imgdata );
#else
      writer->SetInput( imgdata );
#endif
      writer->SetCompression( compress );
      //writer->FileLowerLeftOff(); // not used in the implementation
      writer->Write();
      if( writer->GetErrorCode() )
        {
        std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
        retcode = 1;
        }
      writer->Delete();
      goto cleanup;
      }
#endif
    }
//  else
    {

  vtkGDCMImageWriter * writer = vtkGDCMImageWriter::New();
  //writer->SetFileLowerLeft( 1 );
  if( studyuid )
    {
    writer->SetStudyUID( study_uid.c_str() );
    }
  if( seriesuid )
    {
    writer->SetSeriesUID( series_uid.c_str() );
    }

  // Default:
  writer->SetCompressionType( vtkGDCMImageWriter::NO_COMPRESSION ); // Implicit VR Little Endian
  if( jpeg )
    {
    writer->SetCompressionType( vtkGDCMImageWriter::JPEG_COMPRESSION );
    }
  else if( j2k )
    {
    writer->SetCompressionType( vtkGDCMImageWriter::JPEG2000_COMPRESSION );
    }
  else if( jpegls )
    {
    writer->SetCompressionType( vtkGDCMImageWriter::JPEGLS_COMPRESSION );
    }
  else if( rle )
    {
    writer->SetCompressionType( vtkGDCMImageWriter::RLE_COMPRESSION );
    }

  // HACK: call it *after* instanciating vtkGDCMImageWriter
  if( !gdcm::UIDGenerator::IsValid( study_uid.c_str() ) )
    {
    std::cerr << "Invalid UID for Study UID: " << study_uid << std::endl;
    return 1;
    }
  if( !gdcm::UIDGenerator::IsValid( series_uid.c_str() ) )
    {
    std::cerr << "Invalid UID for Series UID: " << series_uid << std::endl;
    return 1;
    }
  gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "gdcm2vtk" );
  if( !rootuid )
    {
    // only read the env var is no explicit cmd line option
    // maybe there is an env var defined... let's check
    const char *rootuid_env = getenv("GDCM_ROOT_UID");
    if( rootuid_env )
      {
      rootuid = 1;
      root_uid = rootuid_env;
      }
    }
  if( rootuid )
    {
    if( !gdcm::UIDGenerator::IsValid( root_uid.c_str() ) )
      {
      std::cerr << "specified Root UID is not valid: " << root_uid << std::endl;
      return 1;
      }
    gdcm::UIDGenerator::SetRoot( root_uid.c_str() );
    }

  writer->SetFileName( outfilename );

  if( imgreader && imgreader->GetOutput()->GetNumberOfScalarComponents() == 4 && !argb )
    {
    if( verbose )
      {
      std::cout << "alpha channel will be lost " << imgreader->GetOutput()->GetNumberOfScalarComponents() << std::endl;
      }
    vtkImageExtractComponents *extract = vtkImageExtractComponents::New();
#if VTK_MAJOR_VERSION >= 6
    extract->SetInputConnection( imgreader->GetOutputPort() );
#else
    extract->SetInput( imgreader->GetOutput() );
#endif
    extract->SetComponents( 0,1,2 );
#if VTK_MAJOR_VERSION >= 6
    writer->SetInputConnection( extract->GetOutputPort() );
#else
    writer->SetInput( extract->GetOutput() );
#endif
    extract->Delete();
    }
  else
    {
    //writer->SetInput( imgreader->GetOutput() );
#if VTK_MAJOR_VERSION >= 6
    writer->SetInputData( imgdata );
#else
    writer->SetInput( imgdata );
#endif

#if 0
    vtkImageRGBToYBR * rgb2ybr = vtkImageRGBToYBR::New();
    rgb2ybr->SetInput( imgreader->GetOutput() );
    writer->SetInput( rgb2ybr->GetOutput() );
    writer->SetImageFormat( VTK_YBR );
#endif
    }

  // If input is 3D, let's write output as 3D if possible:
  if( imgdata->GetDimensions()[2] != 1 )
    {
    writer->SetFileDimensionality( 3 );
    }

  if( imgreader )
    {
    if( vtkGDCMImageReader * reader0 = vtkGDCMImageReader::SafeDownCast(imgreader) )
      {
      writer->SetMedicalImageProperties( reader0->GetMedicalImageProperties() );
      writer->SetDirectionCosines( reader0->GetDirectionCosines() );
      writer->SetShift( reader0->GetShift() );
      writer->SetScale( reader0->GetScale() );
      writer->SetImageFormat( reader0->GetImageFormat() );
      writer->SetLossyFlag( reader0->GetLossyFlag() );
      if( verbose )
        {
        reader0->GetOutput()->Print( std::cout );
        reader0->GetMedicalImageProperties()->Print( std::cout );
        }
      }
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    else if( vtkDICOMImageReader * reader1 = vtkDICOMImageReader::SafeDownCast(imgreader) )
      {
      const float* iop = reader1->GetImageOrientationPatient();
      double dircos[6];
      for(int i = 0; i < 6; ++i)
        dircos[i] = iop[i];
      writer->SetDirectionCosinesFromImageOrientationPatient( dircos );

      writer->GetMedicalImageProperties()->SetModality( "MR" ); // FIXME
      writer->GetMedicalImageProperties()->SetPatientName( reader1->GetPatientName() );
      //writer->GetMedicalImageProperties()->SetStudyUID( reader1->GetStudyUID() ); // TODO
      writer->GetMedicalImageProperties()->SetStudyID( reader1->GetStudyID() );
      //writer->GetMedicalImageProperties()->SetGantryTilt( reader1->GetGantryAngle() ); // TODO
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 2)
      writer->GetMedicalImageProperties()->SetDirectionCosine( dircos[0],
        dircos[1],
        dircos[2],
        dircos[3],
        dircos[4],
        dircos[5]
      );
#endif
      writer->SetShift( reader1->GetRescaleOffset() );
      writer->SetScale( reader1->GetRescaleSlope() );
      //writer->SetImageFormat( reader1->GetImageFormat() );
      //writer->SetLossyFlag( reader1->GetLossyFlag() );
      }
#endif
    else if( vtkJPEGReader * reader2 = vtkJPEGReader::SafeDownCast(imgreader) )
      {
      (void)reader2;
      // vtk JPEG reader only read 8bits lossy file
      writer->SetLossyFlag( 1 );
      // TODO: It would be nice to specify the original encoder was JPEG -> ISO_10918_1
      }
    else if( vtkBMPReader * reader3 = vtkBMPReader::SafeDownCast(imgreader) )
      {
      if( palettecolor )
        reader3->Allow8BitBMPOn( );
      reader3->Update( );
      //reader3->GetLookupTable()->Print( std::cout );
      if( palettecolor )
        {
        if( reader3->GetNumberOfScalarComponents() == 1 )
          {
          reader3->GetOutput()->GetPointData()->GetScalars()->SetLookupTable( reader3->GetLookupTable() );
          writer->SetImageFormat( VTK_LOOKUP_TABLE );
          }
        }
      }
    else if( vtkGESignaReader * reader4 = vtkGESignaReader::SafeDownCast(imgreader) )
      {
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      writer->SetMedicalImageProperties( reader4->GetMedicalImageProperties() );
#endif
      //reader4->GetMedicalImageProperties()->Print( std::cout );
      }
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    else if( vtkMINCImageReader *reader5 = vtkMINCImageReader::SafeDownCast( imgreader ) )
      {
      writer->SetDirectionCosines( reader5->GetDirectionCosines() );
      //writer->GetMedicalImageProperties()->SetModality( "MR" );
      // the following does not work with VTKData/Data/t3_grid_0.mnc
      //writer->SetScale( reader5->GetRescaleSlope() );
      //writer->SetShift( reader5->GetRescaleIntercept() );
      if( verbose )
        reader5->GetImageAttributes()->PrintFileHeader();
      }
#endif
    else if( vtkTIFFReader *reader6 = vtkTIFFReader::SafeDownCast( imgreader ) )
      {
      // TIFF has resolution (spacing), and VTK make sure to set set in mm
      // For some reason vtkTIFFReader is all skrew up and will load the image in whatever orientation
      // as stored on file, thus this is up to the user to set it properly...
      // If anyone has any clue why...
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
      reader6->SetOrientationType( ORIENTATION_BOTLEFT );
#endif
      }
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
    else if( vtkMetaImageReader *reader7 = vtkMetaImageReader::SafeDownCast( imgreader ) )
      {
//  vtkGetMacro(RescaleSlope, double);
//  vtkGetMacro(RescaleOffset, double);
      writer->SetScale( reader7->GetRescaleSlope() );
      writer->SetShift( reader7->GetRescaleOffset() );
//  vtkGetStringMacro(Modality);
      writer->GetMedicalImageProperties()->SetModality( reader7->GetModality() );
    writer->SetFileLowerLeft( lowerleft );

//  vtkGetStringMacro(DistanceUnits);
  // -> this one is insane, the default behavior is 'um' . What in the world is 'um' unit ?
  // I think I should discard any non 'mm' distance unit, simple as that

// The following looks nice from the API, but the VTK implementation only sets them to '?' ... weird
//  vtkGetMacro(BitsAllocated, int);
//  vtkGetStringMacro(AnatomicalOrientation);
//  vtkGetMacro(GantryAngle, double);
//  vtkGetStringMacro(PatientName);
//  vtkGetStringMacro(PatientID);
//  vtkGetStringMacro(Date);
//  vtkGetStringMacro(Series);
//  vtkGetStringMacro(ImageNumber);
//  vtkGetStringMacro(StudyID);
//  vtkGetStringMacro(StudyUID);
//  vtkGetStringMacro(TransferSyntaxUID);
      }
#endif
    }
  // nothing special need to be done for vtkStructuredPointsReader

  // Handle here the specific modality:
  if ( modality )
    {
    if( !modality_str.empty() )
      writer->GetMedicalImageProperties()->SetModality( modality_str.c_str() );;
    }

  // I would like to use vtkStructuredPointsReader::GetHeader as Image Comments,
  // however this is not possible with the current vtkMedicalImageProperties...
  if( !image_comments.empty() )
    {
    writer->GetMedicalImageProperties()->SetSeriesDescription( image_comments.c_str() );;
    }

  // Let's do that at the end to be sure it always overwrite any other default
  if( oshift )
    {
    writer->SetShift( shift );
    }
  if( oscale )
    {
    writer->SetScale( scale );
    }
  if( oimageformat )
    {
    writer->SetImageFormat( imageformat );
    }

  // Pass on the filetime of input file
  time_t studydatetime = gdcm::System::FileTime( filename );
  char date[22];
  gdcm::System::FormatDateTime(date, studydatetime);
  // ContentDate
  const size_t datelen = 8;
    {
    // Do not copy the whole cstring:
    std::string s( date, date+datelen );
    writer->GetMedicalImageProperties()->SetImageDate( s.c_str() );;
    }
  // ContentTime
  const size_t timelen = 6; // get rid of milliseconds
    {
    // Do not copy the whole cstring:
    std::string s( date+datelen, timelen );
    writer->GetMedicalImageProperties()->SetImageTime( s.c_str() );;
    }

  writer->Write();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
  if( writer->GetErrorCode() )
    {
    std::cerr << "There was an error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) << std::endl;
    return 1;
    }
#endif

  if( verbose )
    writer->GetInput()->Print( std::cout );
  writer->Delete();
}

cleanup:
  if( imgreader ) imgreader->Delete();
  names->Delete();
  xmlreader->Delete();
  datareader->Delete();
  gdcmreader->Delete();
#if VTK_MAJOR_VERSION >= 6 || (VTK_MAJOR_VERSION >= 5 && VTK_MINOR_VERSION > 0)
  dicomreader->Delete();
#endif

  return retcode;
}
