#include "dkdata.h"

extern int color_stat;

static void Color_AddTail(colorList_t *clist, color_t *node)
{
	if (color_stat)
	{
		if (color_stat == 10000)  
		{
			printf(".");
			color_stat = 0;
		}

		color_stat++;
	}


	// if null then add at head
	if ((clist->colorHead == NULL) && (clist->colorTail == NULL))
	{
		clist->colorHead  = node;
		clist->colorTail  = node;
		clist->colorCount = 1;

		return;
	}

	// stick on end
	clist->colorTail->n = node;
	node->p             = clist->colorTail;
	clist->colorTail    = node;

	clist->colorCount++;
}


void Color_AddHead(colorList_t *clist, color_t *node)
{
	if (color_stat)
	{
		if (color_stat == 10000) 
		{
			printf(".");
			color_stat = 0;
		}

		color_stat++;
	}

	// if null then add at head
	if ((clist->colorHead == NULL) && (clist->colorTail == NULL))
	{
		clist->colorHead  = node;
		clist->colorTail  = node;
		clist->colorCount = 1;

		return;
	}

	// stick on front
	node->n             = clist->colorHead;
	clist->colorHead->p = node;
	clist->colorHead    = node;

	clist->colorCount++;
}


void Color_DelNode(colorList_t *clist, color_t *node, int freeNode)
{
    // check to see if this is the last node
	if ((clist->colorHead == node) && (clist->colorTail == node))
	{
		if (freeNode) free(node);
		else node->n = node->p = NULL;

		clist->colorHead  = NULL;
		clist->colorTail  = NULL;
		clist->colorCount = 0;

		return;
	}

	clist->colorCount--;

	// see if node is head
	if (clist->colorHead == node)
	{
		clist->colorHead    = clist->colorHead->n;
		clist->colorHead->p = NULL;

		if (freeNode) free(node);
		else node->n = node->p = NULL;
		return;
	}

	// see if node is tail
	if (clist->colorTail == node)
	{
		clist->colorTail    = clist->colorTail->p;
		clist->colorTail->n = NULL;

	    if (freeNode) free(node);
		else node->n = node->p = NULL;
		return;
	}

	// ok we node is in middle
	node->p->n = node->n;
	node->n->p = node->p;

	if (freeNode) free(node);
	else node->n = node->p = NULL;
}



color_t *Color_RemoveNode(colorList_t *clist, color_t *node)
{
	color_t *ret;

    // check to see if this is the last node
	if ((clist->colorHead == node) && (clist->colorTail == node))
	{
		node->n = node->p = NULL;

		clist->colorHead  = NULL;
		clist->colorTail  = NULL;
		clist->colorCount = 0;

		return(NULL);
	}

	clist->colorCount--;

	// see if node is head
	if (clist->colorHead == node)
	{
		clist->colorHead    = clist->colorHead->n;
		clist->colorHead->p = NULL;

		node->n = node->p = NULL;
		return(clist->colorHead);
	}

	// see if node is tail
	if (clist->colorTail == node)
	{
		clist->colorTail    = clist->colorTail->p;
		clist->colorTail->n = NULL;

		node->n = node->p = NULL;
		return(NULL);
	}

	// ok we node is in middle
	ret = node->n;

	node->p->n = node->n;
	node->n->p = node->p;

	node->n = node->p = NULL;
	return(ret);
}


color_t *Color_DupNode(color_t *p)
{
	color_t *newNode;

	if ((newNode = malloc(sizeof(color_t))) == NULL)
	{
		Error("Color_DupNode: malloc failed.");
	}

	memcpy(newNode, p, sizeof(color_t));

	newNode->p = newNode->n = newNode->close = NULL;
	return(newNode);
}


void Color_Set(colorList_t *dest, colorList_t *source)
{
	memcpy(dest, source, sizeof(colorList_t));
}



void Color_Init(colorList_t *clist)
{
	clist->colorCount = 0;
	clist->colorHead  = NULL;
	clist->colorTail  = NULL;
}

int Color_Len(colorList_t *clist)
{
	color_t *p;
	int count = 0;

	for (p = clist->colorHead; p != NULL; p = p->n) count++;

	return(count);
}


int Color_Count(colorList_t *clist, float r, float g, float b)
{
	color_t *p;
	int count = 0;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if ((p->r == r) && (p->g == g) && (p->b == b)) count++;
	}

	return(count);
}


static void Color_FreeColors(color_t *head)
{
	if (head == NULL) return;

	Color_FreeColors(head->n);
	free(head);
}


void Color_Free(colorList_t *clist)
{
	// free colors
	Color_FreeColors(clist->colorHead);

	clist->colorCount = 0;
	clist->colorHead  = NULL;
	clist->colorTail  = NULL;
}



void Color_ForceTuple(colorList_t *clist, int index, float r, float g, 
					  float b, unsigned long flags, int weight)
{
	color_t *p;

	// create new color node
	if ((p = malloc(sizeof(color_t))) == NULL)
	{
		Error("Color_AddTuple2: malloc failed.");
	}

	// fill in new node
	p->r        = r;
	p->g        = g;
	p->b        = b;
	// p->count    = 1.0;
	p->count    = (float) weight;
	p->index    = index;
	p->flags    = flags;
	p->p = p->n = NULL;


	// add color node
	Color_AddTail(clist, p);
}



void Color_AddTuple(colorList_t *clist, float r, float g, float b, int weight)
{
	color_t *p;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if ((p->r == r) && (p->g == g) && (p->b == b))
		{
			// p->count++;
			p->count += (float) weight;
			return;
		}
	}

	Color_ForceTuple(clist, 500, r, g, b, 0L, weight);
}



color_t *Color_FindTuple(colorList_t *clist, float r, float g, float b)
{
	color_t *p;
	color_t *found = NULL;
	float sq2 = 9999999.0;
	float sq1;


	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		// find least square
		sq1 = ( ((p->r - r) * (p->r - r)) +
			    ((p->g - g) * (p->g - g)) +
				((p->b - b) * (p->b - b)) );

		// see if we have an exact match
		if (sq1 == 0.0) return(p);

		if (sq1 < sq2)
		{
			sq2   = sq1;
			found = p;
		}
	}

	return(found);
}


color_t *Color_FindHeavy(colorList_t *clist)
{
	color_t *p;
	color_t *found = NULL;
	float sq2 = -1.0;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if (p->count > sq2)
		{
			sq2   = p->count;
			found = p;
		}
	}

	return(found);
}


color_t *Color_FindHue(colorList_t *clist)
{
	color_t *p;
	color_t *found = NULL;
	float    hue = -1.0;
	float    h, s, v;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		// translate this color into hsv
		RGB_To_HSV(p->r, p->g, p->b, &h, &s, &v);

		if (h > hue)
		{
			hue   = h;
			found = p;
		}
	}

	return(found);
}


color_t *Color_FindIndex(colorList_t *clist)
{
	color_t *p;
	color_t *found = NULL;
	int sq2 = -1;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if (p->index > sq2)
		{
			sq2   = p->index;
			found = p;
		}
	}

	return(found);
}


color_t *Color_FindFlag(colorList_t *clist, unsigned long flag)
{
	color_t *p;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if (p->flags & flag) return(p);
	}

	return(NULL);
}


color_t *Color_GetIndex(colorList_t *clist, int index)
{
	color_t *p;

	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		if (p->index == index) return(p);
	}

	return(NULL);
}


void Color_SortByWeight(colorList_t *clist)
{
	colorList_t tlist;
	color_t *q;

	// create temp colorlist
	Color_Init(&tlist);

	// mark all nodes in original list as not found
	// Color_ClearFound(clist);

	for (;;)
	{
		// find heavyest
		if ((q = Color_FindHeavy(clist)) == NULL) break;

		// delete this node
		Color_DelNode(clist, q, 0);

		// add this node to our new list
		Color_AddHead(&tlist, q);
	}

	// ok tlist now holds sorted list, copy it back to clist
	Color_Set(clist, &tlist);
}



void Color_SortByIndex(colorList_t *clist)
{
	colorList_t tlist;
	color_t *q;

	// create temp colorlist
	Color_Init(&tlist);

	for (;;)
	{
		// find heavyest
		if ((q = Color_FindIndex(clist)) == NULL) break;

		// delete this node
		Color_DelNode(clist, q, 0);

		// add this node to our new list
		Color_AddHead(&tlist, q);
	}

	// ok tlist now holds sorted list, copy it back to clist
	Color_Set(clist, &tlist);
}



void Color_AddFile(colorList_t *clist, char *source)
{
	bitmap_t bit;
	u_char   r,g,b;
	int      w, h;

	if (!source)
	{
		Error("Color_AddFile: Invalid source name: %s", source);
	}

	// load up bitmap
	LoadBMP(source, &bit);

	printf("Adding %s. (%d bit)\n", source, bit.bpp);

	// if file is 8 bit then just load palette
	if ((bit.bpp == 8) && (bit.palette))
	{
		for (h = 0; h < 256; h++)
		{
			r = bit.palette[h].r;
			g = bit.palette[h].g;
			b = bit.palette[h].b;

			// add tuple to palette list
			Color_AddTuple(clist, (float) r, (float) g, (float) b, 1);
		}
	}

	else if (bit.bpp == 24)
	{
	    for (h = 0; h < bit.height; h++)
	    {
		    printf("Adding row %d/%d.\n", h, bit.height);

		    for (w = 0; w < bit.width; w++)
		    {
			    r = bit.data[(h * 960) + (w * 3) + 0];
			    g = bit.data[(h * 960) + (w * 3) + 1];
			    b = bit.data[(h * 960) + (w * 3) + 2];

			    // add tuple to color list
			    Color_AddTuple(clist, (float) r, (float) g, (float) b, 1);
		    }
	    } 
	}

	else
	{
		Error("Color_AddFile: Expected 8 or 24 bit image.");
	}

    FreeBMP(&bit);
	//printf("added %s to color list (%d colors total).\n", source, clist->colorCount);
}


int Color_NextIndex(colorList_t *clist)
{
	int index;

	for (index = 0;; index++)
	{		
		// see if this index exists in list
		if (Color_GetIndex(clist, index) == NULL)
		{
			return(index);
		}
	}

}


void Color_Fill(colorList_t *clist)
{
	// fill rest of clist with perm gray colors
	// this will rarly need to be called
	while (clist->colorCount < 256)
	{
		Color_ForceTuple(clist, 1, 128.0, 128.0, 128.0, CF_Bright, 1);
	}
}



void Color_Index(colorList_t *clist)
{
	colorList_t tlist;
	color_t *q;

	Color_Init(&tlist);

	// get permenant colors out 
	for (;;)
	{
		if ((q = Color_FindFlag(clist, CF_Perm)) == NULL) break;

		// take node out of clist
		// delete this node
		Color_DelNode(clist, q, 0);

		// add this node to our new list
		Color_AddHead(&tlist, q);
	}

	// ok we have inserted nodes that have an index,
	// take out rest of nodes, give them an index
	// and insert them
	for (;;)
	{
		if ((q = Color_FindHue(clist)) == NULL) break;

		// take this node out of clist
		Color_DelNode(clist, q, 0);

		// find next index number for this node
		q->index = Color_NextIndex(&tlist);

		// add to new list
		Color_AddHead(&tlist, q);
	}

    // ok tlist now holds sorted list, copy it back to clist
	Color_Set(clist, &tlist);
}



/*
void Color_Draw(colorList_t *clist, char *dest)
{
	cmap_t   tmap;
	color_t *p;
	int colorCount;
	int rows;
	int grad, color;
	int count;
	int max = 20000;

	// dont plot more than max colors
	if (clist->colorCount < max) colorCount = clist->colorCount;
	else                         colorCount = max;

	rows = (colorCount / 256) + 1;

	// create a temp cmap for drawing
	Cmap_New(&tmap, rows, 256);

	// draw
	grad  = 0;
	color = 0;

    for (p = clist->colorHead,count=0; (p != NULL) && (count < max); p = p->n, count++)
	{
		// printf("%d, %d\n", grad, color);

		tmap.colors[grad][color].r = p->r;
		tmap.colors[grad][color].g = p->g;
		tmap.colors[grad][color].b = p->b;

		color++;

		if (color >= 256)
		{
			grad++;
			color = 0;
		}
	}

	Cmap_Draw(&tmap, dest);
	Cmap_Free(&tmap);

	printf("Drew %d (out of %d) colors to %s.\n", count, clist->colorCount, dest);
}
*/

void Color_SaveList(colorList_t *clist, char *fileName)
{
	FILE *f;
	color_t *p;
	int count = 0;

	if (clist->colorCount <= 0)
	{
		Error("Color_SaveList: No colors to save.");
	}

	if ((f = fopen(fileName, "wb")) == NULL)
	{
		Error("Color_SaveList: Unable to open %s.", fileName);
	}

	// write out header
	fwrite(clist, sizeof(colorList_t), 1, f);

	// write out basedir
//	fwrite(basedir,  1, 100, f);
	fwrite(cmapname, 1, 100, f);

	// write out nodes
	for (p = clist->colorHead; p != NULL; p = p->n)
	{
		fwrite(p, sizeof(color_t), 1, f);
		count++;
	}

	fclose(f);

	printf("Saved %d nodes to %s.\n", count, fileName);
}


void Color_LoadList(colorList_t *clist, char *fileName)
{
	FILE *f;
	colorList_t header;
	color_t     node, *p;
	int count = 0;


	if ((f = fopen(fileName, "rb")) == NULL)
	{
		Error("Color_LoadList: Unable to open %s.", fileName);
	}

    Color_Init(clist);

	// read in header
	fread(&header, sizeof(header), 1, f);

	// read in basedir
//	fread(basedir,  1, 100, f);
	fread(cmapname, 1, 100, f);


	// read in nodes
	while (fread(&node, sizeof(node), 1, f) == 1)
	{
		// create a new node
		p = Color_DupNode(&node);

		// add node to list
		Color_AddHead(clist, p);
		count++;
	}

	fclose(f);

	if (header.colorCount != clist->colorCount)
	{
		Error("Color_LoadList: Colorcount does not match.");
	}


	printf("Loaded %d colors from %s.\n", count, fileName);
}



void Color_AddOctree(colorList_t *clist, octree_t *otree)
{
	int i;

	// if we are at a leaf then add this node to clist
	if (otree->count)
	{
        Color_ForceTuple(clist, 500, otree->r, otree->g, otree->b, 0L, otree->count);
		return;
	}

	// process children
	for (i = 0; i < 8; i++)
	{
		if (otree->octant[i])
		{
			Color_AddOctree(clist, otree->octant[i]);
		}
	}
}


////////////////////////////////////////////////////////////////
/// Theoritical colormap
////////////////////////////////////////////////////////////////


#ifdef OLD
static float Interval(float x1, float x2, float r)
{
	float x;

	x = x1 + (r * (x2 - x1));
	return(x);
}

void Color_Gradient(colorList_t *clist, color_t *color)
{
	float r_base, g_base, b_base;
	float r_new, g_new, b_new;
	int i;

	r_base = color->r;
	g_base = color->g;
	b_base = color->b;

    // assume the color we got in is 32, fade to
	// white on 0-31 and fade to black on 33-63

	// fade to white
	for (i = 0; i < 32; i++)
	{
		r_new = Interval(r_base, 255.0, (float) i / 31.0);
		g_new = Interval(g_base, 255.0, (float) i / 31.0);
		b_new = Interval(b_base, 255.0, (float) i / 31.0);

		// out new color in color list
		Color_ForceTuple(clist, 500, r_new, g_new, b_new, 
			color->flags, color->count);
	}

	// fade to black
	for (i = 0; i < 32; i++)
	{
		r_new = Interval(0.0, r_base, (float) i / 31.0);
		g_new = Interval(0.0, g_base, (float) i / 31.0);
		b_new = Interval(0.0, b_base, (float) i / 31.0);

		Color_ForceTuple(clist, 500, r_new, g_new, b_new,
			color->flags, color->count);
	}
}
#endif



void Color_Gradient(colorList_t *clist, color_t *color)
{
	float r_new, g_new, b_new;
	int i;
	float r;

	for (i = 0; i < 32; i++)
	{
		r = (float) i / 31.0;

		r_new = r * color->r;
		g_new = r * color->g;
		b_new = r * color->b;

		// out new color in color list
		Color_ForceTuple(clist, 500, r_new, g_new, b_new, 
			color->flags, color->count);
	}
}
	


void Color_ColorMap(colorList_t *clist)
{
	colorList_t tlist;
	color_t *q;

	// create temp colorlist
	Color_Init(&tlist);

	for (;;)
	{
		// take first node from list, interpolate and 
		// put on tlist
		if ((q = clist->colorHead) == NULL) break;

		// if this color has any flagfs set then just out into 
        // new color list
		if (q->flags)
		{
            Color_AddHead(&tlist, Color_DupNode(q));
		}

		// otherwise get colormap for this color
		// and add results to tlist
		else
		{
		    // interpolate and stick on new list
		    Color_Gradient(&tlist, q);	
		}

		// take this node off clist
		Color_DelNode(clist, q, 1);
	}

	// ok tlist now holds sorted list, copy it back to clist
	Color_Set(clist, &tlist);
}


	

