#include "views.h"
#include <gif_lib.h>
#include <fcntl.h>
#include <unistd.h>

static ColorMapObject *makeMap(DanceView *dv, int couples, int cmap_size) {
    /* Color table */
    /* 0 => black for text */
    /* 1 => background */
    /* 2 => band text */
    /* 3 => hand clasp */
    /* For sword dancing we need: */
    /*  4 => sword blade */
    /*  5 => sword hilt */
    /* End sword */
    /* 2 colors for each couple for up to 8 couples */
    ColorMapObject *cmap;
    int i,base;

#ifdef __APPLE__
    cmap = MakeMapObject(1<<cmap_size,NULL);
#else
    cmap = GifMakeMapObject(1<<cmap_size,NULL);
#endif
    for ( i=(1<<cmap_size)-1; i>=0; --i )
	cmap->Colors[i].Red = cmap->Colors[i].Green = cmap->Colors[i].Blue = 0;
    /* Text color already set to black */
    /* Background */
    if ( dv->dance->style==ds_longsword ) {
	cmap->Colors[1].Red   = 0xf0;
	cmap->Colors[1].Green = 0xc9;
	cmap->Colors[1].Blue  = 0xb8;
    } else {
	cmap->Colors[1].Red   = 0xff;
	cmap->Colors[1].Green = 0xf0;
	cmap->Colors[1].Blue  = 0xde;
    }
    /* Band */
    cmap->Colors[2].Red   = 0xb3;
    cmap->Colors[2].Green = 0xb3;
    cmap->Colors[2].Blue  = 0xb3;
    /* Hand clasp */
    cmap->Colors[3].Red   = 0xff;
    cmap->Colors[3].Green = 0xc7;
    cmap->Colors[3].Blue  = 0x00;
    base=4;
    if ( dv->dance->style==ds_longsword ) {
	/* sword blade */
	cmap->Colors[base].Red   = 0xe6;
	cmap->Colors[base].Green = 0xe6;
	cmap->Colors[base++].Blue  = 0xe6;
	/* sword hilt */
	cmap->Colors[base].Red   = 0xa6;
	cmap->Colors[base].Green = 0x51;
	cmap->Colors[base++].Blue  = 0x51;
    }
    for ( i=0; i<couples; ++i ) {
	int edge, body;
	danceGetCoupleColor(i,&edge,&body);
	cmap->Colors[base  +2*i].Red   = (body>>16)&0xff;
	cmap->Colors[base  +2*i].Green = (body>>8 )&0xff;
	cmap->Colors[base  +2*i].Blue  = (body    )&0xff;
	cmap->Colors[base+1+2*i].Red   = (edge>>16)&0xff;
	cmap->Colors[base+1+2*i].Green = (edge>>8 )&0xff;
	cmap->Colors[base+1+2*i].Blue  = (edge    )&0xff;
    }
return( cmap );
}

static int danceGifLoadFile(DanceView *dv,char *frameDir,int frame,
	GifRowType *RasterBuffer,ColorMapObject *cmap,int couples,Rect *crop ) {
    int ccnt = 4+2*couples;
    int r,c,ci;
    GdkPixbuf *png, *cropped;
    GError *error = NULL;
    char buffer[300];
    int rowstride;
    const guint8 *pixels, *row;
    int width, height;

    sprintf( buffer, "%s/D%06d.png", frameDir, frame );
    png = gdk_pixbuf_new_from_file(buffer,&error);
    if ( png==NULL )
return( 0 );
    if ( crop!=NULL ) {
	cropped = gdk_pixbuf_new_subpixbuf(png,crop->x,crop->y,crop->width,crop->height);
	if ( cropped!=NULL ) {
	    g_object_unref(G_OBJECT(png));
	    png = cropped;
	}
    }
    rowstride = gdk_pixbuf_get_rowstride(png);
    pixels = gdk_pixbuf_read_pixels(png);
    width  = gdk_pixbuf_get_width(png);
    height = gdk_pixbuf_get_height(png);
    for ( r=0; r<height; ++r ) {
	row = pixels + r*rowstride;
	for ( c=0; c<width; ++c ) {
	    int red   = row[c*3+0];
	    int green = row[c*3+1];
	    int blue  = row[c*3+2];
	    int besti=0, bestoffset=3*65536, offset;
	    for ( ci=0; ci<ccnt; ++ci ) {
		offset = (red  -cmap->Colors[ci].Red  )*(red  -cmap->Colors[ci].Red) +
			 (green-cmap->Colors[ci].Green)*(green-cmap->Colors[ci].Green) +
			 (blue -cmap->Colors[ci].Blue )*(blue -cmap->Colors[ci].Blue );
		if ( offset<bestoffset ) {
		    bestoffset = offset;
		    besti = ci;
		}
	    }
	    RasterBuffer[r][c] = besti;
	}
    }

    g_object_unref(G_OBJECT(png));
return( 1 );
}

int danceOutputAnimatedGif(DanceView *dv,char *frameDir, char *outputName, Rect *crop) {
    int outputFno;
    GifFileType *gifOut;
    int couples, cmap_size;
    ColorMapObject *cmap;
    int frame, i, ret;
    GifRowType *RasterBuffer;
    unsigned char packedGCB[4];
    unsigned char packedLoop[16];
    int width,height;
#ifndef __APPLE__
    int error;
#endif

    width  = dv->width;
    height = dv->height;
    if ( crop!=NULL && (crop->width==0 || crop->height==0))
	crop = NULL;
    if ( crop!=NULL ) {
	width  = crop->width;
	height = crop->height;
    }

    outputFno = open(outputName,O_CREAT|O_TRUNC|O_WRONLY,0755);
    if( outputFno==-1 ) {
	fprintf( stderr, "Failed to open %s\n", outputName );
return( 0 );
    }
#ifdef __APPLE__
    gifOut = EGifOpenFileHandle(outputFno);
#else
    gifOut = EGifOpenFileHandle(outputFno,&error);
#endif
    if ( gifOut==NULL ) {
	close(outputFno);
return( 0 );
    }
#ifdef __APPLE__
    EGifSetGifVersion(GIF89_STAMP+3);	/* We need Gif89 for animated gifs */
#else
    EGifSetGifVersion(gifOut,1);	/* We need Gif89 for animated gifs */
#endif

    couples = dv->couples;
    if ( couples>=8 )
	couples = 8;
    if ( couples<=2 )
	cmap_size = 3;
    else if ( couples<=5 || (couples==6 && dv->dance->style!=ds_longsword) )
	cmap_size = 4;
    else
	cmap_size = 5;
    cmap = makeMap(dv,couples,cmap_size);
    EGifPutScreenDesc(gifOut,width,height,cmap_size, 0, cmap );

    /* To get an animation to work we need two things */
    /*  to specify a delay between images, this is done with a graphics control block before each image */
    /*  to say how many times we want to loop. This is done with an application control block at the very start */
    /* (I specify a repeat count of 0 which means infinite) */
    /* This is wrong! But this version of the library doesn't let me output */
    /*  the correct value, so I do a later pass to fix it up */
    /* This puts the right number of bytes in the file, and the fixup is just to change one length byte */
    strcpy((char *) packedLoop,"NETSCAPE2.0");
    packedLoop[11] = 3;
    packedLoop[12] = 1;
    packedLoop[13] = 0;		/* Repeat count low-order byte */
    packedLoop[14] = 0;		/* Repeat count high-order byte */
    EGifPutExtension(gifOut,0xff,15,packedLoop);

    RasterBuffer = malloc(height*sizeof(GifRowType));
    for ( i=0; i<height; ++i )
	RasterBuffer[i] = malloc(width*sizeof(GifPixelType));

    memset(&packedGCB,0,sizeof(packedGCB));
    packedGCB[3] = 0xff;
    packedGCB[1] = 100/dv->frames_per_sec;

    for( frame=0; ; ++frame ) {
	if ( !danceGifLoadFile(dv,frameDir,frame,RasterBuffer,cmap,couples,crop))
    break;
	if ( frame!=0 )
	    EGifPutExtension(gifOut,0xf6,4,packedGCB);
	EGifPutImageDesc(gifOut,0,0,width,height,false,NULL);
	for ( i=0; i<dv->height; ++i )
	    EGifPutLine(gifOut,RasterBuffer[i],width);
    }

    ret = 1;
#ifdef __APPLE__
    if ( EGifCloseFile(gifOut) == GIF_ERROR ) {
#else
    if ( EGifCloseFile(gifOut,&error) == GIF_ERROR ) {
#endif
	ret = 0;
    }

    /* fixup the application extension length byte */
    outputFno = open(outputName,O_WRONLY,0755);		/* GifClose closed our file descriptor */
    lseek(outputFno,6+7+3*(1<<cmap_size)+2,SEEK_SET);
    write(outputFno,"\x0b",1);		/* Length field should be 11 not 15 */
    close( outputFno );

    for ( i=0; i<height ; ++i )
	free( RasterBuffer[i] );
    free( RasterBuffer );
#ifdef __APPLE__
    FreeMapObject( cmap );
#else
    GifFreeMapObject( cmap );
#endif
return( ret );
}
