
/**************************************************************************
THIS IS A MODIFIED VERSION OF THE original vtkTIFFWriter class.
This version adds 23-bit floating point pixel support.

-Maik
***************************************************************************/
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkTIFFWriterX.cxx,v $

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkTIFFWriterX.h"

#include "vtkErrorCode.h"
#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "tiff.h"
#include "tiffio.h"

vtkStandardNewMacro(vtkTIFFWriterX);

//----------------------------------------------------------------------------
vtkTIFFWriterX::vtkTIFFWriterX() 
{
  this->TIFFPtr = 0;
  this->Compression = vtkTIFFWriterX::PackBits;
};


class vtkTIFFWriterXIO
{
public:
  // Writing file no reading
  static tsize_t TIFFRead(thandle_t, tdata_t, tsize_t) { return 0; }

  // Write data
  static tsize_t TIFFWrite(thandle_t fd, tdata_t buf, tsize_t size) 
    {
    ostream *out = reinterpret_cast<ostream *>(fd);
    out->write(static_cast<char *>(buf), size);
    return out->fail() ? static_cast<tsize_t>(0) : size;
    }

  static toff_t TIFFSeek(thandle_t fd, toff_t off, int whence) 
    {
    ostream *out = reinterpret_cast<ostream *>(fd);
    switch (whence) 
      {
      case SEEK_SET:
        out->seekp(off, ios::beg);
        break;
      case SEEK_END:
        out->seekp(off, ios::end);
        break;
      case SEEK_CUR:
        out->seekp(off, ios::cur);
        break;
      default:
        return out->tellp();
      }
    return out->tellp();
    }

  // File will be closed by the superclass
  static int TIFFClose(thandle_t) { return 1; }

  static toff_t TIFFSize(thandle_t fd) 
    {
    ostream *out = reinterpret_cast<ostream *>(fd);
    out->seekp(0, ios::end);
    return out->tellp();
    }

  static int TIFFMapFile(thandle_t, tdata_t*, toff_t*) { return (0); }
  static void TIFFUnmapFile(thandle_t, tdata_t, toff_t) {}
};

//----------------------------------------------------------------------------
void vtkTIFFWriterX::WriteFileHeader(ofstream *file, vtkImageData *data)
{
  int dims[3];
  int width, height;
  data->GetDimensions(dims);
  int scomponents = data->GetNumberOfScalarComponents();
  int stype = data->GetScalarType();
  double resolution = -1;
  uint32 rowsperstrip = (uint32) -1;

  int min0, min1, max0, max1, min2, max2;


  int bps;
  switch (stype)
    {
  case VTK_CHAR:
  case VTK_UNSIGNED_CHAR:
    bps = 8;
    break;
  case VTK_SHORT:
  case VTK_UNSIGNED_SHORT:
    bps = 16;
    break;
	case VTK_FLOAT:
		bps = 32;
		break;
  default:
    vtkErrorMacro(<< "Unsupported data type: " << data->GetScalarTypeAsString());
    this->SetErrorCode(vtkErrorCode::FileFormatError);
    return;
    }

  int predictor;
  ostream* ost = file;

  // Find the length of the rows to write.
  data->GetWholeExtent(min0, max0, min1, max1, min2, max2);
  width = (max0 - min0 + 1);
  height = (max1 - min1 + 1);

  TIFF* tif = TIFFClientOpen(this->GetFileName(), "w",
    (thandle_t) ost,
    reinterpret_cast<TIFFReadWriteProc>(vtkTIFFWriterXIO::TIFFRead), 
    reinterpret_cast<TIFFReadWriteProc>(vtkTIFFWriterXIO::TIFFWrite),
    reinterpret_cast<TIFFSeekProc>(vtkTIFFWriterXIO::TIFFSeek),
    reinterpret_cast<TIFFCloseProc>(vtkTIFFWriterXIO::TIFFClose), 
    reinterpret_cast<TIFFSizeProc>(vtkTIFFWriterXIO::TIFFSize),
    reinterpret_cast<TIFFMapFileProc>(vtkTIFFWriterXIO::TIFFMapFile), 
    reinterpret_cast<TIFFUnmapFileProc>(vtkTIFFWriterXIO::TIFFUnmapFile)
    );
  if ( !tif )
    {
    this->TIFFPtr = 0;
    return;
    }
  this->TIFFPtr = tif;

  uint32 w = width;
  uint32 h = height;
  TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
  TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, scomponents);
  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); // Fix for stype
  TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	if(stype == VTK_FLOAT) {
		TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT,  SAMPLEFORMAT_IEEEFP);
	}

  if ( scomponents > 3 )
    {
    // if number of scalar components is greater than 3, that means we assume
    // there is alpha.
    uint16 extra_samples = scomponents-3;
    uint16 *sample_info = new uint16[scomponents-3];
    sample_info[0]=EXTRASAMPLE_ASSOCALPHA;
    int cc;
    for ( cc = 1; cc < scomponents-3; cc ++ )
      {
      sample_info[cc] = EXTRASAMPLE_UNSPECIFIED;
      }
    TIFFSetField(tif,TIFFTAG_EXTRASAMPLES,extra_samples,
      sample_info);
    delete [] sample_info;
    }

  int compression;
  switch ( this->Compression )
    {
  case vtkTIFFWriterX::PackBits: compression = COMPRESSION_PACKBITS; break;
  case vtkTIFFWriterX::JPEG:     compression = COMPRESSION_JPEG; break;
  case vtkTIFFWriterX::Deflate:  compression = COMPRESSION_DEFLATE; break;
  case vtkTIFFWriterX::LZW:      compression = COMPRESSION_LZW; break;
  default: compression = COMPRESSION_NONE;
    }
  //compression = COMPRESSION_JPEG;
  TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); // Fix for compression
	uint16 photometric = stype==VTK_FLOAT?PHOTOMETRIC_MINISBLACK :PHOTOMETRIC_RGB;
  if ( compression == COMPRESSION_JPEG )
    {
    TIFFSetField(tif, TIFFTAG_JPEGQUALITY, 75); // Parameter
    TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB);
    photometric = PHOTOMETRIC_YCBCR;
    }
  else if ( compression == COMPRESSION_LZW )
    {
    predictor = 2;
    TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
    vtkErrorMacro("LZW compression is patented outside US so it is disabled");
    }
  else if ( compression == COMPRESSION_DEFLATE )
    {
    predictor = 2;
    TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
    }

  TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric); // Fix for scomponents
  TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,
    TIFFDefaultStripSize(tif, rowsperstrip));
  if (resolution > 0) {
    TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution);
    TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution);
    TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
  }
}

/*static unsigned int convertToLittleEndian(unsigned int in) {
  if (in == 0) {
    return 0;
  } else {
    unsigned int rt;
    char *inptr;
    inptr = (char *) &rt;
    inptr[0] = (char) (in >> 24);
    inptr[1] = (char) (in >> 16);
    inptr[2] = (char) (in >> 8);
    inptr[3] = (char) (in);
    return rt;
  }
}*/

template <typename T>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, T*ptr, int *extent) {
	int idx1, idx2;
	int row = 0;
	
  for (idx2 = extent[4]; idx2 <= extent[5]; ++idx2)
    {
    for (idx1 = extent[3]; idx1 >= extent[2]; idx1--)
      {
      ptr = (T*) data->GetScalarPointer(extent[0], idx1, idx2);
			unsigned char * _ptr = (unsigned char*)(ptr);
      if ( TIFFWriteScanline(tif, ptr, row, 0) < 0)
        {
        //this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
				//cout << "Error...it broke!!" << endl;
        //break;
				return vtkErrorCode::OutOfDiskSpaceError;
        }
      row ++;
      }
    }
		return vtkErrorCode::NoError;
}

template<typename T, int N>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFileNBytes(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, T*ptr, int *extent) {
	int idx1, idx2, idx0;
	int row = 0;
	unsigned char* buffer = new unsigned char[N *(extent[1] - extent[0] + 1)];
	
  for (idx2 = extent[4]; idx2 <= extent[5]; ++idx2)
    {
    for (idx1 = extent[3]; idx1 >= extent[2]; idx1--)
      {
      //ptr = data->GetScalarPointer(extent[0], idx1, idx2);
			int i=0;
			unsigned char *bufPtr = buffer;
			for (idx0=extent[0]; idx0<= extent[1];idx0++,i++) {
				unsigned char *pixel = (unsigned char*) data->GetScalarPointer(idx0, idx1, idx2);
				for(unsigned short int byte_index =0;byte_index < N; byte_index++) {
					*bufPtr++ = pixel[byte_index];
				}
			}
			//int nbytes = sizeof(T);
		
      if ( TIFFWriteScanline(tif, buffer, row, 0) < 0)
        {
        return vtkErrorCode::OutOfDiskSpaceError;
				
        }
      row ++;
      }
    }
	delete [] buffer;
	return vtkErrorCode::NoError;
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, float*ptr, int *extent) {
	return vtkTIFFWriterXWriteFileNBytes<float,4>(self, tif, data, ptr, extent);
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, double *ptr, int *extent) {
	return vtkTIFFWriterXWriteFileNBytes<double,8>(self, tif, data, ptr, extent);
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, int*ptr, int *extent) {
	switch(sizeof(int)) {
		case 4:
			vtkTIFFWriterXWriteFileNBytes<int,4>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		case 8:
			vtkTIFFWriterXWriteFileNBytes<int,8>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		default:
			return vtkErrorCode::UnknownError;
			//cout << "all hell breaks loose" << endl;
	}
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, short*ptr, int *extent) {
	switch(sizeof(short)) {
		case 2:
			vtkTIFFWriterXWriteFileNBytes<short,2>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		case 4:
			vtkTIFFWriterXWriteFileNBytes<short,4>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		default:
			return vtkErrorCode::UnknownError;
			//cout << "all hell breaks loose" << endl;
	}
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, unsigned int*ptr, int *extent) {
	switch(sizeof(unsigned int)) {
		case 2:
			vtkTIFFWriterXWriteFileNBytes<unsigned int,2>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		case 4:
			vtkTIFFWriterXWriteFileNBytes<unsigned int,4>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		default:
			return vtkErrorCode::UnknownError;
			//cout << "all hell breaks loose" << endl;
	}
}

template <>
vtkErrorCode::ErrorIds vtkTIFFWriterXWriteFile(vtkTIFFWriterX *self, TIFF *tif, vtkImageData *data, unsigned short*ptr, int *extent) {
	switch(sizeof(unsigned short)) {
		case 2:
			vtkTIFFWriterXWriteFileNBytes<unsigned short,2>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		case 4:
			vtkTIFFWriterXWriteFileNBytes<unsigned short,4>(self, tif, data, ptr, extent);
			return vtkErrorCode::NoError;
		default:
			return vtkErrorCode::UnknownError;
			//cout << "all hell breaks loose" << endl;
	}
}

//----------------------------------------------------------------------------
void vtkTIFFWriterX::WriteFile(ofstream *, vtkImageData *data,
  int extent[6])
{
  /*int idx1, idx2;
  void *ptr;*/

  // Make sure we actually have data.
  if ( !data->GetPointData()->GetScalars())
    {
    vtkErrorMacro(<< "Could not get data from input.");
    return;
    }

  TIFF* tif = reinterpret_cast<TIFF*>(this->TIFFPtr);
  if ( !tif )
    {
    vtkErrorMacro("Problem writting trailer.");
    this->SetErrorCode(vtkErrorCode::FileFormatError);
    return;
    }

		int deleteme = data->GetScalarType();
  // take into consideration the scalar type
  if( data->GetScalarType() != VTK_UNSIGNED_CHAR 
	 && data->GetScalarType() != VTK_FLOAT
	 )
    {
    vtkErrorMacro("TIFFWriter only accepts unsigned char scalars and floats!");
    return; 
    }
		
		vtkErrorCode::ErrorIds erno;
		switch(data->GetScalarType()) {
			vtkTemplateMacro(erno = vtkTIFFWriterXWriteFile(this, tif, data, (VTK_TT*) data->GetScalarPointer(), extent);this->SetErrorCode(erno));
			default:
				vtkErrorMacro(<< "Unknown scalar type " << data->GetScalarType());
		}
		
}

//----------------------------------------------------------------------------
void vtkTIFFWriterX::WriteFileTrailer(ofstream *, vtkImageData *)
{
  TIFF* tif = reinterpret_cast<TIFF*>(this->TIFFPtr);
  if ( !tif )
    {
    vtkErrorMacro("Problem writting trailer.");
    this->SetErrorCode(vtkErrorCode::FileFormatError);
    }
  TIFFClose(tif);
  this->TIFFPtr = 0;
}
//----------------------------------------------------------------------------
void vtkTIFFWriterX::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Compression: ";
  if ( this->Compression == vtkTIFFWriterX::PackBits )
    {
    os << "Pack Bits\n";
    }
  else if ( this->Compression == vtkTIFFWriterX::JPEG )
    {
    os << "JPEG\n";
    }
  else if ( this->Compression == vtkTIFFWriterX::Deflate )
    {
    os << "Deflate\n";
    }
  else if ( this->Compression == vtkTIFFWriterX::LZW )
    {
    os << "LZW\n";
    }
  else //if ( this->Compression == vtkTIFFWriterX::NoCompression )
    {
    os << "No Compression\n";
    }
}
