/*
jpeg: a libjpeg source manager
(c) Harmen van der Wal, 2006.
License: GPLv2
$Id: jpeg.c,v 1.2 2006/08/16 10:58:07 harmen Exp $
See http://www.harmwal.nl/pccam880/
*/

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include "jpeg.h"

#define DBG(fmt,args...)
//#define DBG(fmt,args...) fprintf(stderr, "%s %s (%d): "fmt, __FILE__, __FUNCTION__, __LINE__,##args)
#define WARN(fmt,args...) fprintf(stderr, "%s %s (%d): "fmt, __FILE__, __FUNCTION__, __LINE__,##args)


void init_source(j_decompress_ptr cinfo){
  DBG("\n");
}

boolean fill_input_buffer(j_decompress_ptr cinfo)
{
  struct my_source_mgr *src = (struct my_source_mgr *)cinfo->src;

  DBG("\n");

  int len = src->fill(&src->buf, sizeof(src->buf), src->arg);

  DBG("len=%d\n", len);
  
  if (len < 1) {
    // eoi marker
    src->buf[0] = 0xff;
    src->buf[1] = 0xd9;
    len = 2;
  }

  src->pub.next_input_byte = src->buf;
  src->pub.bytes_in_buffer = len;

  DBG("bytes_in_buffer=%d\n", src->pub.bytes_in_buffer);

  return TRUE;
}

// untested.
void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
  struct my_source_mgr *src = (struct my_source_mgr *)cinfo->src;

  if (num_bytes > 0) {
    while (num_bytes > (long) src->pub.bytes_in_buffer) {
      num_bytes -= (long) src->pub.bytes_in_buffer;
      (void) fill_input_buffer(cinfo);
    }
    src->pub.next_input_byte += (size_t) num_bytes;
    src->pub.bytes_in_buffer -= (size_t) num_bytes;
  }
}

void term_source (j_decompress_ptr cinfo){
  DBG("\n");
}

void jpeg_my_src (j_decompress_ptr cinfo,
		  jpeg_func_t fill, void *arg)
{
  struct my_source_mgr *src;

  DBG("\n");

  if (cinfo->src == NULL) {
    cinfo->src = (struct jpeg_source_mgr *)malloc(sizeof(struct my_source_mgr));
  }

  src = (struct my_source_mgr *)cinfo->src;
  src->pub.init_source = init_source;
  src->pub.fill_input_buffer = fill_input_buffer;
  src->pub.skip_input_data = skip_input_data;
  src->pub.resync_to_restart = jpeg_resync_to_restart;
  src->pub.term_source = term_source;
  src->fill = fill;
  src->arg = arg;

  src->pub.bytes_in_buffer = 0;
}

void jpeg_cleanup(j_decompress_ptr cinfo)
{
  jpeg_destroy((j_common_ptr)cinfo);
  if (cinfo->src != NULL) {
    free(cinfo->src);
  }
}

void jpeg_error(j_common_ptr cinfo)
{
  struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err;
  WARN("\n");
  (*cinfo->err->output_message) (cinfo);
  longjmp(myerr->setjmp_buffer, 1);
}

void jpeg_init(j_decompress_ptr cinfo, struct my_error_mgr *jerr, jpeg_func_t fill, void *arg)
{
  DBG("\n");
  cinfo->err = jpeg_std_error((struct jpeg_error_mgr *)jerr);
  cinfo->err->error_exit = jpeg_error;
  jpeg_create_decompress(cinfo);
  jpeg_my_src(cinfo, fill, arg);
}

int jpeg_read(j_decompress_ptr cinfo, void *buf, int count)
{
  struct my_source_mgr *src = (struct my_source_mgr *)cinfo->src;
  int row_stride = 0;
  JSAMPARRAY buffer_in = NULL;  

  DBG("count=%d\n", count);
  jpeg_read_header(cinfo, TRUE);
  jpeg_start_decompress(cinfo);

  row_stride = cinfo->output_width * cinfo->output_components;
  if (count < (row_stride * (cinfo->output_height + 0))) {
    WARN("buffer to small. count=%d/%d\n", count, row_stride * (cinfo->output_height + 0));
    return -1;
  }
  buffer_in = (*cinfo->mem->alloc_sarray)
    ((j_common_ptr) cinfo, JPOOL_IMAGE, row_stride, 1);

  while(cinfo->output_scanline < cinfo->output_height) {
    //DBG("scanline=%d\n", cinfo->output_scanline);
    //DBG("bytes_in_buffer=%d\n", src->pub.bytes_in_buffer);
    (void) jpeg_read_scanlines(cinfo, buffer_in, 1);
    memcpy(buf + (cinfo->output_scanline - 1) * row_stride,
	   buffer_in[0], row_stride);
  }

  DBG("bytes_in_buffer=%d\n", src->pub.bytes_in_buffer);

  // ignore bytes past EOI
  src->pub.bytes_in_buffer = 0;

  jpeg_finish_decompress(cinfo);
  return cinfo->output_scanline * row_stride;
}

