/*****************************************************************************
*   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 specified size (in GlblShadeInfo 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   *
*****************************************************************************/

#include <math.h>
#include <stdio.h>
#include "program.h"
#include "iritprsr.h"
#include "iritgrap.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		*/ 6,   50,   0,   0 },
    { /* LIGHTGRAY	*/ 7,  127, 127, 127 },
    { /* DARKGRAY	*/ 8,   63,  63,  63 },
    { /* LIGHTBLUE	*/ 9,    0,   0, 255 },
    { /* LIGHTGREEN	*/ 10,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 11,   0, 255, 255 },
    { /* LIGHTRED	*/ 12, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 13, 255,   0, 255 },
    { /* YELLOW		*/ 14, 255, 255,   0 },
    { /* WHITE		*/ 15, 255, 255, 255 },
    { /* BROWN		*/ 20,  50,   0,   0 },
    { /* 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(IPObjectStruct *PObjects,
			       GifColorType *Colors, int *NumColors);
static void InsertColor(IPObjectStruct *PObject, GifColorType *Colors,
							int *NumColors);
static void PrepareAllObjects2(IPObjectStruct *PObjects, GifColorType *Colors,
							int NumColors);
static void UpdateColorIndex(IPObjectStruct *PObject, GifColorType *Colors,
							int NumColors);

/*****************************************************************************
* Routine to prepare color map for the given scene.			     *
*****************************************************************************/
void PrepareColorTable(IPObjectStruct *PObjects)
{
    int	i, j, NumColors = 0, NumOfObjs, ColorMapSize, Levels,
	*MinIntensityIndex;
    RealType DIntensity, CrntIntensity;
    GifColorType *Colors, *ColorMap;
    IPObjectStruct *PObject;

    for (NumOfObjs = 0, PObject = PObjects;  /* How many objects do we have? */
	 PObject != NULL;
	 NumOfObjs++, PObject = PObject -> Pnext);

    Colors = (GifColorType *) IritMalloc(sizeof(GifColorType) * NumOfObjs);

    PrepareAllObjects1(PObjects, Colors, &NumColors);

    /* Allocate color table and fill it in: */
    ColorMapSize = (1 << GlblShadeInfo.BitsPerPixel);
    GlblShadeInfo.PColorMap = ColorMap =
	(GifColorType *) IritMalloc(sizeof(GifColorType) * ColorMapSize);
    GlblShadeInfo.MinIntensityIndex = MinIntensityIndex =
	(int *) IritMalloc(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");
	Poly3drExit(1);
    }
    GlblShadeInfo.LevelsPerColor = Levels = (ColorMapSize - 1) / NumColors;
    if (Levels == 0) {
	fprintf(stderr, "Too many colors (%d) for color map size (%d), exit.\n",
						NumColors, ColorMapSize);
	Poly3drExit(1);
    }

    /* Insert the background color in: */
    for (i = 0; TransColorTable[i][0] >= 0; i++)
	if (TransColorTable[i][0] == GlblShadeInfo.BackGroundColor)
	    break;
    if (TransColorTable[i][0] < 0) {
	fprintf(stderr,
	    "Undefined color required (%d) for background, default selected instead.\n",
	    AttrGetObjectColor(PObjects));
	AttrSetObjectColor(PObjects, GlblShadeInfo.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 - GlblShadeInfo.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.						     */
    PrepareAllObjects2(PObjects, Colors, NumColors);

    IritFree((VoidPtr) Colors);
}

/*****************************************************************************
* Scan all objects (first pass).					     *
*****************************************************************************/
static void PrepareAllObjects1(IPObjectStruct *PObjects, GifColorType *Colors,
							int *NumColors)
{
    while (PObjects) {
	InsertColor(PObjects, Colors, NumColors);
	PObjects = PObjects -> Pnext;
    }
}

/*****************************************************************************
* Scanning all the object in tree PBinTree and prepare them.		     *
*****************************************************************************/
static void InsertColor(IPObjectStruct *PObject, GifColorType *Colors,
							int *NumColors)
{
    int i, Red, Green, Blue, Color;

    if ((Color = AttrGetObjectColor(PObject)) != IP_ATTR_BAD_INT) {
	/* Translate the color into RGB form: */
	for (i = 0; TransColorTable[i][0] >= 0; i++)
	    if (TransColorTable[i][0] == Color)
		break;
	if (TransColorTable[i][0] < 0) {
	    fprintf(stderr,
		"Undefined color required (%d), default selected instead.\n",
		Color);
	    AttrSetObjectColor(PObject, GlblShadeInfo.DefaultColor);
	}
	AttrSetObjectRGBColor(PObject,
			      TransColorTable[i][1],
			      TransColorTable[i][2],
			      TransColorTable[i][3]);
    }

    /* Lets see if this color exists in table already, and if so do nothing: */
    AttrGetObjectRGBColor(PObject, &Red, &Green, &Blue);
    for (i = 0; i < *NumColors; i++) {
	if (Red == Colors[i].Red &&
	    Green == Colors[i].Green &&
	    Blue == Colors[i].Blue)
	    return;
    }

    /* Its a new color - add it to out colors table to allocate: */
    Colors[*NumColors].Red      = Red;
    Colors[*NumColors].Green    = Green;
    Colors[(*NumColors)++].Blue = Blue;
}

/*****************************************************************************
* Scan all objects (second pass).					     *
*****************************************************************************/
static void PrepareAllObjects2(IPObjectStruct *PObjects,
			       GifColorType *Colors, int NumColors)
{
    while (PObjects) {
	UpdateColorIndex(PObjects, Colors, NumColors);
	PObjects = PObjects -> Pnext;
    }
}

/*****************************************************************************
* Update object with the index to its color in the color map.		     *
*****************************************************************************/
static void UpdateColorIndex(IPObjectStruct *PObject, GifColorType *Colors,
							int NumColors)
{
    int i, Red, Green, Blue;

    /* Find this color in table (must be in it...): */
    AttrGetObjectRGBColor(PObject, &Red, &Green, &Blue);
    for (i = 0; i < NumColors; i++) {
	if (Red == Colors[i].Red &&
	    Green == Colors[i].Green &&
	    Blue == Colors[i].Blue)
	    break;
    }

    /* Color table hold this object color, with decreasing intensity, from   */
    /* this index up to (i + 1) * GlblShadeInfo.LevelsPerColor.		     */
    AttrSetObjectColor(PObject, i * GlblShadeInfo.LevelsPerColor + 1);
}

