/*****************************************************************************
*   "Gif-Lib" - Yet another gif library.
*
* Written by:  Gershon Elber				Ver 0.1, Jul. 1989
******************************************************************************
* Program to combine 2 GIF images into single one, using optional mask GIF
* file. Result colormap will be the union of the two images colormaps.
* Both images should have exactly the same size, although they may be mapped *
* differently on screen. Only First GIF screen descriptor info. is used.
* Options:
* -v : verbose mode.
* -m mask : optional boolean image, defines where second GIF should be used. *
* -h : on-line help.
******************************************************************************
* History:
* 12 Jul 89 - Version 1.0 by Gershon Elber.
*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>

#include "gif_lib.h"
#include "getarg.h"

#define PROGRAM_NAME	"gifcomb"

static char
    *VersionStr =
	PROGRAM_NAME
	VERSION_COOKIE
	"	Gershon Elber,	"
	__DATE__ ",   " __TIME__ "\n"
	"(C) Copyright 1989 Gershon Elber.\n";
static char
    *CtrlStr =
	PROGRAM_NAME
	" v%- m%-MaskGIFFile!s h%- GifFile!*s";

static int ReadUntilImage(GifFileType *GifFile);
static void QuitGifError(GifFileType *GifFileIn1, GifFileType *GifFileIn2,
			 GifFileType *GifMaskFile, GifFileType *GifFileOut);

/******************************************************************************
* Interpret the command line and scan the given GIF file.
******************************************************************************/
int main(int argc, char **argv)
{
    int	i, j, NumFiles, Size;
    bool Error, MaskFlag = false, HelpFlag = false;
    char **FileName = NULL, *MaskFileName;
    GifPixelType ColorTransIn2[256];
    GifRowType LineIn1 = NULL, LineIn2 = NULL, LineMask = NULL, LineOut = NULL;
    ColorMapObject *ColorUnion;
    ColorMapObject *ColorIn1 = NULL, *ColorIn2 = NULL;
    GifFileType *GifFileIn1 = NULL, *GifFileIn2 = NULL, *GifMaskFile = NULL,
	*GifFileOut = NULL;

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
		&GifNoisyPrint, &MaskFlag, &MaskFileName,
		&HelpFlag, &NumFiles, &FileName)) != false ||
		(NumFiles != 2 && !HelpFlag)) {
	if (Error)
	    GAPrintErrMsg(Error);
	else if (NumFiles != 2)
	    GIF_MESSAGE("Error in command line parsing - two GIF file please.");
	GAPrintHowTo(CtrlStr);
	exit(EXIT_FAILURE);
    }

    if (HelpFlag) {
	(void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
	GAPrintHowTo(CtrlStr);
	exit(EXIT_SUCCESS);
    }

    /* Open all input files (two GIF to combine, and optional mask): */
    if ((GifFileIn1 = DGifOpenFileName(FileName[0])) == NULL ||
	(GifFileIn2 = DGifOpenFileName(FileName[1])) == NULL ||
	(MaskFlag && (GifMaskFile = DGifOpenFileName(MaskFileName)) == NULL))
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    if (ReadUntilImage(GifFileIn1) == GIF_ERROR ||
	ReadUntilImage(GifFileIn2) == GIF_ERROR ||
	(MaskFlag && ReadUntilImage(GifMaskFile) == GIF_ERROR))
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    if (GifFileIn1->Image.Width != GifFileIn2->Image.Width ||
	GifFileIn2->Image.Height != GifFileIn2->Image.Height ||
	(MaskFlag && (GifFileIn1->Image.Width != GifMaskFile->Image.Width ||
		      GifFileIn1->Image.Height != GifMaskFile->Image.Height)))
	GIF_EXIT("Given GIF files have different image dimensions.");

    /* Open stdout for the output file: */
    if ((GifFileOut = EGifOpenFileHandle(1)) == NULL)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    Size = sizeof(GifPixelType) * GifFileIn1->Image.Width;
    if ((LineIn1 = (GifRowType) malloc(Size)) == NULL ||
	(LineIn2 = (GifRowType) malloc(Size)) == NULL ||
	(MaskFlag && (LineMask = (GifRowType) malloc(Size)) == NULL) ||
	(LineOut = (GifRowType) malloc(Size)) == NULL)
	GIF_EXIT("Failed to allocate memory required, aborted.");

    if (GifFileIn1->Image.ColorMap) {
	ColorIn1 = GifFileIn1->Image.ColorMap;
    }
    else if (GifFileIn1->SColorMap) {
	ColorIn1 = GifFileIn1->SColorMap;
    }
    else
	GIF_EXIT("Neither Screen nor Image color map exists - GIF file 1.");

    if (GifFileIn2->Image.ColorMap) {
	ColorIn2 = GifFileIn2->Image.ColorMap;
    }
    else if (GifFileIn2->SColorMap) {
	ColorIn2 = GifFileIn2->SColorMap;
    }
    else
	GIF_EXIT("Neither Screen nor Image color map exists - GIF file 2.");

    /* Create union of the two given color maps. ColorIn1 will be copied as  */
    /* is while ColorIn2 will be mapped using ColorTransIn2 table.	     */
    /* ColorUnion is allocated by the procedure itself.			     */
    if ((ColorUnion = UnionColorMap(ColorIn1, ColorIn2, ColorTransIn2))==NULL)
	GIF_EXIT("Unioned color map is too big (>256 colors).");

    /* Dump out new image and screen descriptors: */
    if (EGifPutScreenDesc(GifFileOut,
	GifFileIn1->SWidth, GifFileIn1->SHeight,
	ColorUnion->BitsPerPixel, GifFileIn1->SBackGroundColor,
	ColorUnion) == GIF_ERROR)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    if (EGifPutImageDesc(GifFileOut,
	GifFileIn1->Image.Left, GifFileIn1->Image.Top,
	GifFileIn1->Image.Width, GifFileIn1->Image.Height,
	GifFileIn1->Image.Interlace, NULL) == GIF_ERROR)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);


    /* Time to do it: read 2 scan lines from 2 files (and optionally from    */
    /* the mask file, merge them and them result out. Do it Height times:    */
    GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]:     ",
	PROGRAM_NAME, GifFileOut->Image.Left, GifFileOut->Image.Top,
				GifFileOut->Image.Width, GifFileOut->Image.Height);
    for (i = 0; i < GifFileIn1->Image.Height; i++) {
	if (DGifGetLine(GifFileIn1, LineIn1, GifFileIn1->Image.Width) == GIF_ERROR ||
	    DGifGetLine(GifFileIn2, LineIn2, GifFileIn2->Image.Width) == GIF_ERROR ||
	    (MaskFlag &&
	     DGifGetLine(GifMaskFile, LineMask, GifMaskFile->Image.Width)
								== GIF_ERROR))
	    QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
	if (MaskFlag) {
	    /* Every time Mask has non background color, use LineIn1 pixel,  */
	    /* otherwise use LineIn2 pixel instead.			     */
	    for (j = 0; j < GifFileIn1->Image.Width; j++) {
		if (LineMask[j] != GifMaskFile->SBackGroundColor)
		    LineOut[j] = LineIn1[j];
		else
		    LineOut[j] = ColorTransIn2[LineIn2[j]];
	    }
	}
	else {
	    /* Every time Color of Image 1 is equal to background - take it  */
	    /* From Image 2 instead of the background.			     */
	    for (j = 0; j < GifFileIn1->Image.Width; j++) {
		if (LineIn1[j] != GifFileIn1->SBackGroundColor)
		    LineOut[j] = LineIn1[j];
		else
		    LineOut[j] = ColorTransIn2[LineIn2[j]];
	    }
	}
	if (EGifPutLine(GifFileOut, LineOut, GifFileOut->Image.Width)
								== GIF_ERROR)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
	GifQprintf("\b\b\b\b%-4d", i);
    }

    FreeMapObject(ColorUnion);		    /* We dont need this any more... */
    ColorUnion = NULL;

    if (DGifCloseFile(GifFileIn1) == GIF_ERROR ||
	DGifCloseFile(GifFileIn2) == GIF_ERROR ||
	EGifCloseFile(GifFileOut) == GIF_ERROR ||
	(MaskFlag && DGifCloseFile(GifMaskFile) == GIF_ERROR))
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    return 0;
}

/******************************************************************************
* Read until first image in GIF file is detected and read its descriptor.
******************************************************************************/
static int ReadUntilImage(GifFileType *GifFile)
{
    int ExtCode;
    GifRecordType RecordType;
    GifByteType *Extension;

    /* Scan the content of the GIF file, until image descriptor is detected: */
    do {
	if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
	    return GIF_ERROR;

	switch (RecordType) {
	    case IMAGE_DESC_RECORD_TYPE:
		return DGifGetImageDesc(GifFile);
	    case EXTENSION_RECORD_TYPE:
		/* Skip any extension blocks in file: */
		if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR)
		    return GIF_ERROR;

		while (Extension != NULL)
		    if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
			return GIF_ERROR;
		break;
	    case TERMINATE_RECORD_TYPE:
		break;
	    default:		    /* Should be traps by DGifGetRecordType. */
		break;
	}
    }
    while (RecordType != TERMINATE_RECORD_TYPE);

    return GIF_ERROR;		  /* We should be here - no image was found! */
}

/******************************************************************************
* Close both input and output file (if open), and exit.
******************************************************************************/
static void QuitGifError(GifFileType *GifFileIn1, GifFileType *GifFileIn2,
			 GifFileType *GifMaskFile, GifFileType *GifFileOut)
{
    PrintGifError();
    if (GifFileIn1 != NULL) DGifCloseFile(GifFileIn1);
    if (GifFileIn2 != NULL) DGifCloseFile(GifFileIn2);
    if (GifMaskFile != NULL) DGifCloseFile(GifMaskFile);
    if (GifFileOut != NULL) EGifCloseFile(GifFileOut);
    exit(EXIT_FAILURE);
}
