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

  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 "vtkJPEG12Reader.h"

#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkUnsignedCharArray.h"

extern "C" {
#include <jpeglib.h>
#include <setjmp.h>
}


vtkCxxRevisionMacro(vtkJPEG12Reader, "$Revision: 1.7 $");
vtkStandardNewMacro(vtkJPEG12Reader);

vtkCxxSetObjectMacro(vtkJPEG12Reader, MemoryBuffer, vtkUnsignedCharArray);

  
// create an error handler for jpeg that
// can longjmp out of the jpeg library 
struct vtk_jpeg12_error_mgr 
{
  struct jpeg_error_mgr pub;    /* "public" fields */
  jmp_buf setjmp_buffer;        /* for return to caller */
  vtkJPEG12Reader* JPEG12Reader;
};

// this is called on jpeg error conditions
extern "C" void vtk_jpeg12_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  vtk_jpeg12_error_mgr * err = 
    reinterpret_cast<vtk_jpeg12_error_mgr*>(cinfo->err);

  /* Return control to the setjmp point */
  longjmp(err->setjmp_buffer, 1);
}

extern "C" void vtk_jpeg12_output_message (j_common_ptr cinfo)
{
  char buffer[JMSG_LENGTH_MAX];

  /* Create the message */
  (*cinfo->err->format_message) (cinfo, buffer);
  vtk_jpeg12_error_mgr * err = 
    reinterpret_cast<vtk_jpeg12_error_mgr*>(cinfo->err);
  vtkWarningWithObjectMacro(err->JPEG12Reader,
                            "libjpeg error: " <<  buffer);
}

typedef struct {
  struct jpeg_source_mgr pub;   /* public fields */
  unsigned char *InputData;
  size_t         InputSize;
} vtk_jpeg12_source_mgr;

/*
 * Initialize source --- called by jpeg_read_header
 * before any data is actually read.
 */
METHODDEF(void) vtk_jpeg12_init_source (j_decompress_ptr )
{
}


/*
 * Fill the input buffer --- called whenever buffer is emptied.
 *
 * In typical applications, this should read fresh data into the buffer
 * (ignoring the current state of next_input_byte & bytes_in_buffer),
 * reset the pointer & count to the start of the buffer, and return TRUE
 * indicating that the buffer has been reloaded.  It is not necessary to
 * fill the buffer entirely, only to obtain at least one more byte.
 *
 * There is no such thing as an EOF return.  If the end of the file has been
 * reached, the routine has a choice of ERREXIT() or inserting fake data into
 * the buffer.  In most cases, generating a warning message and inserting a
 * fake EOI marker is the best course of action --- this will allow the
 * decompressor to output however much of the image is there.  However,
 * the resulting error message is misleading if the real problem is an empty
 * input file, so we handle that case specially.
 *
 * In applications that need to be able to suspend compression due to input
 * not being available yet, a FALSE return indicates that no more data can be
 * obtained right now, but more may be forthcoming later.  In this situation,
 * the decompressor will return to its caller (with an indication of the
 * number of scanlines it has read, if any).  The application should resume
 * decompression after it has loaded more data into the input buffer.  Note
 * that there are substantial restrictions on the use of suspension --- see
 * the documentation.
 *
 * When suspending, the decompressor will back up to a convenient restart point
 * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
 * indicate where the restart point will be if the current call returns FALSE.
 * Data beyond this point must be rescanned after resumption, so move it to
 * the front of the buffer rather than discarding it.
 */
METHODDEF(boolean) vtk_jpeg12_fill_input_buffer (j_decompress_ptr cinfo)
{
  vtk_jpeg12_source_mgr *src = (vtk_jpeg12_source_mgr *) cinfo->src;

  src->pub.next_input_byte = src->InputData;
  src->pub.bytes_in_buffer = src->InputSize;

  return TRUE;
}


/*
 * Skip data --- used to skip over a potentially large amount of
 * uninteresting data (such as an APPn marker).
 *
 * Writers of suspendable-input applications must note that skip_input_data
 * is not granted the right to give a suspension return.  If the skip extends
 * beyond the data currently in the buffer, the buffer can be marked empty so
 * that the next read will cause a fill_input_buffer call that can suspend.
 * Arranging for additional bytes to be discarded before reloading the input
 * buffer is the application writer's problem.
 */

METHODDEF(void) vtk_jpeg12_skip_input_data (j_decompress_ptr cinfo, 
                                            long num_bytes)
{
  vtk_jpeg12_source_mgr *src = (vtk_jpeg12_source_mgr *) cinfo->src;
  src->pub.next_input_byte += (size_t) num_bytes;
  src->pub.bytes_in_buffer -= (size_t) num_bytes;
}


/*
 * Terminate source --- called by jpeg_finish_decompress
 * after all data has been read.  Often a no-op.
 *
 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
 * application must deal with any cleanup that should happen even
 * for error exit.
 */
METHODDEF(void) vtk_jpeg12_term_source (j_decompress_ptr )
{
}

GLOBAL(void) vtk_jpeg12_memory_src (j_decompress_ptr cinfo, 
                                    unsigned char *buff,
                                    size_t size)
{
  vtk_jpeg12_source_mgr *src;

  if (cinfo->src == NULL) 
    { 
    /* first time for this JPEG object? */
    cinfo->src = (struct jpeg_source_mgr *)
      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
                                  sizeof(vtk_jpeg12_source_mgr));
    }

  src = (vtk_jpeg12_source_mgr *) cinfo->src;
  src->pub.init_source = vtk_jpeg12_init_source;
  src->pub.fill_input_buffer = vtk_jpeg12_fill_input_buffer;
  src->pub.skip_input_data = vtk_jpeg12_skip_input_data;
  src->pub.resync_to_restart = jpeg_resync_to_restart; 
  src->pub.term_source = vtk_jpeg12_term_source;
  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
  src->pub.next_input_byte = NULL; /* until buffer loaded */
  src->InputData = buff;
  src->InputSize = size;
}


#ifdef _MSC_VER
// Let us get rid of this funny warning on /W4:
// warning C4611: interaction between '_setjmp' and C++ object 
// destruction is non-portable
#pragma warning( disable : 4611 )
#endif 

void vtkJPEG12Reader::ExecuteInformation()
{
  FILE *fp = 0;
  if (!this->MemoryBuffer)
    {
    this->ComputeInternalFileName(this->DataExtent[4]);
    if (this->InternalFileName == NULL)
      {
      return;
      }
    
    fp = fopen(this->InternalFileName, "rb");
    if (!fp)
      {
      vtkErrorWithObjectMacro(this, 
                              "Unable to open file " 
                              << this->InternalFileName);
      return;
      }
    }
  
  // create jpeg decompression object and error handler
  struct jpeg_decompress_struct cinfo;
  struct vtk_jpeg12_error_mgr jerr;
  jerr.JPEG12Reader = this;

  cinfo.err = jpeg_std_error(&jerr.pub); 
  // for any jpeg error call vtk_jpeg12_error_exit
  jerr.pub.error_exit = vtk_jpeg12_error_exit;
  // for any output message call vtk_jpeg12_output_message
  jerr.pub.output_message = vtk_jpeg12_output_message;
  if (setjmp(jerr.setjmp_buffer))
    {
    // clean up
    jpeg_destroy_decompress(&cinfo);
    // close the file
    if (fp)
      {
      fclose(fp);
      }
    // this is not a valid jpeg file 
    vtkErrorWithObjectMacro(this, "libjpeg could not read file: "
                            << this->InternalFileName);
    return;
    }
  jpeg_create_decompress(&cinfo);

  if (this->MemoryBuffer)
    {
    // set the source file
    vtk_jpeg12_memory_src(&cinfo, this->MemoryBuffer->GetPointer(0),
                          this->MemoryBuffer->GetNumberOfTuples());
    }
  else
    {
    // set the source file
    jpeg_stdio_src(&cinfo, fp);
    }
  
  // read the header
  jpeg_read_header(&cinfo, TRUE);

  // force the output image size to be calculated (we could have used
  // cinfo.image_height etc. but that would preclude using libjpeg's
  // ability to scale an image on input).
  jpeg_calc_output_dimensions(&cinfo);

  // pull out the width/height, etc.
  this->DataExtent[0] = 0;
  this->DataExtent[1] = cinfo.output_width - 1;
  this->DataExtent[2] = 0;
  this->DataExtent[3] = cinfo.output_height - 1;

  this->SetDataScalarTypeToUnsignedShort();
  this->SetNumberOfScalarComponents( cinfo.output_components );

  this->vtkImageReader2::ExecuteInformation();

  // close the file
  jpeg_destroy_decompress(&cinfo);
  if (fp)
    {
    fclose(fp);
    }
}

template <class OT>
void vtkJPEG12ReaderUpdate2(vtkJPEG12Reader *self, OT *outPtr,
                          int *outExt, vtkIdType *outInc, long)
{
  unsigned int ui;
  int i;

  FILE *fp = 0;
  if (!self->GetMemoryBuffer())
    {
    fp = fopen(self->GetInternalFileName(), "rb");
    if (!fp)
      {
      return;
      }
    }
  
  // create jpeg decompression object and error handler
  struct jpeg_decompress_struct cinfo;
  struct vtk_jpeg12_error_mgr jerr;
  jerr.JPEG12Reader = self;

  cinfo.err = jpeg_std_error(&jerr.pub);
  // for any jpeg error call vtk_jpeg12_error_exit
  jerr.pub.error_exit = vtk_jpeg12_error_exit;
  // for any output message call vtk_jpeg12_output_message
  jerr.pub.output_message = vtk_jpeg12_output_message;
  if (setjmp(jerr.setjmp_buffer))
    {
    // clean up
    jpeg_destroy_decompress(&cinfo);
    // close the file
    if (fp)
      {
      fclose(fp);
      }
    vtkErrorWithObjectMacro(self, "libjpeg could not read file: "
                            << self->GetInternalFileName());
    // this is not a valid jpeg file
    return;
    }
  jpeg_create_decompress(&cinfo);

  if (self->GetMemoryBuffer())
    {
    // set the source file
    vtk_jpeg12_memory_src(&cinfo, self->GetMemoryBuffer()->GetPointer(0),
                          self->GetMemoryBuffer()->GetNumberOfTuples());
    }
  else
    {
    // set the source file
    jpeg_stdio_src(&cinfo, fp);
    }

  // read the header
  jpeg_read_header(&cinfo, TRUE);

  // prepare to read the bulk data
  jpeg_start_decompress(&cinfo);

  
  int rowbytes = cinfo.output_components * cinfo.output_width;
  unsigned short *tempImage = new unsigned short [rowbytes*cinfo.output_height];
  JSAMPROW *row_pointers = new JSAMPROW [cinfo.output_height];
  for (ui = 0; ui < cinfo.output_height; ++ui)
    {
    row_pointers[ui] = (JSAMPROW)(tempImage + rowbytes*ui);
    }

  // read the bulk data
  unsigned int remainingRows = cinfo.output_height;
  while (cinfo.output_scanline < cinfo.output_height)
    {
    remainingRows = cinfo.output_height - cinfo.output_scanline;
    jpeg_read_scanlines(&cinfo, &row_pointers[cinfo.output_scanline],
                        remainingRows);
    }

  // finish the decompression step
  jpeg_finish_decompress(&cinfo);

  // destroy the decompression object
  jpeg_destroy_decompress(&cinfo);

  // copy the data into the outPtr
  OT *outPtr2;
  outPtr2 = outPtr;
  long outSize = 2*cinfo.output_components*(outExt[1] - outExt[0] + 1);
  for (i = outExt[2]; i <= outExt[3]; ++i)
    {
    memcpy(outPtr2,
           row_pointers[cinfo.output_height - i - 1]
           + outExt[0]*cinfo.output_components,
           outSize);
    outPtr2 += outInc[1];
    }
  delete [] tempImage;
  delete [] row_pointers;

  // close the file
  if (fp)
    {
    fclose(fp);
    }
}

//----------------------------------------------------------------------------
// This function reads in one data of data.
// templated to handle different data types.
template <class OT>
void vtkJPEG12ReaderUpdate(vtkJPEG12Reader *self, vtkImageData *data, OT *outPtr)
{
  vtkIdType outIncr[3];
  int outExtent[6];
  OT *outPtr2;

  data->GetExtent(outExtent);
  data->GetIncrements(outIncr);

  long pixSize = data->GetNumberOfScalarComponents()*sizeof(OT);  
  
  outPtr2 = outPtr;
  int idx2;
  for (idx2 = outExtent[4]; idx2 <= outExtent[5]; ++idx2)
    {
    self->ComputeInternalFileName(idx2);
    // read in a JPEG file
    vtkJPEG12ReaderUpdate2(self, outPtr2, outExtent, outIncr, pixSize);
    self->UpdateProgress((idx2 - outExtent[4])/
                         (outExtent[5] - outExtent[4] + 1.0));
    outPtr2 += outIncr[2];
    }
}


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

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

  this->ComputeDataIncrements();
  
  data->GetPointData()->GetScalars()->SetName("JPEGImage");

  // Call the correct templated function for the output
  void *outPtr;

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



int vtkJPEG12Reader::CanReadFile(const char* fname)
{
  // open the file
  FILE *fp = fopen(fname, "rb");
  if (!fp)
    {
    return 0;
    }
  // read the first two bytes
  char magic[2];
  int n = static_cast<int>(fread(magic, sizeof(magic), 1, fp));
  if (n != 1) 
    {
    fclose(fp);
    return 0;
    }
  // check for the magic stuff:
  // 0xFF followed by 0xD8
  if( ( (static_cast<unsigned char>(magic[0]) != 0xFF) || 
        (static_cast<unsigned char>(magic[1]) != 0xD8) ) )
    {
    fclose(fp);
    return 0;
    }
  // go back to the start of the file
  fseek(fp, 0, SEEK_SET);
  // magic number is ok, try and read the header
  struct vtk_jpeg12_error_mgr jerr;
  jerr.JPEG12Reader = this;
  struct jpeg_decompress_struct cinfo;
  cinfo.err = jpeg_std_error(&jerr.pub);
  // for any jpeg error call vtk_jpeg12_error_exit
  jerr.pub.error_exit = vtk_jpeg12_error_exit;
  // for any output message call vtk_jpeg12_error_exit
  jerr.pub.output_message = vtk_jpeg12_error_exit;
  jerr.pub.output_message = vtk_jpeg12_output_message;
  // set the jump point, if there is a jpeg error or warning
  // this will evaluate to true
  if (setjmp(jerr.setjmp_buffer))
    {
    // clean up
    jpeg_destroy_decompress(&cinfo);
    // close the file
    fclose(fp);
    // this is not a valid jpeg file
    return 0;
    }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);
  // set the source file
  jpeg_stdio_src(&cinfo, fp);
  /* Step 2: specify data source (eg, a file) */
  jpeg_stdio_src(&cinfo, fp);
  /* Step 3: read file parameters with jpeg_read_header() */
  jpeg_read_header(&cinfo, TRUE);
  
  // if no errors have occurred yet, then it must be jpeg
  jpeg_destroy_decompress(&cinfo);
  fclose(fp);
  return 3;
}
#ifdef _MSC_VER
// Put the warning back
#pragma warning( default : 4611 )
#endif 


//----------------------------------------------------------------------------
void vtkJPEG12Reader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  // do not print this->MemoryBuffer
}
