#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gif_lib.h>

#include "readers.h"

struct gif_state {
    FILE         *infile;
    GifFileType  *gif;
    GifPixelType *row;
    GifPixelType *il;
    int w,h;
};

static GifRecordType
gif_fileread(struct gif_state *h)
{
    GifRecordType RecordType;
    GifByteType *Extension;
    int ExtCode, rc;
    char *type;

    for (;;) {
	if (GIF_ERROR == DGifGetRecordType(h->gif,&RecordType)) {
	    if (debug)
		fprintf(stderr,"gif: DGifGetRecordType failed\n");
	    PrintGifError();
	    return -1;
	}
	switch (RecordType) {
	case IMAGE_DESC_RECORD_TYPE:
	    if (debug)
		fprintf(stderr,"gif: IMAGE_DESC_RECORD_TYPE found\n");
	    return RecordType;
	case EXTENSION_RECORD_TYPE:
	    if (debug)
		fprintf(stderr,"gif: EXTENSION_RECORD_TYPE found\n");
	    for (rc = DGifGetExtension(h->gif,&ExtCode,&Extension);
		 NULL != Extension;
		 rc = DGifGetExtensionNext(h->gif,&Extension)) {
		if (rc == GIF_ERROR) {
		    if (debug)
			fprintf(stderr,"gif: DGifGetExtension failed\n");
		    PrintGifError();
		    return -1;
		}
		if (debug) {
		    switch (ExtCode) {
		    case COMMENT_EXT_FUNC_CODE:     type="comment";   break;
		    case GRAPHICS_EXT_FUNC_CODE:    type="graphics";  break;
		    case PLAINTEXT_EXT_FUNC_CODE:   type="plaintext"; break;
		    case APPLICATION_EXT_FUNC_CODE: type="appl";      break;
		    default:                        type="???";       break;
		    }
		    fprintf(stderr,"gif: extcode=0x%x [%s]\n",ExtCode,type);
		}
	    }
	    break;
	case TERMINATE_RECORD_TYPE:
	    if (debug)
		fprintf(stderr,"gif: TERMINATE_RECORD_TYPE found\n");
	    return RecordType;
	default:
	    if (debug)
		fprintf(stderr,"gif: unknown record type [%d]\n",RecordType);
	    return -1;
	}
    }
}

#if 0
static void
gif_skipimage(struct gif_state *h)
{
    unsigned char *line;
    int i;

    if (debug)
	fprintf(stderr,"gif: skipping image record ...\n");
    DGifGetImageDesc(h->gif);
    line = malloc(h->gif->SWidth);
    for (i = 0; i < h->gif->SHeight; i++)
	DGifGetLine(h->gif, line, h->gif->SWidth);
    free(line);
}
#endif

static void*
gif_init(FILE *fp, char *filename, unsigned int page,
	 struct ida_image_info *info, int thumbnail)
{
    struct gif_state *h;
    GifRecordType RecordType;
    int i, image = 0;
    
    h = malloc(sizeof(*h));
    memset(h,0,sizeof(*h));

    h->infile = fp;
    h->gif = DGifOpenFileHandle(fileno(fp));
    h->row = malloc(h->gif->SWidth * sizeof(GifPixelType));

    while (0 == image) {
	RecordType = gif_fileread(h);
	switch (RecordType) {
	case IMAGE_DESC_RECORD_TYPE:
	    if (GIF_ERROR == DGifGetImageDesc(h->gif)) {
		if (debug)
		    fprintf(stderr,"gif: DGifGetImageDesc failed\n");
		PrintGifError();
	    }
	    if (NULL == h->gif->SColorMap &&
		NULL == h->gif->Image.ColorMap) {
		if (debug)
		    fprintf(stderr,"gif: oops: no colormap found\n");
		goto oops;
	    }
#if 0
	    info->width  = h->w = h->gif->SWidth;
	    info->height = h->h = h->gif->SHeight;
#else
	    info->width  = h->w = h->gif->Image.Width;
	    info->height = h->h = h->gif->Image.Height;
#endif
            info->npages = 1;
	    image = 1;
	    if (debug)
		fprintf(stderr,"gif: reading image record ...\n");
	    if (h->gif->Image.Interlace) {
		if (debug)
		    fprintf(stderr,"gif: interlaced\n");
		h->il = malloc(h->w * h->h * sizeof(GifPixelType));
		for (i = 0; i < h->h; i += 8)
		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
		for (i = 4; i < h->gif->SHeight; i += 8)
		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
		for (i = 2; i < h->gif->SHeight; i += 4)
		    DGifGetLine(h->gif, h->il + h->w*i,h->w);
	    }
	    break;
	case TERMINATE_RECORD_TYPE:
	default:
	    goto oops;
	}
    }
    if (0 == info->width || 0 == info->height)
	goto oops;

    if (debug)
	fprintf(stderr,"gif: s=%dx%d i=%dx%d\n",
		h->gif->SWidth,h->gif->SHeight,
		h->gif->Image.Width,h->gif->Image.Height);
    return h;

 oops:
    if (debug)
	fprintf(stderr,"gif: fatal error, aborting\n");
    DGifCloseFile(h->gif);
    fclose(h->infile);
    free(h->row);
    free(h);
    return NULL;
}

static void
gif_read(unsigned char *dst, unsigned int line, void *data)
{
    struct gif_state *h = data;
    GifColorType *cmap;
    int x;
    
    if (h->gif->Image.Interlace) {
	if (line % 2) {
	    DGifGetLine(h->gif, h->row, h->w);
	} else {
	    memcpy(h->row, h->il + h->w * line, h->w);
	}
    } else {
	DGifGetLine(h->gif, h->row, h->w);
    }
    cmap = h->gif->Image.ColorMap ?
	h->gif->Image.ColorMap->Colors : h->gif->SColorMap->Colors;
    for (x = 0; x < h->w; x++) {
        dst[0] = cmap[h->row[x]].Red;
	dst[1] = cmap[h->row[x]].Green;
	dst[2] = cmap[h->row[x]].Blue;
	dst += 3;
    }
}

static void
gif_done(void *data)
{
    struct gif_state *h = data;

    if (debug)
	fprintf(stderr,"gif: done, cleaning up\n");
    DGifCloseFile(h->gif);
    fclose(h->infile);
    if (h->il)
	free(h->il);
    free(h->row);
    free(h);
}

static struct ida_loader gif_loader = {
    magic: "GIF",
    moff:  0,
    mlen:  3,
    name:  "libungif",
    init:  gif_init,
    read:  gif_read,
    done:  gif_done,
};

static void __init init_rd(void)
{
    load_register(&gif_loader);
}
