#include "dkdata.h"

static u_char cbuf[16384];


static float max3(float r, float g, float b)
{
	if      ((r >= g) && (r >= b)) return(r);
	else if ((g >= r) && (g >= b)) return(g);
	else                           return(b);
}

static float min3(float r, float g, float b)
{
	if      ((r <= g) && (r <= b)) return(r);
	else if ((g <= r) && (g <= b)) return(g);
	else                           return(b);
}


void Cmap_New(cmap_t *cmap, int gcount, int ccount)
{
	int i;

	// malloc grads
	cmap->colors = (cmapColor_t **) malloc(gcount * sizeof(cmapColor_t *));

	if (cmap->colors == NULL)
	{
		Error("Cmap_New: unable to malloc cmap->colors.");
	}

	// now malloc each gradient
	for (i = 0; i < gcount; i++)
	{
		cmap->colors[i] = (cmapColor_t *) malloc(ccount * sizeof(cmapColor_t));

		if (cmap->colors[i] == NULL)
		{
			Error("Cmap_New: Unable to malloc cmap->colors[%d].", i);
		}
	}
 
	cmap->gradCount  = gcount;
	cmap->colorCount = ccount;
}


void Cmap_Free(cmap_t *cmap)
{
	int i;

	if (cmap->colors)
	{

	    for (i = 0; i < cmap->gradCount; i++)
	    {
		    if (cmap->colors[i])
		    {
			    free(cmap->colors[i]);
			    cmap->colors[i] = NULL;
			}
		}

		free(cmap->colors);
		cmap->colors = NULL;
	}

	cmap->gradCount  = 0;
	cmap->colorCount = 0;
}



void Cmap_Load(cmap_t *cmap, char *pal_nm, char *cmap_nm)
{
	bitmap_t bit_pal;
	FILE *f;
	int len;
	float r, g, b;
	int k;
	int grad, color;
	int ccount, gcount;

	if (!pal_nm || !cmap_nm)
	{
		Error("Cmap_Load: Invalid file name.");
	}

	// load up palette
	LoadBMP(pal_nm, &bit_pal);

	// make sure palette file is 8 bit
	if (!bit_pal.palette)
	{
		Error("No palette found in %s.", pal_nm);
	}

	// load up colormap
	if ((f = fopen(cmap_nm, "rb")) == NULL)
	{
		FreeBMP(&bit_pal);
		Error("Unable to open %s.", cmap_nm);
	}

	// read it in
	len = fread(cbuf, 1, 16384, f);

	if (len != 16384)
	{
		FreeBMP(&bit_pal); fclose(f);
		Error("Unable to read colormap.");
	}

	gcount = cmap->gradCount;
	ccount = cmap->colorCount;

	// insert rows into cmap
	for (grad = 0; grad < gcount; grad++)
	{
		for (color = 0; color < ccount; color++)
		{
			k = cbuf[(grad * ccount) + color];

			r = (float) bit_pal.palette[k].r;
			g = (float) bit_pal.palette[k].g;
			b = (float) bit_pal.palette[k].b;

			// insert gradient into cmap
			cmap->colors[grad][color].r = r;
			cmap->colors[grad][color].g = g;
			cmap->colors[grad][color].b = b;
		}

		printf("Inserted gradient %d.\n", grad);
	}

	fclose(f);
	FreeBMP(&bit_pal);
}

extern byte BestColor (int r, int g, int b, int start, int stop);

void Cmap_CreateAlphaTable (bitmap_t *bitmap)
{
	byte			*p;
	int				i, c;
	float			red, green, blue;

	// the alphamap begins immediately after the colormap
	p = bitmap->data + (256 * 64);

	for (i = 0; i < 255; i++)
	{
		for (c = 0 ; c < 255 ; c++)
		{
			red		= ((bitmap->palette->r + (c * 3)) * 0.33) + ((bitmap->palette->r + (i * 3)) * 0.66);
			green	= ((bitmap->palette->g + (c * 3 + 1)) * 0.33) + ((bitmap->palette->g + (i * 3 + 1)) * 0.66);
			blue	= ((bitmap->palette->b + (c * 3 + 2)) * 0.33) + ((bitmap->palette->b + (i * 3 + 2)) * 0.66);

			*p++ = BestColor(red, green, blue, 1, 254);
		}
		*p++ = 255;
	}

	for (c = 0; c < 256; c++)
		*p++ = 255;

}

void Cmap_Draw(colorList_t *pal, cmap_t *cmap, char *dest)
{
	bitmap_t bit;
	u_char *d;
	int base;
	int color, grad;
	int gcount, ccount, acount;
	float r, g, b;
	color_t *q;

	if (!dest)
	{
		Error("Cmap_Draw: Invalid dest.");
	}

	// make sure pal has no more than 256 colors
	if (pal->colorCount != 256)
	{
		Error("Cmap_Draw8: Palette must contain 256 colors.");
	}

	gcount = cmap->gradCount;
	ccount = cmap->colorCount;

	//take into account the alpha map
	acount = gcount + 256;

	// create new huge bitmap
	NewBMP(ccount, acount, 8, &bit);

	// set up palette
	for (color = 0; color < pal->colorCount; color++)
	{
		if ((q = Color_GetIndex(pal, color)) == NULL)
		{
			Error("Cmap_Draw8: unable to get color %d", color);
		}

        bit.palette[color].r = (u_char) floor(q->r);
		bit.palette[color].g = (u_char) floor(q->g);
		bit.palette[color].b = (u_char) floor(q->b);
	}

	d = bit.data;

    // draw color map
	for (grad = 0; grad < gcount; grad++)
	{
		// calculate base
		base = grad * ccount;

		for (color = 0; color < ccount; color++)
		{
		    r = cmap->colors[grad][color].r;
		    g = cmap->colors[grad][color].g;
			b = cmap->colors[grad][color].b;

			// find closest color
			q = Color_FindTuple(pal, r, g, b);

			d[base + color] = (u_char) q->index;
		}
	}

	Cmap_CreateAlphaTable (&bit);

	// save bitmap
    WriteBMP(dest, &bit);
	FreeBMP(&bit);
}

void RGB_To_HSV(float r, float g, float b, float *h, float *s, float *v)
{
	double max, min;

	// convert rgb from 0-255 - 0-1
	r *= (1.0/255.0);
	g *= (1.0/255.0);
	b *= (1.0/255.0);

	max = max3(r, g, b);
	min = min3(r, g, b);

	// get v
	*v = max;

	// get saturation
	*s = (max != 0.0) ? ((max - min) / max) : 0.0;

	// get hue
	if (*s == 0.0)
	{
		*h = 0.0;
	}
	else
	{
		float delta = max - min;

		if      (r == max) *h = (g - b) / delta;
		else if (g == max) *h = 2.0 + (b - r) / delta;
		else if (b == max) *h = 4.0 + (r - g) / delta;

		*h *= 60.0;

		if (*h < 0.0) *h += 360.0;
	}
}


void HSV_To_RGB(float *r, float *g, float *b, float h, float s, float v)
{
	// see if color is on the black-and-white center line
    if (s == 0.0)
	{
		*r = v;
		*g = v;
		*b = v;
	}
	else
	{
		float f, p, q, t;
		int i;

		if (h == 360.0) h = 0.0;

		h /= 60.0;
		i = floor(h);
		f = h - i;
		p = v * (1.0 - s);
		q = v * (1.0 - (s * f));
		t = v * (1.0 - (s * (1.0 - f)));

		switch (i)
		{
		    case 0: *r = v; *g = t; *b = p; break;
			case 1: *r = q; *g = v; *b = p; break;
			case 2: *r = p; *g = v; *b = t; break;
			case 3: *r = p; *g = q; *b = v; break;
			case 4: *r = t; *g = p; *b = v; break;
			case 5: *r = v; *g = p; *b = q; break;
		}
	}

	// reconvert rgb to 0-255 values
	*r *= 255;
	*g *= 255;
	*b *= 255;
}

static void Cmap_FadeBlack(cmap_t *cmap, colorList_t *pal, color_t *p, 
						   int color, int start, int count)
{
	int i;
	float h, s, v;
	float r, g, b;
	float theta, delta;
	float v_start;

	// convert this point to hsv
	RGB_To_HSV(p->r, p->g, p->b, &h, &s, &v);
	v_start = v;

	// is s is 0 then we know we have a gray scale
	if (s != 0.0)
	{
	    // calculate angle between s and v
	    theta = atan(v/s);
	}

	// compute increment from 1.0 to 0.0
	delta = v_start / ( ((float) count) - ((float) start));

	for (v = v_start, i = start; i < count; v -= delta, i++)
	{
		if (s != 0.0)
		{
		    // calc new s
		    s = v / tan(theta);
		}

		// convert new hsc back into rgb
		HSV_To_RGB(&r, &g, &b, h, s, v);
		// printf("rgb:(%f %f %f) hsv:(%f %f %f)\n", r, g, b, h, s, v);

		cmap->colors[i][color].r = r;
		cmap->colors[i][color].g = g;
		cmap->colors[i][color].b = b;
	}
}

static void Cmap_FadeNone(cmap_t *cmap, color_t *p, int color, 
						  int start, int count)
{
	int i;

	for (i = start; i < count; i++)
	{
		cmap->colors[i][color].r = p->r;
		cmap->colors[i][color].g = p->g;
		cmap->colors[i][color].b = p->b;
	}
}

void Cmap_Create(colorList_t *pal, cmap_t *cmap)
{
	int i;
	color_t *p;

	// make sure clist has 256 or less colors
	if (pal->colorCount != 256)
	{
		Error("Cmap_Create: Palette must contain 256 colors.");
	}

	for (p = pal->colorHead, i = 0; p != NULL; p = p->n, i++)
	{
		cmap->colors[31][i].r = p->r;
		cmap->colors[31][i].g = p->g;
		cmap->colors[31][i].b = p->b;

		// if this is a bright color then don't fade
		if (p->flags & CF_Bright)
		{
			Cmap_FadeNone(cmap, p, i, 0, 64);
		}
		else
		{
			// fade color to black using linear scale
			Cmap_FadeBlack(cmap, pal, p, i, 32, 64);

			// do fade to white latter
			Cmap_FadeNone(cmap, p, i, 0, 31);
		}
	}

	// draw cmap
	// Cmap_Draw(cmap, "cmap.bmp");

	printf("Created color map.\n");
}



void Cmap_Reduce(cmap_t *cmap, colorList_t *pal)
{
	int grad, color;
	color_t *q;
	float r, g, b;

	for (grad = 0; grad < cmap->gradCount; grad++)
	{
		for (color = 0; color < cmap->colorCount; color++)
		{
			r = cmap->colors[grad][color].r;
			g = cmap->colors[grad][color].g;
			b = cmap->colors[grad][color].b;

			// find closest values in palette
			q = Color_FindTuple(pal, r, g, b);

			cmap->colors[grad][color].r = q->r;
			cmap->colors[grad][color].g = q->g;
			cmap->colors[grad][color].b = q->b;
		}
	}
}


// write out colormap in 8 bit format

void Cmap_Write(colorList_t *pal, cmap_t *cmap, char *dest)
{
	if (!dest)
	{
		Error("Invalid dest name.");
	}

	if (pal->colorCount != 256)
	{
		Error("Cmap_Write: Palette must be 256 colors.");
	}

	// write out cmap in 8 bit format
	Cmap_Draw(pal, cmap, dest);

	printf("Wrote colormap to %s.\n", dest);
}



//////////////////////////////////////////////////////////////
/// New Interpolate code
//////////////////////////////////////////////////////////////



static void Cmap_ExitRGB(float xs, float ys, float zs, float *xe, float *ye, float *ze)
{
	float x1, y1, z1;
	float x2, y2, z2;
	float r;
	float x_end, y_end, z_end;

	x1 = 0.0;
	y1 = 0.0;
	z1 = 0.0;

	x2 = (float) xs;
	y2 = (float) ys;
	z2 = (float) zs;

	for (r = 0.0;; r += 0.001)
	{
	    x_end = x1 + (r * (x2 - x1));
	    y_end = y1 + (r * (y2 - y1));
	    z_end = z1 + (r * (z2 - z1));	

		if ((x_end >= 255.0) || (y_end >= 255.0) || (z_end >= 255.0))
		{
		    if (x_end >= 255.0) x_end = 255.0;
		    if (y_end >= 255.0) y_end = 255.0;
		    if (z_end >= 255.0) z_end = 255.0;

			break;
		}
	}

	*xe = x_end;
	*ye = y_end;
	*ze = z_end;
}

void Cmap_CreateInterpolate(colorList_t *pal, cmap_t *cmap)
{
	int i, j, k;
	color_t *p;
	float r;

	// make sure clist has 256 or less colors
	if (pal->colorCount != 256)
	{
		Error("Cmap_Create: Palette must contain 256 colors.");
	}

	for (p = pal->colorHead, i = 0; p != NULL; p = p->n, i++)
	{
		// colors that don't fade
		if (p->flags & CF_Bright)
		{
			for (j = 0; j < 64; j++)
			{
				cmap->colors[j][i].r = p->r;
				cmap->colors[j][i].g = p->g;
				cmap->colors[j][i].b = p->b;
			}

			// don't fade color
			continue;
		}

		// figure out where a line from 0 to this rgb value exits
		// the rgb cube
		// Cmap_ExitRGB(p->r, p->g, p->b, &r_end, &g_end, &b_end);

		// upper half
		for (j = 0; j < 22; j++)
		{
			cmap->colors[j][i].r = p->r;
			cmap->colors[j][i].g = p->g;
			cmap->colors[j][i].b = p->b;
		}

		// fade to black on lower half
		for (j = 0, k = 63; j < 42; j++, k--)
		{
			r = (float) j / 41.0;

			cmap->colors[k][i].r = r * p->r;
			cmap->colors[k][i].g = r * p->g;
			cmap->colors[k][i].b = r * p->b;
		}
	}
	printf("Created color map.\n");
}