/*****************************************************************************
                           jpegdatasource.c
******************************************************************************
   This is the data source manager that Jpegtopnm supplies to the JPEG
   library.  The Jpeg library uses this object to get JPEG input.

   This data source manager is the same as the Jpeg library's built in
   "stdio" one, except that it looks ahead and one can query it to see
   if there is any data in the stream that the Jpeg library hasn't seen
   yet.  Thus, you can use it in a program that reads multiple JPEG
   images and know when to stop.  You can also nicely handle completely
   empty input files more gracefully than just crying input error.

   This data source manager does 4K fread() reads and passes 4K buffers
   to the Jpeg library.  It reads one 4K block ahead, so there is up to
   8K of image buffered at any time.

   By Bryan Henderson, San Jose CA 2002.10.13
*****************************************************************************/

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
   itself, but doesn't.
*/
#include <jpeglib.h>

#include "pm_c_util.h"
#include "mallocvar.h"
#include "pnm.h"

#include "jpegdatasource.h"

#define BUFFER_SIZE 4096

struct sourceManager {
    /* The following structure _must_ be the first thing in this structure,
       because the methods below assume the address of the struct
       jpeg_source_mgr is the same as the address of the struct sourceManager.
       The former address is the only information the Jpeg library gives
       these methods to tell them on what source manager object they are
       supposed to operate.
    */
    struct jpeg_source_mgr jpegSourceMgr;
    FILE * ifP;
    bool prematureEof;
        /* We have been asked for data and were unable to comply because
           the file had no more to give (so we supplied EOI markers
           instead).
        */
    JOCTET * currentBuffer;
    JOCTET * nextBuffer;
    unsigned int bytesInNextBuffer;
    JOCTET buffer1[BUFFER_SIZE];
    JOCTET buffer2[BUFFER_SIZE];
};


static void
dsInitSource(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the init_source method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    /* The object was created ready to go and remains ready from one image
       to the next.  No per-image initialization is necessary.
    */
}



static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI};
    /* An EOI (end of image) marker */


static boolean
dsFillInputBuffer(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the fill_input_buffer method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;

    if (srcP->bytesInNextBuffer == 0) {
        /* The decompressor expects more bytes, but there aren't any, so
           the file is corrupted -- probably truncated.  We want the
           decompressor to decompress whatever it's read so far, so we
           synthesize an EOI marker here, but we also set error state
           in the source manager.  The decompressor will recognize the
           truncation and pad out the image with gray.
        */
        srcP->prematureEof = TRUE;

        srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker;
        srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker);
    } else {
        /* Rotate the buffers */
        srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
        srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
        {
            JOCTET * tmp;
            tmp = srcP->nextBuffer;
            srcP->nextBuffer = srcP->currentBuffer;
            srcP->currentBuffer = tmp;
        }

        /* Fill the new "next" buffer */
        srcP->bytesInNextBuffer =
            fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
    }
    return TRUE;
}



static void
dsSkipInputData(j_decompress_ptr const cinfoP, long const num_bytes) {
/*----------------------------------------------------------------------------
   This is the skip_input_data method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    struct jpeg_source_mgr * const jpegSourceMgrP = cinfoP->src;

    long i;

    for (i = 0; i < num_bytes; ++i) {
        if (jpegSourceMgrP->bytes_in_buffer == 0)
            dsFillInputBuffer(cinfoP);
        ++jpegSourceMgrP->next_input_byte;
        --jpegSourceMgrP->bytes_in_buffer;
    }
}



static void
dsTermSource(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the term_source method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    /* We couldn't care less that the Jpeg library is done reading an
       image.  The source manager object remains active and ready for the
       Jpeg library to read the next image.
    */
}



bool
dsDataLeft(struct sourceManager * const srcP) {

    return((srcP->jpegSourceMgr.bytes_in_buffer > 0 ||
            srcP->bytesInNextBuffer > 0));
}



bool
dsPrematureEof(struct sourceManager * const srcP) {

    return srcP->prematureEof;
}



struct sourceManager *
dsCreateSource(const char * const fileName) {

    struct sourceManager * srcP;

    MALLOCVAR(srcP);
    if (srcP == NULL)
        pm_error("Unable to get memory for the Jpeg library source manager.");

    srcP->ifP = pm_openr(fileName);

    srcP->jpegSourceMgr.init_source = dsInitSource;
    srcP->jpegSourceMgr.fill_input_buffer = dsFillInputBuffer;
    srcP->jpegSourceMgr.skip_input_data = dsSkipInputData;
    srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
    srcP->jpegSourceMgr.term_source = dsTermSource;

    srcP->prematureEof = FALSE;
    srcP->currentBuffer = srcP->buffer1;
    srcP->nextBuffer = srcP->buffer2;
    srcP->jpegSourceMgr.bytes_in_buffer =
        fread(srcP->currentBuffer, 1, BUFFER_SIZE, srcP->ifP);
    srcP->jpegSourceMgr.next_input_byte = srcP->currentBuffer;
    srcP->bytesInNextBuffer =
        fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);

    return srcP;
}



void
dsDestroySource(struct sourceManager * const srcP) {

    pm_close(srcP->ifP);
    free(srcP);

}



struct jpeg_source_mgr *
dsJpegSourceMgr(struct sourceManager * const srcP) {
    return &srcP->jpegSourceMgr;
}



