#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "../util/error.h"
#include "../util/memory.h"
#include "../util/gui.h"
#include "../util/sparsearray.h"

#define VColor_IMPORT
#include "VColor.h"


struct VColor_Type {
	/** Index to CLUT in gui.c. */
	int index;
	/** 0 = no depth cueing, 1 = yes. */
	unsigned char is_depth_cueing;
	/** RGB color actually requested by client. */
	unsigned char red;
	unsigned char green;
	unsigned char blue;
};


/**
 * Sparse array of colors. The index is built by packing the is_depth_cueing,
 * red, green and blue fields of the color, which are univocal of that color.
 * Lazy initialized as the first color gets requested.
 */
static sparsearray_Type *colors;


static void VColor_cleanup()
{
	double quality = sparsearray_getAverageComparisonsPerSearch(colors);
	if( quality > 2 )
		sparsearray_report(colors, "Warning: VColor module performances issue");
	memory_dispose(colors);
	colors = NULL;
}


VColor_Type * VColor_getByRGB(int red, int green, int blue, int is_depth_cueing)
{
	if( (red&255)!=red || (green&255)!=green || (blue&255)!=blue )
		error_internal("invalid RGB: %d,%d,%d", red, green, blue);
	is_depth_cueing = is_depth_cueing? 1 : 0; // normalize flag to 0 or 1
	
	// Search for existing color in the hash map:
	if( colors == NULL ){
		colors = sparsearray_new(1234, 0);
		memory_registerCleanup(VColor_cleanup);
	}
	int packed = (is_depth_cueing << 24) | (red << 16) | (green << 8) | blue;
	VColor_Type *c = sparsearray_get(colors, packed);
	if( c != NULL )
		return c;
	
	// Add new color to the colors table:
	c = memory_allocate(sizeof(struct VColor_Type), NULL);
	c->index = gui_getColorIndex(NULL, red, green, blue);
	c->is_depth_cueing = is_depth_cueing;
	c->red = red;
	c->green = green;
	c->blue = blue;
	sparsearray_set(colors, packed, c);
	assert( sparsearray_get(colors, packed) == c );
	return c;
}


VColor_Type * VColor_getByName(char *name, int is_depth_cueing)
{
	int red, green, blue;
	if( ! gui_parseColorString(name, &red, &green, &blue) )
		error_internal("invalid color specification: %s", name);
	return VColor_getByRGB(red, green, blue, is_depth_cueing);
}

int VColor_getIndex(VColor_Type *c){ return c->index; }
int VColor_isDepthCueing(VColor_Type *c){ return c->is_depth_cueing; }
int VColor_getRed(VColor_Type *c){ return c->red; }
int VColor_getGreen(VColor_Type *c){ return c->green; }
int VColor_getBlue(VColor_Type *c){ return c->blue; }


char * VColor_getName(VColor_Type *c)
{
	static char s[8];
	sprintf(s, "#%02u%02u%02u", c->red, c->green, c->blue);
	return s;
}
