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

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.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 "vtkSTKReader.h"

#include "vtkObjectFactory.h"
#include "vtkByteSwap.h"
#include "vtkImageData.h"

#include <stdio.h>
#include <sys/stat.h>

#if(!defined(TIFFTAG_UIC2TAG))
  #define TIFFTAG_UIC2TAG 33629
#endif

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkSTKReader);
vtkCxxRevisionMacro(vtkSTKReader, "$Revision: 1.13 $");

extern "C" {
  void vtkSTKReaderInternalErrorHandler(const char* vtkNotUsed(module), 
                                        const char* vtkNotUsed(fmt), 
                                        va_list vtkNotUsed(ap))
  {
    // Do nothing
    // Ignore errors
  }
}
vtkSTKReader::vtkSTKReader() 
{
  this->Image = 0;
  this->Units = 0;

  this->OriginSpecifiedFlag = false;
  this->SpacingSpecifiedFlag = false;
}

vtkSTKReader::~vtkSTKReader() 
{
  this->Clean();
  this->SetUnits(0);
}
 
int vtkSTKReader::Open( const char *filename )
{
  this->Clean();
  struct stat fs;
  if ( stat(filename, &fs) )
    {
    return 0;
    }
  this->Image = TIFFOpen(filename, "r");
  if ( !this->Image)
    {
    this->Clean();
    return 0;
    }
  
  TIFFSetErrorHandler(&vtkSTKReaderInternalErrorHandler);
  TIFFSetWarningHandler(&vtkSTKReaderInternalErrorHandler);

  return 1;
}

void vtkSTKReader::Clean()
{
  if ( this->Image )
    {
    TIFFClose(this->Image);
    }
  this->Image=NULL;
}

int vtkSTKReader::CanReadFile(const char* fname)
{
  int res = this->Open(fname);
  if (!res || !this->Image)
    {
    this->Clean();
    return 0;
    }
  
  TIFFSetDirectory(this->Image,0);
  unsigned long size;
  float *offset;
  // look for the zeiss structure
  if ( !TIFFGetField(this->Image, TIFFTAG_UIC2TAG, &size, &offset))
    {
    return 0;
    }
  
  this->Clean();
  return 3;
}

void vtkSTKReader::ExecuteInformation()
{
  this->ComputeInternalFileName(this->DataExtent[4]);
  if (this->InternalFileName == NULL)
    {
    return;
    }
  
  if ( !this->Open(this->InternalFileName) || !this->Image)
    {  
    vtkErrorMacro("Unable to open file " <<this->InternalFileName );
    this->DataExtent[0] = 0;
    this->DataExtent[1] = 0;
    this->DataExtent[2] = 0;
    this->DataExtent[3] = 0;
    this->DataExtent[4] = 0;
    this->DataExtent[5] = 0;
    this->SetNumberOfScalarComponents(1);
    this->vtkImageReader2::ExecuteInformation();
    return;
    }

  int width, height;
  short samplesPerPixel, bitsPerSample[3];
  short planarConfig;  
  char *units = 0;
  unsigned int i;

  // read in the uic structure
  unsigned long slices;
  float *offset;
  if ( !TIFFGetField(this->Image, TIFFTAG_UIC2TAG, &slices, &offset))
    {
    vtkErrorMacro("Not a valid metamorph stack file: " <<
                  this->InternalFileName );
    this->Clean();
    return;
    }

  // pull out the width/height, etc.
  if ( !TIFFGetField(this->Image, TIFFTAG_IMAGEWIDTH, &width) ||
       !TIFFGetField(this->Image, TIFFTAG_IMAGELENGTH, &height) )
    {
    vtkErrorMacro("This is not a valid metamorph STK file: " <<
                  this->InternalFileName );
    this->Clean();
    return;
    }
  this->DataExtent[0] = 0;
  this->DataExtent[1] = width - 1;
  this->DataExtent[2] = 0;
  this->DataExtent[3] = height - 1;
  this->DataExtent[4] = 0;
  this->DataExtent[5] = slices - 1;

  // convert spacing from meters to micro µm
#ifdef WIN32
  FILE *fin = fopen(this->GetFileName(),"rb");
#else
  FILE *fin = fopen(this->GetFileName(),"r");
#endif
  if (!fin)
    {
    vtkGenericWarningMacro("bad STK file");
    return;
    }

  fseek(fin,4,0);
  unsigned long loc;
  fread(&loc,4,1,fin);
  vtkByteSwap::Swap4LE(&loc);
  fseek(fin,loc,0);
  
  //load IFD entries into memory
  unsigned short numentries;
  fread(&numentries,2,1,fin);
  vtkByteSwap::Swap2LE(&numentries);
  loc+=2;
  int e;
  unsigned short tag;
  unsigned long toffset;
  long *uic2Data = new long [slices*6];
  float xspacing, yspacing, zspacing;
  xspacing = -0.125;
  yspacing = -0.125;
  zspacing = -0.125;  
  long num, den;
  for (e=0;e<numentries;e++)
    {
    fseek(fin,loc+12*e,0);
    fread(&tag,2,1,fin);
    vtkByteSwap::Swap2LE(&tag);
    switch (tag)
      {
      case TIFFTAG_UIC2TAG:
        // read in the data 
        fseek(fin,loc+12*e+8,0);
        fread(&toffset,4,1,fin);
        vtkByteSwap::Swap4LE(&toffset);
        fseek(fin,toffset,0);
        fread(uic2Data,4,6*slices,fin);
        vtkByteSwap::Swap4LERange(uic2Data,6*slices);
        //compute average z spacing
        zspacing = 0;
        for (i = 0; i < slices; ++i)
          {
          zspacing = zspacing + (uic2Data[i*6]/(float)uic2Data[i*6+1]);
          }
        zspacing /= slices;
        break;
      case 33628 /* uic1tag */:
        //iterate through table pointed to by UIC1TAG to get X and Y spacing
        unsigned short ttype;
        fread(&ttype,2,1,fin);        
        vtkByteSwap::Swap2LE(&ttype);
        if (ttype == TIFF_LONG)
          {
          unsigned long tlength;
          fread(&tlength,4,1,fin);
          vtkByteSwap::Swap4LE(&tlength);
          fseek(fin,loc+12*e+8,0);
          fread(&toffset,4,1,fin);
          vtkByteSwap::Swap4LE(&toffset);
          // read the structure
          fseek(fin,toffset,0);
          unsigned long code;
          unsigned long codeOffset;
          for (unsigned long j = 0; j < tlength; j++) 
            {
            fread(&code,4,1,fin);
            vtkByteSwap::Swap4LE(&code);
            switch (code)
              {
              case 3:
                fread(&num,4,1,fin);
                vtkByteSwap::Swap4LE(&num);
                //then spacing info is not listed, so forget it
                if (num == 0) 
                  {
                  j = tlength;
                  }
                break;
              case 4: 
                //then the X spacing is where it should be
                fread(&codeOffset,4,1,fin);
                vtkByteSwap::Swap4LE(&codeOffset);
                toffset = ftell(fin);
                fseek(fin,codeOffset,0);
                fread(&num,4,1,fin);
                vtkByteSwap::Swap4LE(&num);
                fread(&den,4,1,fin);
                vtkByteSwap::Swap4LE(&den);
                fseek(fin,toffset,0);
                xspacing = num/(float)den;
                break;
              case 5:
                //then the Y spacing is where it should be
                fread(&codeOffset,4,1,fin);
                vtkByteSwap::Swap4LE(&codeOffset);
                toffset = ftell(fin);
                fseek(fin,codeOffset,0);
                fread(&num,4,1,fin);
                vtkByteSwap::Swap4LE(&num);
                fread(&den,4,1,fin);
                vtkByteSwap::Swap4LE(&den);
                fseek(fin,toffset,0);
                yspacing = num/(float)den;
                break;
              case 6:
                // read the units string
                fread(&codeOffset,4,1,fin);
                vtkByteSwap::Swap4LE(&codeOffset);
                toffset = ftell(fin);
                fseek(fin,codeOffset,0);
                fread(&num,4,1,fin);
                vtkByteSwap::Swap4LE(&num);
                units = new char [num+1];
                fread(units,num,1,fin);
                units[num] = '\0';
                this->SetUnits(units);
                delete [] units;
                units = 0;
                fseek(fin,toffset,0);
                break;
              }
            }
          }
      }
    }
  
  fclose (fin);
  delete [] uic2Data;

  // set the spacing
  if( !SpacingSpecifiedFlag )
  {
    this->SetDataSpacing(xspacing, yspacing, zspacing);
  }

  // read number of components
  samplesPerPixel = 1;
  TIFFGetField(this->Image, TIFFTAG_SAMPLESPERPIXEL,&samplesPerPixel);
  if (samplesPerPixel > 4)
    {
    vtkErrorMacro("VolView only supports up to four channels and this file contains " << samplesPerPixel << " channels. VolView will read in the first four channels only.");
    samplesPerPixel = 4;
    }
  this->SetNumberOfScalarComponents(samplesPerPixel);
  
  // read in data type
  TIFFGetField(this->Image, TIFFTAG_BITSPERSAMPLE, bitsPerSample);
  switch (bitsPerSample[0])
    {
    case 8:
      this->SetDataScalarTypeToUnsignedChar();
      break;
    case 16:
      this->SetDataScalarTypeToUnsignedShort();
      break;
    default:
      vtkErrorMacro(
        "Bad pixel type for Metamorph STK file");
      this->Clean();
      return;
    }

  planarConfig = 1;
  TIFFGetField(this->Image, TIFFTAG_PLANARCONFIG, &planarConfig);
  if (planarConfig != 1)
    {
    vtkErrorMacro(
      "VolView does not support STK files with channel seperate polanar configuraitons");
    this->Clean();
    return;
    }

  short compression;
  TIFFGetField(this->Image, TIFFTAG_COMPRESSION, &compression);
  
  if (compression != 1)
    {
    vtkErrorMacro(
      "VolView does not support STK files with LSW compression");
    this->Clean();
    return;
    }
  
  this->vtkImageReader2::ExecuteInformation();

  // close the file
  this->Clean();
}

template <class OT>
void vtkSTKReaderUpdate(vtkSTKReader *self, vtkImageData *data, OT *outPtr, TIFF *Image)
{
  vtkIdType outIncr[3];
  int outExtent[6];
  OT *outPtr2;
  
  data->GetExtent(outExtent);
  data->GetIncrements(outIncr);
  int *wExt = data->GetWholeExtent();
  
  //unsigned int numComps = data->GetNumberOfScalarComponents();
  //unsigned int pixSize = numComps*sizeof(OT);  
  unsigned int *sbc = NULL;
  unsigned int *stoff = NULL;
  
  int numStrips = TIFFNumberOfStrips(Image);
  int stripsPerImage = numStrips /* /(wExt[5] - wExt[4] + 1) */;
  TIFFGetField( Image, TIFFTAG_STRIPBYTECOUNTS, &sbc );
  TIFFGetField( Image, TIFFTAG_STRIPOFFSETS, &stoff );

  // read the data ourselves yuck
#ifdef WIN32
  FILE *fin = fopen(self->GetFileName(),"rb");
#else
  FILE *fin = fopen(self->GetFileName(),"r");
#endif
  if (!fin)
    {
    vtkGenericWarningMacro("bad STK file");
    return;
    }
  
  outPtr2 = outPtr;
  int idx2;
  int i;
  size_t bytes;
  for (idx2 = outExtent[4]; idx2 <= outExtent[5]; ++idx2)
    {
    // read in a slice 
    int currStrip = 0;
    fseek(fin, 
          (idx2 - wExt[4])*
          (stoff[stripsPerImage-1] + sbc[stripsPerImage-1] - stoff[0])+stoff[0], 
          SEEK_SET);
    for (i = 0; i < stripsPerImage; ++i)
      {
      bytes = fread(outPtr2,1, sbc[currStrip],fin);
      if( bytes == 0)
        {
        vtkGenericWarningMacro("Read Failure in STK Reader");
        return;
        }
      currStrip++;
      if (sizeof(OT) == 2)
        {
        vtkByteSwap::Swap2LERange((char *)outPtr2,bytes/2);
        }
      outPtr2 += (bytes/sizeof(OT));
      }
    self->UpdateProgress((idx2 - outExtent[4])/
                         (outExtent[5] - outExtent[4] + 1.0));
    } 
  fclose(fin);
}

//----------------------------------------------------------------------------
// This function reads a data from a file.  The datas extent/axes
// are assumed to be the same as the file extent/order.
void vtkSTKReader::ExecuteData(vtkDataObject *output)
{
  vtkImageData *data = this->AllocateOutputData(output);

  if (this->InternalFileName == NULL)
    {
    vtkErrorMacro("Either a FileName or FilePrefix must be specified.");
    return;
    }

  this->ComputeDataIncrements();
  
  // Call the correct templated function for the output
  void *outPtr;

  int res = this->Open(this->InternalFileName);
  if (!res || !this->Image)
    {
    this->Clean();
    return;
    }

  // Call the correct templated function for the input
  outPtr = data->GetScalarPointer();
  switch (data->GetScalarType())
    {
    vtkTemplateMacro(vtkSTKReaderUpdate(this, data, (VTK_TT *)(outPtr), this->Image));
    default:
      vtkErrorMacro("UpdateFromFile: Unknown data type");
    }   

  this->Clean();
}

//----------------------------------------------------------------------------
void vtkSTKReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  
  os << indent << "Units: " << (this->Units ? this->Units : "None") << endl;
  os << indent << "OriginSpecifiedFlag: " << this->OriginSpecifiedFlag << endl;
  os << indent << "SpacingSpecifiedFlag: " << this->SpacingSpecifiedFlag << endl;
}

