/* -----------------------------------------------------------------------------
*
*  npgldraw.c
*
*  ANTz - realtime 3D data visualization tools for the real-world, based on NPE.
*
*  ANTz is hosted at http://openantz.com and NPE at http://neuralphysics.org
*
*  Written in 2010-2019 by Shane Saxon - saxon@openantz.com
*
*  Please see main.c for a complete list of additional code contributors.
*
*  To the extent possible under law, the author(s) have dedicated all copyright 
*  and related and neighboring rights to this software to the public domain
*  worldwide. This software is distributed without any warranty.
*
*  Released under the CC0 license, which is GPL compatible.
*
*  You should have received a copy of the CC0 Public Domain Dedication along
*  with this software (license file named LICENSE.txt). If not, see
*  http://creativecommons.org/publicdomain/zero/1.0/
*
* --------------------------------------------------------------------------- */

#include "npcolor.h"

//zz pale
//------------------------------------------------------------------------------
void npInitColor( void* dataRef )
{
	pData data = (pData) dataRef;

	return;
}

/*

//MB//
// converts the ID value to an RGB value for picking objects by color
//------------------------------------------------------------------------------
void npIDtoRGB( int id, int* r, int* g, int* b )
{
	int remain = id;
	
	*r = remain / (256*256);	remain = remain - *r * (256*256);
	*g = remain / 256;			remain = remain - *g * 256;
	*b = remain;
}

//convert RGB pixel value back to the encoded node ID
int npRGBtoID( int r, int g, int b )
{
	int id;
	id = (256*256)*r + 256*g + b;

	return id;
}
//MB-End//



//zz debug, update to allow loading palette from CSV file and user defined color
// sets the node RGB color based on index, preserves existing alpha value
//------------------------------------------------------------------------------
void npSetIndexColor( NPubyteRGBA *color, int *colorIndex )
{	
	int index = *colorIndex;
									//kNPpaletteSize stored in nptypes.h
	static const GLubyte colorPalette[kNPpaletteSize][3] = {
											{50,  101, 101},
											{0,   255, 0},
											{255,   0, 0},
											{0,     0, 255},
											{255, 255, 0},
											{152,   0, 255},
											{255, 168, 0},
											{0,   255, 255},
											{255,   0, 255},
											{0,   153, 0},
											{185, 153, 102},
											{255, 180, 255},
											{0,   152, 255},
											{185, 255, 0},
											{152,   0, 0},
											{127, 127, 127},
											{127, 127, 255},
											{197,  82, 0},
											{0,     0, 0},
											{255, 255, 255}
											};
	//re-maps out of bounds colorIndex
	if (index >= kNPpaletteSize)
	{
		while (index >= kNPpaletteSize)
			index -= kNPpaletteSize;	//subtracts palette sized increments
		*colorIndex = index;
	}
	else if (index < 0)
	{
		while (index < 0)
			index += kNPpaletteSize;
		*colorIndex = index;			//adds palette sized increments		
	}

	//now set the color
	color->r = colorPalette[index][0];
	color->g = colorPalette[index][1];
	color->b = colorPalette[index][2];
}
*/


void npPostPalette( int paletteID, void* dataRef)
{
	int index = 0;
	static char msg[64] = {'\0'};

	const static char palette[14][20] = {
		{"20 distinct colors"},
		{"rainbow heatmap"},
		{"blue to green"},
		{"green to red"},
		{"blue to red"},
		{"purple to yellow"},
		{"black to red"},
		{"black to green"},
		{"black to blue"},
		{"white to red"},
		{"white to green"},
		{"white to blue"},
		{"greyscale"},
		{"custom"}
		};

	// divide by 2 to handle the inverted palettes
	index = paletteID / 2;

	if( index > 13 ) // out of bounds is set to 'custom'
		index = 13;

	/// odd pallete_id's use inverted colors
	if( paletteID & 1 )
		sprintf( msg, "palette_id: %d  desc: %s inverted",
						paletteID, &palette[index][0] );
	else // even
		sprintf( msg, "palette_id: %d  desc: %s", 
						paletteID, &palette[index][0] );
	
	npPostMsg( msg, kNPmsgCtrl, dataRef );
}

#define kNPpaletteCount 26
/// @param paletteID refers to which color palette table to use
/// @return returns the number of palette tables
int npPaletteCount( void* dataRef )
{
	return kNPpaletteCount;	//default palette has 20 colors
}

/// @param paletteID refers to which color palette table to use
/// @return returns the number of colors in the palette table
int npPaletteSize( int paletteID )
{
	/// original palette size was 20 colors
	if( paletteID <= 1 )
		return 20;		//used for both pallete 0 and 1 (inverted)

	return 128;	///< Default palette size is 128 colors
}

/// @ todo support loading palettes from CSV files and user defined color
// sets the node RGB color based on index, preserves existing alpha value
//------------------------------------------------------------------------------
void npSetIndexColor( NPubyteRGBA *color, int paletteID, int colorID )
{		
	int paletteSize = 20;	// default used for palletID 0 and 1

	static const NPubyte colorPalette[kNPpaletteSize][3] = {
											{50,  101, 101},
											{0,   255, 0},
											{255,   0, 0},
											{0,     0, 255},
											{255, 255, 0},
											{152,   0, 255},
											{255, 168, 0},
											{0,   255, 255},
											{255,   0, 255},
											{0,   153, 0},
											{185, 153, 102},
											{255, 180, 255},
											{0,   152, 255},
											{185, 255, 0},
											{152,   0, 0},
											{127, 127, 127},
											{127, 127, 255},
											{197,  82, 0},
											{0,     0, 0},
											{255, 255, 255}
											};

//	if( !palette )
//		palette = currentPalette;

	/*		void npColorFade( pNPubyteRGBA colorDest, 
								pNPubyteRGBA colorA,
								pNPubyteRGBA colorB,
								float fade );
			npColorFade( &color, Purple, lightPink, colorID * 16 );
	*/

	
	paletteSize = npPaletteSize( paletteID );

	/// non-destrutive looped mapping of the color_id into the palette range
	if( colorID >= paletteSize )
		while( colorID >= paletteSize)
			colorID -= paletteSize;
	else if( colorID < 0 )
		while( colorID < 0 )
			colorID += paletteSize;	

	switch( paletteID )
	{
		case 0 : // use indexed color array above
		case 1 : // odd cases are inverted after switch statement

			color->r = colorPalette[colorID][0];
			color->g = colorPalette[colorID][1];
			color->b = colorPalette[colorID][2];
			break;
		case 2 :		///< rainbow heatmap
		case 3 :		///< rainbow heatmap inverted

				if( colorID < 16 )			// light-pink to purple
				{
					color->r = 255 - colorID * 8;
					color->g = 127 - colorID * 8;
					color->b = 255 - colorID * 8;
				}
				else if( colorID < 32 )		// purple to blue-green
				{
					color->r = 255 - colorID * 8;
					color->g = (colorID - 16) * 8;
					color->b = (colorID - 16) * 8 + 127;
				}
				else if( colorID < 48 )		// blue-green to green
				{
					color->r = 0;
					color->g = 127 + (colorID - 32) * 8;
					color->b = 255 - (colorID - 32) * 16;
				}
				else if( colorID < 64 )		// green to yellow
				{
					color->r = (colorID - 48) * 16;
					color->g = 255;
					color->b = 0;
				}
				else if( colorID < 80 )		// yellow to orange
				{
					color->r = 255;
					color->g = 255 - (colorID - 64) * 8;
					color->b = 0;
				}
				else if( colorID < 96 )		// orange to red
				{
					color->r = 255;
					color->g = 127 - (colorID - 80) * 8;
					color->b = 0;
				}
				else if( colorID < 112 )	// red to dark-red
				{
					color->r = 255 - (colorID - 96) * 8;
					color->g = 0;
					color->b = 0;
				}
				else if( colorID < 128 )	// dark-red to light-pink
				{
					color->r = 127 + (colorID - 112) * 8;
					color->g = (colorID - 112) * 8;
					color->b = (colorID - 112) * 16;
				}
				break;
			case 4 :	// blue to green
			case 5 :	// inverted
				color->r = 0;
				color->g = colorID * 2;
				color->b = 255 - colorID * 2;
				break;
			case 6 :	// green to red
			case 7 :	// inverted
				color->r = colorID * 2;
				color->g = 255 - colorID * 2;
				color->b = 0;
				break;
			case 8 :	// blue to red
			case 9 :	// ...
				color->r = colorID * 2;
				color->g = 0;
				color->b = 255 - colorID * 2;
				break;
			case 10 :	// purple to yellow
			case 11 :
				color->r = 255;
				color->g = colorID * 2;
				color->b = 255 - colorID * 2;
				break;
			case 12 :	// black to red
			case 13 :
				color->r = colorID * 2;
				color->g = 0;
				color->b = 0;
				break;
			case 14 :	// black to green
			case 15 :
				color->r = 0;
				color->g = colorID * 2;
				color->b = 0;
				break;
			case 16 :	// black to blue
			case 17 :
				color->r = 0;
				color->g = 0;
				color->b = colorID * 2;
				break;
			case 18 :	// white to red
			case 19 :
				color->r = 255;
				color->g = 255 - colorID * 2;
				color->b = 255 - colorID * 2;
				break;
			case 20 :	// white to green
			case 21 :
				color->r = 255 - colorID * 2;
				color->g = 255;
				color->b = 255 - colorID * 2;
				break;
			case 22 :	// white to blue
			case 23 :
				color->r = 255 - colorID * 2;
				color->g = 255 - colorID * 2;
				color->b = 255;
				break;
			case 24 :	// black to white greyscale
			case 25 :
				color->r = colorID * 2;
				color->g = colorID * 2;
				color->b = colorID * 2;
				break;
		default :
				color->r = colorPalette[colorID][0];
				color->g = colorPalette[colorID][1];
				color->b = colorPalette[colorID][2];
			break;
	}

	/// if odd numbered paletteID then invert the color
	if( paletteID & 1)
	{ 
		color->r = 255 - color->r;
		color->g = 255 - color->g;
		color->b = 255 - color->b;
	}
}

// NOTE x,y,z does not refer to XYZ color unless type = kNPcolorXYZtoRGB
NPfloatRGBA npColorPicker (float x, float y, float z, int type, void* data);
float npHueToRGB (float v1, float v2, float Hue);
void npHSLtoRGB (float H, float S, float L, float* R, float* G, float* B); //pNPfloatRGB rgbOut

//------------------------------------------------------------------------------
float npHueToRGB( float v1, float v2, float vH )             //Function Hue_2_RGB
{
   if ( vH < 0.0 ) vH += 1.0f;
   if ( vH > 1.0 ) vH -= 1.0f;
   if ( (6.0f * vH) < 1.0f ) return (v1 + (v2 - v1) * 6.0f * vH);
   if ( (2.0f * vH) < 1.0f ) return (v2);
   if ( (3.0f * vH) < 2.0f ) return (v1 + (v2 - v1) * ((2.0f/3.0f) - vH) * 6.0f);
   return ( v1 );
}

// maps HSL (Hue Saturation Luminance) to RGB 0 to 255 ubyte color space
// HSL is also called HSV or other variants such as HSB
// calls npHueToRGB() for each component
//------------------------------------------------------------------------------
void npHSLtoRGB8 (float H, float S, float L,
				  unsigned char* R, unsigned char* G, unsigned char* B)
{
//	NPfloatRGB rgb;
//	pNPfloat pRGB = &rgb;
	float v1, v2;
	
	//HSL
	if ( S == 0.0f )							//HSL from 0 to 1
	{
		*R = (unsigned char)(L * 255.0f);		//RGB results from 0 to 255
		*G = (unsigned char)(L * 255.0f);
		*B = (unsigned char)(L * 255.0f);
	}
	else
	{
		if ( L < 0.5f )
			v2 = L * ( 1.0f + S );
		else
			v2 = ( L + S ) - ( S * L );
	   
	   v1 = 2.0f * L - v2;
	   
	   *R = (unsigned char)(255.0f * npHueToRGB(v1, v2, H + 0.3333333f) );
	   *G = (unsigned char)(255.0f * npHueToRGB(v1, v2, H) );
	   *B = (unsigned char)(255.0f * npHueToRGB(v1, v2, H - 0.3333333f) );
	}

	// return rgb;
}

// maps HSL (Hue Saturation Luminance) to RGB 0 to 1 float color space
// HSL is also called HSV or other variants such as HSB
// calls npHueToRGB() for each component
//------------------------------------------------------------------------------
void npHSLtoRGB( float H, float S, float L, float* R, float* G, float* B ) //pNPfloatRGB rgbOut
{
	float v1, v2;
	
	//HSL
	if ( S == 0 )						//HSL from 0 to 1
	{
		*R = L;	//R * 255 for ubyte		//RGB results from 0 to 255
		*G = L;	// * 255;
		*B = L;	// * 255;
	}
	else
	{
		if ( L < 0.5f )
			v2 = L * ( 1.0f + S );
		else
			v2 = ( L + S ) - ( S * L );
	   
	   v1 = 2.0f * L - v2;
	   
	   *R = npHueToRGB( v1, v2, H + 0.3333333f );	//R * 255 for ubyte
	   *G = npHueToRGB( v1, v2, H );				//G * 255 for ubyte
	   *B = npHueToRGB( v1, v2, H - 0.3333333f );	//B * 255 for ubyte
	}
}

NPfloatRGBA npHSLtoRGBA( float H, float S, float L, float alpha )
{
	NPfloatRGBA color; 

	// regular 'npHSLtoRGB()' color functions located in 'npcolor.h' 
	// alternate 'HSLtoRGB()' here for convienance
	npHSLtoRGB( H, S, L, &color.r, &color.g, &color.b);

	color.a = alpha;

	return color;
}

// NOTE x,y,z does not refer to XYZ color unless type = kNPcolorXYZtoRGB
//------------------------------------------------------------------------------
NPfloatRGBA npColorPicker( float x, float y, float z, int type, void* dataRef)
{
	NPfloatRGBA color = {0.0f,0.0f,0.0f};

	//zz add gradients and ability to fly color spaces... think video toaster
	//zz add function to get nearest index color (int) npGetColorIndex(R,G,B,A);

	switch (type)
	{
//		case kNPcolorRGB8 : break;		//bits per channel
//		case kNPcolorRGB16 : break;
		case kNPcolorXYtoRGB :		//set to 100% saturation S = 1
			npHSLtoRGB (x, 1.0f, y, &color.r, &color.g, &color.b);
			break;
//		case kNPcolorXYtoHSB : break;
//		case kNPcolorHSB : break;
//		case kNPcolorLAB : break;
//		case kNPcolorYCtoRGB : break;
//		case kNPcolorYUVtoRGB : break;
		default : 
			npHSLtoRGB (x, 1.0f, y, &color.r, &color.g, &color.b);
			break;//RGB
	}

	return color;
}

//---
// mouse / 6-DOE color picker... CMYK, RGB, lab, YUV, YC, Grayscale
// tracks velocity (over space time) for 6-DOE HID or regular XY mouse
// analogous to a 6 component color printer + white (alpha)
// maps up to 7 components with translation based on type
// control light color (power, wavelength, spread)
// polorization (u,v,w... could do more...)
// returns an RGBA color
NPfloatRGBA npColorPicker6DOE ( float x, float y, float z,
								float u, float v, float w, float t,
								int type, void* dataRef )
{
	//npColorPicker( u, v, w, type, dataRef);

	return npColorPicker( x, y, z, type, dataRef);
}
/*
int npColorID(ubyte r, ubyte g, ubyte b, int paletteID, void* dataRef);
// returns nearest colorID match(es) based on RGB, YUV, YC...
// colorID can be dynamically mapped to different palettes
// will not change color unless you also call npGetColorIndex(id)
// changing the colorIndex of a custom color maintains a phase offset!

// ---------------------------------------------------------------------------
int npColorID(ubyte r, ubyte g, ubyte b, int paletteID, void* dataRef)
{ return 0;
}
z
*/




/*
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

//MB-Transp
//------------------------------------------------------------------------------
//Distance between two points
float npDistance( NPfloatXYZ vecA, NPfloatXYZ vecB )
{
	float x = vecA.x - vecB.x;
	float y = vecA.y - vecB.y;
	float z = vecA.z - vecB.z;
	
//	float len = x*x + y*y + z*z;
//	len = sqrtf( len );
//	return len;

	return sqrtf( x*x + y*y + z*z );
}

//------------------------------------------------------------------------------
//QSort Compare function
int comparePinsDistanceFromCamera (const void* a, const void* b)
{
	pNPnode pinA = *(pNPnode*)a;
	pNPnode pinB = *(pNPnode*)b;
	
	//optimized for the common case where neither a nor b is null
	if( pinA == NULL || pinB == NULL )	//Null element in pinA & pinB: Nothing or Grid or Camera
	{
		if( pinA != NULL )				//Null element in pinB: Nothing or Grid or Camera
			return +1;					//pinB is at beginning of list... therefore pinA is "greater than" pinB. Return +1
		if( pinB != NULL )				//Null element in pinA: Nothing or Grid or Camera
			return -1;					//pinA is at beginning of list... therefore pinA is    "less than" pinB. Return -1
		return 0;						//These are "equal". Both should be at the beginning of the list.
	}

	if( pinA->distFromCamera   >  pinB->distFromCamera )	//pinA is furter from camera than pinB.
		return -1;											//Therefore should be at the beginning of the list. ie "less than"... Return -1
	if( pinA->distFromCamera   <  pinB->distFromCamera )	//pinA is closer from camera than pinB.
		return +1;											//Therefore should be at the end of the list.    ie "greater than"... Return +1
	if( pinA->distFromCamera  ==  pinB->distFromCamera )	//pinA & pinB are exactly the same distance from the camera. (impossible-ish)
		return 0;											//Return 0;

	//return ( *(int*)a - *(int*)b );
	return -1;
}

int sortCount = 0;	//zzhp
void npDrawSortThread(void* dataRef);
//------------------------------------------------------------------------------
void npDrawSortThread(void* dataRef)										 //zzhp
{
	int waitForSwap = 0;
	pData data = (pData) dataRef;

	int* killThread = false; //data->map.sortKillThread;		//zz debug

	while (!killThread)
	{
		//swap flag set by draw routine when its reaches the end of sort buffer
		//loading flag signals not to sort 
		while(data->map.sortSwapFlag || data->io.file.loading
				|| data->io.clearIndex)		//zzhp test flag pauses sorting, rigged to background colorIndex
			nposSleep(0.001);

		if (data->map.sortSwap)
		{
			data->map.sortCountA = data->map.nodeRootCount;
			npDrawSort (data->map.sortA, data->map.sortCountA, data);
		}
		else
		{
			data->map.sortCountB = data->map.nodeRootCount;
			npDrawSort (data->map.sortB, data->map.sortCountB, data);
		}

		data->map.sortSwapFlag = true;		//flag that sortDone
	}

	nposEndThread();
}

//------------------------------------------------------------------------------
void npDrawSort(void** sort, int count, void* dataRef)						//zzhp
{
	int i = 0;

	pData data = (pData) dataRef;

	pNPnode node = NULL;

	pNPnode camNode = data->map.currentCam;
	NPfloatXYZ camVec = camNode->translate;
	
	int nodeRootCount = data->map.nodeRootCount;
//	sortCount = nodeRootCount;											//zzhp

	//add root node thread locking and node delete synchro											//zz debug 

	//could pre-subsample the node list when large nodeRootCount > 100K		//zz debug

	//update root node distance from camera
	for( i = 0; i < count; i++ )		//zzhp
	{
		if( i < kNPnodeRootPin )	//Nothing, Camera, Grid, HUD...
		{
			sort[i] = NULL;
			continue;
		}
		
		node = data->map.node[i];
		node->distFromCamera = npDistance(camVec, node->translate);

		sort[i] = node;
	}

	if (count > data->map.nodeRootCount)
	{
		printf("err 2399 - count > data->map.nodeRootCount by: %d\n",
				(count - data->map.nodeRootCount));
		return;
	}
	//create z-sorted list, back to front
	//QuickSort based on Distance From Camera, back to front z-sorted
	qsort( sort, count, sizeof(pNPnode),
			comparePinsDistanceFromCamera );
}
*/

