/*------------------------------------------------------------------------
 *
 * Copyright (c) 1997-1998 by Cornell University.
 * 
 * See the file "license.txt" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * ppmstoanimgif
 *
 * Steve Weiss sweiss@cs.cornell.edu
 *
 * Usage : ppmstoanimgif prefix outputGIF numOfImage
 *
 * Encodes a series of PPM (prefix0.ppm, prefix1.ppm etc) as an animated
 * GIF.  The color map of the first image is shared with the rest of the
 * images.  The number of images is specified by numOfImage.
 *
 *------------------------------------------------------------------------
 */
#include <dvmbasic.h>
#include <dvmimap.h>
#include <dvmpnm.h>
#include <dvmgif.h>
#include <dvmcolor.h>

int fsize (char *);


/*
 * This is similar to ReadPPM_, except it only allocates r, g, b only 
 * if they are NULL.  This allow more efficient use of memory by reusing
 * previously allocated r, g, and b.  
 */
void 
ReadPPM_ (filename, hdr, r, g, b)
    char *filename;
    PnmHdr **hdr;
    ByteImage **r;
    ByteImage **g;
    ByteImage **b;
{
    FILE *f;
    BitStream *inbs;
    BitParser *inbp;
    int w, h;

    f = fopen (filename, "rb");
    if (f == NULL) {
	fprintf (stderr, "unable to open file %s for reading\n", filename);
	exit (1);
    }
    *hdr = PnmHdrNew ();
    inbs = BitStreamNew (fsize (filename));
    inbp = BitParserNew ();
    BitParserWrap (inbp, inbs);
    BitStreamFileRead (inbs, f, 0);
    PnmHdrParse (inbp, *hdr);

    w = PnmHdrGetWidth (*hdr);
    h = PnmHdrGetHeight (*hdr);
    if (*r == NULL)
	*r = ByteNew (w, h);
    if (*g == NULL)
	*g = ByteNew (w, h);
    if (*b == NULL)
	*b = ByteNew (w, h);
    PpmParse (inbp, *r, *g, *b);

    fclose (f);
    BitParserFree (inbp);
    BitStreamFree (inbs);

}

int
main (int argc, char *argv[])
{
    BitParser *bp;
    BitStream *bs;
    PnmHdr *pnmhdr;
    ImageMap *rmap, *gmap, *bmap;
    ByteImage *r, *g, *b, *indices;
    FILE *f;
    int w, h, i, numOfImages;
    GifSeqHdr *hdr;
    GifImgHdr *img;
    char inname[256];
    ColorHashTable *cht;
    VpTree tree;

    if (argc < 4) {
	fprintf (stderr, "usage: %s inputPrefix output numOfImages\n", argv[0]);
	exit (1);
    }
    /* make compiler happy */
    rmap = gmap = bmap = NULL;
    img = NULL;
    hdr = NULL;
    indices = NULL;
    bs = NULL;
    bp = NULL;

    f = fopen (argv[2], "w");
    if (f == NULL) {
	fprintf (stderr, "unable to open file %s for writing.\n", argv[2]);
	exit (1);
    }
    numOfImages = atoi (argv[3]);
    /*
     * Read the first image. We need the width and height information
     */
    sprintf (inname, "%s%03d.ppm", argv[1], 0);
    r = g = b = NULL;
    ReadPPM_ (inname, &pnmhdr, &r, &g, &b);

    /*
     * Allocates the image maps, ByteImage for indices and BitStream.
     */
    rmap = ImageMapNew ();
    gmap = ImageMapNew ();
    bmap = ImageMapNew ();
    w = ByteGetWidth (r);
    h = ByteGetHeight (r);
    indices = ByteNew (w, h);
    bs = BitStreamNew (3 * numOfImages * w * h);	// output better be smaller than this

    bp = BitParserNew ();
    BitParserWrap (bp, bs);

    /*
     * Create and encodes the sequence header.
     */
    hdr = GifSeqHdrNew ();
    GifSeqHdrSetWidth (hdr, w);
    GifSeqHdrSetHeight (hdr, h);
    GifSeqHdrSetCtFlag (hdr, 1);
    GifSeqHdrSetVersion (hdr, "87a");
    GifSeqHdrSetCtSize (hdr, 256);
    GifSeqHdrSetCtSorted (hdr, 0);
    GifSeqHdrSetAspectRatio (hdr, 0);
    GifSeqHdrSetBackgroundColor (hdr, 0);
    GifSeqHdrSetResolution (hdr, 3);
    GifSeqHdrEncode (hdr, bp);

    /*
     * Calculate the color map to use. and
     * encode the color map.
     */
    cht = ColorHashTableNew (15);
    ColorHashTableClear (cht);
    RgbTo256 (r, g, b, cht, rmap, gmap, bmap);
    ColorHashTableFree (cht);
    cht = ColorHashTableNew (16);
    ColorHashTableClear (cht);
    tree = VpTreeNew ();
    VpTreeInit (rmap, gmap, bmap, tree);
    RgbQuantWithVpTree (r, g, b, tree, cht, rmap, gmap, bmap, indices);
    GifCtEncode (256, rmap, gmap, bmap, bp);

    /*
     * Encode Netscape extension for animated GIF.
     */
    GifSeqLoopEncode (bp);

    /*
     * Create and encodes the image header.
     */
    img = GifImgHdrNew ();
    GifImgHdrSetWidth (img, w);
    GifImgHdrSetHeight (img, h);
    GifImgHdrSetCtFlag (img, 0);
    GifImgHdrSetLeftPosition (img, 0);
    GifImgHdrSetTopPosition (img, 0);
    GifImgHdrSetInterlaced (img, 0);
    GifImgHdrSetGraphicControlFlag (img, 1);
    GifImgHdrSetDisposalMethod (img, 0);
    GifImgHdrSetUserInputFlag (img, 0);
    GifImgHdrSetTransparentColorFlag (img, 0);
    GifImgHdrSetDelayTime (img, 1);

    GifImgHdrEncode (img, bp);
    GifImgEncode (hdr, img, indices, bp);

    /*
     * Encode the rest of the images, using the same color map and
     * image header.
     */
    for (i = 0; i < numOfImages; i++) {
	sprintf (inname, "%s%03d.ppm", argv[1], i);
	ReadPPM_ (inname, &pnmhdr, &r, &g, &b);
	RgbQuantWithVpTree (r, g, b, tree, cht, rmap, gmap, bmap, indices);
	GifImgHdrEncode (img, bp);
	GifImgEncode (hdr, img, indices, bp);
    }

    /*
     * Clean up the stuffs.
     */
    ImageMapFree (rmap);
    ImageMapFree (gmap);
    ImageMapFree (bmap);
    ColorHashTableFree (cht);
    GifSeqTrailerEncode (bp);
    BitStreamFileWrite (bs, f, 0);
    BitStreamFree (bs);
    ByteFree (r);
    ByteFree (g);
    ByteFree (b);
    fclose (f);

    return 0;
}