/*****************************************************************************
*   Routines to	prpepare the color table for the given scene. Each color     *
* get equal share in the map table, so its modules does:		     *
* 1. Scans all objects and retrieve all possible colors.		     *
* 2. Create a color map table, of the specified size (in ShadingInfo global  *
*    structure) and interpolate intensity levels for each color into it.     *
* 3. Update the objects for their index into the color map.		     *
*									     *
* Written by:  Gershon Elber				Ver 2.0, Mar. 1990   *
*****************************************************************************/

#ifdef __MSDOS__
#include <stdlib.h>
#endif /* __MSDOS__ */

#include <math.h>
#include <stdio.h>
#include <time.h>
#include "program.h"
#include "parser.h"
#include "gif_lib.h"

/* The colors can be specified as indices in which we must convert to RGB: */
/* We uses the EGA convension (see also TC 2.0 graphics.h file).	   */
static int TransColorTable[][4] = {
    { /* BLACK		*/ 0,    0,   0,   0 },
    { /* BLUE		*/ 1,    0,   0, 255 },
    { /* GREEN		*/ 2,    0, 255,   0 },
    { /* CYAN		*/ 3,    0, 255, 255 },
    { /* RED		*/ 4,  255,   0,   0 },
    { /* MAGENTA 	*/ 5,  255,   0, 255 },
    { /* BROWN		*/ 20,  50,   0,   0 },
    { /* LIGHTGRAY	*/ 7,  127, 127, 127 },
    { /* DARKGRAY	*/ 56,  63,  63,  63 },
    { /* LIGHTBLUE	*/ 57,   0,   0, 255 },
    { /* LIGHTGREEN	*/ 58,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 59,   0, 255, 255 },
    { /* LIGHTRED	*/ 60, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 61, 255,   0, 255 },
    { /* YELLOW		*/ 62, 255, 255,   0 },
    { /* WHITE		*/ 63, 255, 255, 255 },
    {			   -1,   0,   0,   0 }
};


static void PrepareAllObjects1(FileDescription **FD, GifColorType *Colors,
							int *NumColors);
static void VisitObjectTree1(BinTree *PBinTree, GifColorType *Colors,
							int *NumColors);
static void InsertColor(ObjectStruct *PObject, GifColorType *Colors,
							int *NumColors);
static void PrepareAllObjects2(FileDescription **FD, GifColorType *Colors,
							int NumColors);
static void VisitObjectTree2(BinTree *PBinTree, GifColorType *Colors,
							int NumColors);
static void UpdateColorIndex(ObjectStruct *PObject, GifColorType *Colors,
							int NumColors);

/*****************************************************************************
* Routine to prepare color map for the given scene.			     *
*****************************************************************************/
void PrepareColorTable(FileDescription **FD, int NumOfObjects,
					int RealNumOfObjects, char **Objects)
{
    int	i, j, NumColors = 0, ColorMapSize, Levels, *MinIntensityIndex;
    float DIntensity, CrntIntensity;
    GifColorType *Colors, *ColorMap;
    struct ObjectStruct	*PObject;

    Colors = (GifColorType *) MyMalloc(sizeof(GifColorType) * RealNumOfObjects);

    if (NumOfObjects > 0)	   /* There was something on command line... */
	for (i=0; i<NumOfObjects; i++) {
	    if ((PObject = SearchObject(FD, *Objects)) != NULL)
		InsertColor(PObject, Colors, &NumColors);
	}
    else {		    /* Prepare all objects by scanning object trees. */
	PrepareAllObjects1(FD, Colors, &NumColors);
    }

    /* Allocate color table and fill it in: */
    ColorMapSize = (1 << ShadingInfo.BitsPerPixel);
    ShadingInfo.PColorMap = ColorMap =
	(GifColorType *) MyMalloc(sizeof(GifColorType) * ColorMapSize);
    ShadingInfo.MinIntensityIndex = MinIntensityIndex =
	(int *) MyMalloc(sizeof(int) * ColorMapSize);

    /* Save one place to hold the back ground color: */
    if (NumColors == 0) {
	fprintf(stderr, "No color allocated - that is really wierd!\n");
	MyExit(1);
    }
    ShadingInfo.LevelsPerColor = Levels = (ColorMapSize - 1) / NumColors;
    if (Levels == 0) {
	fprintf(stderr, "Too many colors (%d) for color map size (%d), exit\n",
						NumColors, ColorMapSize);
	MyExit(1);
    }

    /* Insert the back ground color in: */
    for (i=0; TransColorTable[i][0] >= 0; i++)
	if (TransColorTable[i][0] == ShadingInfo.BackGroundColor) break;
    if (TransColorTable[i][0] < 0) {
	fprintf(stderr,
	    "Undefined color required (%d) for background, default selected instead.\n",
	    PObject -> Color);
	PObject -> Color = ShadingInfo.DefaultColor;
    }
    ColorMap[0].Red   = TransColorTable[i][1];
    ColorMap[0].Green = TransColorTable[i][2];
    ColorMap[0].Blue  = TransColorTable[i][3];

    /* O.k. time to interpolate the colors. Each given color intensities are */
    /* interplated between the ambient light and full intensity (1.0):	     */
    DIntensity = (1.0 - ShadingInfo.Ambient) / Levels;
    for (i=0; i<NumColors; i++) {
	CrntIntensity = 1.0;
	for (j=0; j<Levels; j++) {
	    ColorMap[i * Levels + j + 1].Red =
					(int) (Colors[i].Red * CrntIntensity);
	    ColorMap[i * Levels + j + 1].Green =
					(int) (Colors[i].Green * CrntIntensity);
	    ColorMap[i * Levels + j + 1].Blue =
					(int) (Colors[i].Blue * CrntIntensity);
	    MinIntensityIndex[i * Levels + j + 1] = (i + 1) * Levels;
	    CrntIntensity -= DIntensity;
	}
    }

    /* Now the final step - let the objects know where their color is mapped */
    /* to in the color table.						     */
    if (NumOfObjects > 0)	   /* There was something on command line... */
	for (i=0; i<NumOfObjects; i++) {
	    if ((PObject = SearchObject(FD, *Objects)) != NULL)
		UpdateColorIndex(PObject, Colors, NumColors);
	}
    else {		    /* Prepare all objects by scanning object trees. */
	PrepareAllObjects2(FD, Colors, NumColors);
    }

    free((char *) Colors);
}

/*****************************************************************************
* Scan all objects.							     *
*****************************************************************************/
static void PrepareAllObjects1(FileDescription **FD, GifColorType *Colors,
							int *NumColors)
{
    while (*FD)	VisitObjectTree1((*FD++) -> ObjectPointer, Colors, NumColors);
}

/*****************************************************************************
* Scanning all the object in tree PBinTree and prepare them.		     *
*****************************************************************************/
static void VisitObjectTree1(BinTree *PBinTree, GifColorType *Colors,
							int *NumColors)
{
    if (PBinTree == (BinTree *)	NULL) return;

    VisitObjectTree1(PBinTree -> right, Colors, NumColors);

    InsertColor(PBinTree -> Data.PObject, Colors, NumColors);

    VisitObjectTree1(PBinTree -> left, Colors, NumColors);
}

/*****************************************************************************
* Scanning all the object in tree PBinTree and prepare them.		     *
*****************************************************************************/
static void InsertColor(ObjectStruct *PObject, GifColorType *Colors,
							int *NumColors)
{
    int i;

    if (PObject -> Color != RGB_COLOR_GIVEN) {
	/* Translate the color into RGB form: */
	for (i=0; TransColorTable[i][0] >= 0; i++)
	    if (TransColorTable[i][0] == PObject -> Color) break;
	if (TransColorTable[i][0] < 0) {
	    fprintf(stderr,
		"Undefined color required (%d), default selected instead.\n",
		PObject -> Color);
	    PObject -> Color = ShadingInfo.DefaultColor;
	}
	PObject -> RGBColor[0] = TransColorTable[i][1];
	PObject -> RGBColor[1] = TransColorTable[i][2];
	PObject -> RGBColor[2] = TransColorTable[i][3];
    }

    /* Lets see if this color exists in table already, and if so do nothing: */
    for (i=0; i<*NumColors; i++)
	if (PObject -> RGBColor[0] == Colors[i].Red &&
	    PObject -> RGBColor[1] == Colors[i].Green &&
	    PObject -> RGBColor[2] == Colors[i].Blue) return;

    /* Its a new color - add it to out colors table to allocate: */
    Colors[*NumColors].Red      = PObject -> RGBColor[0];
    Colors[*NumColors].Green    = PObject -> RGBColor[1];
    Colors[(*NumColors)++].Blue = PObject -> RGBColor[2];
}

/*****************************************************************************
* Scan all objects.							     *
*****************************************************************************/
static void PrepareAllObjects2(FileDescription **FD, GifColorType *Colors,
							int NumColors)
{
    while (*FD)	VisitObjectTree2((*FD++) -> ObjectPointer, Colors, NumColors);
}

/*****************************************************************************
* Scanning all the object in tree PBinTree and prepare them.		     *
*****************************************************************************/
static void VisitObjectTree2(BinTree *PBinTree, GifColorType *Colors,
							int NumColors)
{
    if (PBinTree == (BinTree *)	NULL) return;

    VisitObjectTree2(PBinTree -> right, Colors, NumColors);

    UpdateColorIndex(PBinTree -> Data.PObject, Colors, NumColors);

    VisitObjectTree2(PBinTree -> left, Colors, NumColors);
}

/*****************************************************************************
* Update object with the index to its color in the color map.		     *
*****************************************************************************/
static void UpdateColorIndex(ObjectStruct *PObject, GifColorType *Colors,
							int NumColors)
{
    int i;

    /* Find this color in table (must be in it...): */
    for (i=0; i<NumColors; i++)
	if (PObject -> RGBColor[0] == Colors[i].Red &&
	    PObject -> RGBColor[1] == Colors[i].Green &&
	    PObject -> RGBColor[2] == Colors[i].Blue) break;

    /* Color table hold this object color, with decreasing intensity, from   */
    /* this index up to (i + 1) * ShadingInfo.LevelsPerColor.		     */
    PObject -> Color = i * ShadingInfo.LevelsPerColor + 1;
}

