#include "processing.h"

//------------------------------------------------------------------------
// Normalizes an array of short ints
//------------------------------------------------------------------------
void Normalize(const int numVerts, const short int minRange, const short int maxRange, short int** data) {
	int i;
	short int min, max;
	short int* list;
	
	max = SHRT_MIN;
	min = SHRT_MAX;
	list=*data;
	for(i=0;i<3*numVerts;i++) {
		if(max<list[i]) max=list[i];
		if(min>list[i]) min=list[i];
	}

	for(i=0;i<3*numVerts;i++) {
		list[i] = minRange+(maxRange-minRange)*(list[i]-min)/(max-min);
	}

	//Make sure values are in the expected vcbRange
	for(i=0;i<3*numVerts;i++) {
		if( (list[i]>maxRange) || (list[i]<minRange) )
			printf("Error: Normalization for element %d failed\n", i);
	}
	printf("  Normalized %d vertices\n",numVerts);
}

void Normalize1sd(const short int min,      const short int max,
                  const short int minRange, const short int maxRange,
                  short int* data) {
	
	(*data) = minRange+(maxRange-minRange)*((*data)-min)/(max-min);
}

//------------------------------------------------------------------------
// Normalize the floating point vector
//------------------------------------------------------------------------
void Normalize4f(const float minRange, const float maxRange, float* data[4]) {
	int i;
	float min, max;
	float* list;
	
	max = FLT_MIN;
	min = FLT_MAX;
	list=*data;
	for(i=0;i<list[i];i++) {
		if(max<list[i]) max=list[i];
		if(min>list[i]) min=list[i];
	}

	for(i=0;list[i];i++) {
		list[i] = minRange+(maxRange-minRange)*(list[i]-min)/(max-min);
	}

	//Make sure values are in the expected vcbRange
	/*for(i=0;list[i];i++)
		if( (list[i]>maxRange) || (list[i]<minRange) )
			printf("Error: Normalization for element %d failed\n", i);*/
}

void Normalize1f(const float startRange, const float endRange,
                const float minRange, const float maxRange,
                float* data) {
	
	(*data) = minRange+(maxRange-minRange)*((*data)-startRange)/(endRange-startRange);
}

void Normalizefv(const float startRange, const float endRange,
                const float minRange, const float maxRange,
                int nelem, float** data) {
	int i;
	float* list;

	list=*data;
	for(i=0;nelem;i++) {
		list[i] = minRange+(maxRange-minRange)*(list[i]-startRange)/(endRange-startRange);
	}
}

//------------------------------------------------------------------------
// Get the minimum and maximum float from the array
//------------------------------------------------------------------------
void getMinMaxfv(int numElems, float* data, float* min, float* max) {
	int i;
	(*min)=FLT_MAX; (*max)=FLT_MIN;
	for(i=0;i<numElems;i++) {
		if(data[i]>(*max))
			(*max)=data[i];
		if(data[i]<(*min))
			(*min)=data[i];
	}
}

//------------------------------------------------------------------------
// Get the minimum and maximum int from the array
//------------------------------------------------------------------------
void getMinMaxiv(int numElems, int* data, int* min, int* max) {
	int i;
	(*min)=INT_MAX; (*max)=INT_MIN;
	for(i=0;i<numElems;i++) {
		if(data[i]>(*max))
			(*max)=data[i];
		if(data[i]<(*min))
			(*min)=data[i];
	}
}

//------------------------------------------------------------------------
// Used to compute the median
//------------------------------------------------------------------------
float kth_smallest(float a[], int n, int k)
{
	register int i,j,l,m;
	register float x;
	l=0; m=n-1;
	while (l<m) {
		x=a[k];
		i=l;
		j=m;
		do {
			while (a[i]<x) i++;
			while (x<a[j]) j--;
			if (i<=j) {
				ELEM_SWAP(a[i],a[j]);
				i++; j--;
			}
		} while (i<=j) ;
		if (j<k) l=i ;
		if (k<i) m=j ;
	}
	return a[k] ;
}

//------------------------------------------------------------------------
// Creates a randomized array of short ints
//------------------------------------------------------------------------
void initRandom(int numDims, short int min, short int max, const int numVerts, short int** location) {
	int i, j, index;
	short int *loc;

	//Initialize with random positions
	loc=(short int*)malloc(numVerts*numDims*sizeof(short int));
	*location=loc; index=0; srand(1);
	for(i=0;i<numVerts;i++) {
		for(j=0;j<numDims;j++) {
			loc[index]=min+(max-min)*rand()/RAND_MAX;
			index++;
		}
	}
}

//------------------------------------------------------------------------
// Initialize voxels and point all normals randomly
//------------------------------------------------------------------------
void initVoxelsWithRandNormals(int numVerts,unsigned char** voxels, int colorIndex, viewData view) {
	int i;
	unsigned short* pos;
	if( ((*voxels) = (unsigned char*)malloc(numVerts*10*sizeof(unsigned char))) == NULL) {
		perror("initVoxelsWithRandNormals() voxels"); exit(-1);
	}
	for(i=0;i<numVerts;i++) {
		(*voxels)[i*10+ 0] = (unsigned char)colorIndex;	// 8-bit index into the transfer function
		(*voxels)[i*10+ 1] = (unsigned char)(255.f*rand()/RAND_MAX);	// x-component of normal
		(*voxels)[i*10+ 2] = (unsigned char)(255.f*rand()/RAND_MAX);	// y-component of normal
		(*voxels)[i*10+ 3] = (unsigned char)(255.f*rand()/RAND_MAX);	// z-component of normal
		pos = (unsigned short*)&((*voxels)[i*10+4]);
		(*pos++) = view.locList[0][i*3+ 0]; // x-coordinate
		(*pos++) = view.locList[0][i*3+ 1]; // y-coordinate
		(*pos)   = view.locList[0][i*3+ 2]; // z-coordinate
	}
}

//------------------------------------------------------------------------
// Initialize voxels with all normals pointing in the Z direction
//------------------------------------------------------------------------
void initVoxelsWithZNormals(int numVerts,unsigned char** voxels, int colorIndex, viewData view) {
	int i;
	unsigned short* pos;
	if( ((*voxels) = (unsigned char*)malloc(numVerts*10*sizeof(unsigned char))) == NULL) {
		perror("initVoxelsWithZNormals() voxels"); exit(-1);
	}
	for(i=0;i<numVerts;i++) {
		(*voxels)[i*10+ 0] = (unsigned char)colorIndex;	// 8-bit index into the transfer function
		(*voxels)[i*10+ 1] = (unsigned char)0;		// x-component of normal
		(*voxels)[i*10+ 2] = (unsigned char)0;		// y-component of normal
		(*voxels)[i*10+ 3] = (unsigned char)255;	// z-component of normal
		pos = (unsigned short*)&((*voxels)[i*10+4]);
		(*pos++) = view.locList[0][i*3+ 0]; // x-coordinate
		(*pos++) = view.locList[0][i*3+ 1]; // y-coordinate
		(*pos)   = view.locList[0][i*3+ 2]; // z-coordinate
	}
}

//------------------------------------------------------------------------
// Initialize voxels and point all normals for the data towards the viewer
//------------------------------------------------------------------------
void initNormals(int numVerts,unsigned char** voxels, int colorIndex, viewData view) {
	int i;
	unsigned short* pos;
	float angle, dx, dy, dz;
	(*voxels) = (unsigned char*)malloc(numVerts*10*sizeof(unsigned char));
	for(i=0;i<numVerts;i++) {
		(*voxels)[i*10+ 0] = (unsigned char)colorIndex;	// 8-bit index into the transfer function
		dx=(float)view.locList[0][i*3+ 0]-(float)DIM_SIZE/2.f-view.eye[0];
		dy=(float)view.locList[0][i*3+ 1]-(float)DIM_SIZE/2.f-view.eye[1];
		dz=(float)view.locList[0][i*3+2]-(float)DIM_SIZE/2.f-view.eye[2]-view.zoom*view.eye[2];
		angle=(float)tan(dz/dx);
		(*voxels)[i*10+ 1] = (unsigned char)(255.f*angle/M_PI);  // x-component of normal
		angle=(float)tan(dz/dy);
		(*voxels)[i*10+ 2] = (unsigned char)(255.f*angle/M_PI);	// y-component of normal
		(*voxels)[i*10+ 3] = (unsigned char)255;// z-component of normal
		pos = (unsigned short*)&((*voxels)[i*10+4]);
		(*pos++) = view.locList[0][i*3+ 0]; // x-coordinate
		(*pos++) = view.locList[0][i*3+ 1]; // y-coordinate
		(*pos)   = view.locList[0][i*3+ 2]; // z-coordinate
	}
}

//------------------------------------------------------------------------
// Create float[256][4] color lookup table via RGB interpolation of given
// values: index1,R1,G1,B1,index2,R2,G2,B2
//------------------------------------------------------------------------
float** createTLUT(int numFilled, float* idxRGB) {
	int i,idx,start,end;
	float** tlut;

	//Allocate space
	tlut=(float**)malloc(256*sizeof(float*));
	if(tlut==NULL) {perror("createTLUT() tlut"); exit(-1);}
	for(i=0;i<256;i++) {
		tlut[i]=(float*)malloc(4*sizeof(float));
		if(tlut[i]==NULL) {perror("createTLUT() tlut"); exit(-1);}
		tlut[i][0]=-1.f;
		tlut[i][4]=1.f;
	}

	//Fill color table with known values
	for(i=0;i<numFilled;i++) {
		idx=(int)idxRGB[4*i];
		memcpy(tlut[idx],&(idxRGB[4*i+1]),3*sizeof(float));
	}

	//Interpolate between each pair of known values
	idx=0;
	while(idx<256) {
		while(tlut[idx][0]<-0.5f) {
			idx++;
			if(idx>255) return tlut;
		}
		start=idx;
		idx++;
		if(idx>255) return tlut;
		while(tlut[idx][0]<-0.5f) {
			idx++;
			if(idx>255) return tlut;
		}
		end=idx;
		for(i=start+1;i<end;i++) {
			vcbInterpolateColor(tlut[start][0],tlut[start][1],tlut[start][2],
								tlut[end][0],tlut[end][1],tlut[end][2],
								i/(float)(end-start),
								&(tlut[i][0]),&(tlut[i][1]),&(tlut[i][2]));
		}
	}
	return NULL; //should never get here
}
