
#ifndef RADVIZ_H
#define RADVIZ_H

#include <stdlib.h>
#include <math.h>

#ifndef PI
#define PI 3.14159
#endif PI

float RadVizColors[MAX_N_CLASSES][3];

struct Axis {
	float x; float y;
};

class RadViz {
	// displays axes and clusters in a circle centered on (0,0) with radius 1.0
public:

	Axis *axes;
	float **lines;
	int numaxes;
	int selectedaxis, lastaxis;
	float expansion;
	float zoomshrink;
	float zoomsx, zoomsy, zoomex, zoomey;
	int highlightclass;
	int NoShowHighClass;
	int display_axes_flag;
	int display_axes_names;
	
	char dimensionnames[64][32];

	float *tempcoords;
	float *coords;

	int nocolor;
	int background;
	int alloneclass;
	int transparent_on;

	RadViz() : numaxes(0) , selectedaxis(-1), lastaxis(0), 
		expansion(5.0) , zoomshrink(0.01) , zoomsx(-2.0) , highlightclass(-1) ,
		NoShowHighClass(0) , nocolor(0) , background(0) , alloneclass(0) {
	
		int i;
		RadVizColors[0][0] = 0.4; RadVizColors[0][1] = 0.1; RadVizColors[0][2] = 0.8;
		RadVizColors[1][0] = 0.0; RadVizColors[1][1] = 1.0; RadVizColors[1][2] = 0.0;
		RadVizColors[2][0] = 0.0; RadVizColors[2][1] = 0.0; RadVizColors[2][2] = 1.0;
		RadVizColors[3][0] = 1.0; RadVizColors[3][1] = 0.0; RadVizColors[3][2] = 0.0;
		RadVizColors[4][0] = 0.0; RadVizColors[4][1] = 0.7; RadVizColors[4][2] = 0.7;
		RadVizColors[5][0] = 0.7; RadVizColors[5][1] = 0.0; RadVizColors[5][2] = 0.7;
		RadVizColors[6][0] = 0.7; RadVizColors[6][1] = 0.7; RadVizColors[6][2] = 0.0;
		RadVizColors[7][0] = 0.8; RadVizColors[7][1] = 0.4; RadVizColors[7][2] = 0.1;
		RadVizColors[8][0] = 0.1; RadVizColors[8][1] = 0.4; RadVizColors[8][2] = 0.8;
		for (i=9;i<MAX_N_CLASSES;i++) {
			RadVizColors[i][0] = RadVizColors[i][1] = RadVizColors[i][2] = 0.5;
		}
		lines = new float *[MAXDIMS];
		for (i=0;i<MAXDIMS;i++) {
			lines[i] = new float[2];
		}

		tempcoords = new float[MAXDIMS];
		coords = new float[MAXDIMS];
	
	
	};


	void DisplayLegend() {
		int i;
		int r;
		char buffer[16];

		char classnames[5][16];


		strcpy(classnames[0],"normal");
		strcpy(classnames[1],"probe");
		strcpy(classnames[2],"DOS");
		strcpy(classnames[3],"U2R");
		strcpy(classnames[4],"R2L");

		strcpy(buffer,"classes");
		glColor3f(0.0,0.0,0.0);
		for (i=0;i<strlen(buffer);i++) {
			glRasterPos2f(2.1+0.2*(float)i,0.3);
			glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,buffer[i]);
		}
		
		for (i=0;i<5;i++) {
			
			glColor3f(0.5,0.0,0.0);
			for (r=0;r<strlen(classnames[i]);r++) {
				glRasterPos2f(4.1+1.2*(float)i+0.2*(float)r,1.1);
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,classnames[i][r]);
			}

			glBegin(GL_QUADS);
			glColor3f(RadVizColors[i][0],RadVizColors[i][1],RadVizColors[i][2]);
			glVertex3f(4.1+1.2*(float)i,0.1,0.0);
			glVertex3f(3.9+1.2*(float)(i+1),0.1,0.0);
			glVertex3f(3.9+1.2*(float)(i+1),0.9,0.0);
			glVertex3f(4.1+1.2*(float)i,0.9,0.0);
			glEnd();
		}

	};

	void zoomstart(float x, float y) {
		zoomsx = zoomex = x;
		zoomsy = zoomey = y;
	};

	void zoomend(float x, float y) {
		if (x<zoomsx) zoomsx = x;
		if (x>zoomex) zoomex = x;
		if (y<zoomsy) zoomsy = y;
		if (y>zoomey) zoomey = y;
	};

	void DisplayZoomRectangle() {
		if (zoomsx>=-1.0) {
			glColor3f(0.0,0.0,0.0);
			glBegin(GL_LINES);
			glVertex3f(zoomsx,zoomsy,0.5);
			glVertex3f(zoomex,zoomsy,0.5);
			glVertex3f(zoomsx,zoomey,0.5);
			glVertex3f(zoomex,zoomey,0.5);
			glVertex3f(zoomsx,zoomsy,0.5);
			glVertex3f(zoomsx,zoomey,0.5);
			glVertex3f(zoomex,zoomsy,0.5);
			glVertex3f(zoomex,zoomey,0.5);
			glEnd();
		}
	};

	void InitAxes(int n) {
		int i;
		float ang;
		numaxes = n;
		axes = new Axis[numaxes];
		for (i=0;i<numaxes;i++) {
			ang = ((2.0*PI)/(float)numaxes) * (float)i;
			axes[i].x = cos(ang);
			axes[i].y = sin(ang);
		}
	};

	void RandomAxes() {
		int i,j,k;
		float ang;
		int *array;
		int *array2;
		if (numaxes>0) {
			array = new int[numaxes];
			array2 = new int[numaxes];
			for (i=0;i<numaxes;i++) array[i] = 0;
			for (i=numaxes;i>0;i--) {
				// pick position of axis
				k = rand()%i; 
				// find the kth empty spot in array
				for (j=0;(j<numaxes)&&(k>=0);j++) {
					if (array[j]==0) k--;
				}
				j--;
				array[j] = 1;
				array2[i-1] = j;
			}
			for (i=0;i<numaxes;i++) {
				ang = ((2.0*PI)/(float)numaxes) * (float)i;
				axes[array2[i]].x = cos(ang);
				axes[array2[i]].y = sin(ang);
			}

		}
	};

	// centroid coords, class, projection
	void DisplayCentroid(float *cptr, int cs, int p) {

		int i;
		float x,y;
		float *c;
		Group *gptr;

		gptr = new Group();
		gptr->SetDims(numaxes);
		for (i=0;i<numaxes;i++) {
			gptr->startingcoords[i] = cptr[i];
		}

		// draw the groups until they get smaller than threshold
		// find screen coordinates of the cluster
		Project(gptr, axes, expansion, &x, &y, p, NULL);
		// draw the cluster onto the screen
		glPointSize(10);
		c = RadVizColors[cs];
		glColor3f(c[0],c[1],c[2]);
		glBegin(GL_POINTS);
		glVertex3f(x,y,0.0);
		glEnd();

	}

	void readKDDdimensionNames() {
		int i, n;
		int d;
		int symbolics[41];
		char buffer[32];
		FILE *fptr;
		FILE *fptr2;


		fptr = fopen("../data/kddcup99/processed/symbolics.txt","rt");
		for (i=0;i<41;i++) {
			fscanf(fptr,"%d", &(symbolics[i]));
		}
		fclose(fptr);

		n = 0;
		fptr = fopen("../data/kddcup99/processed/dimensions.txt","rt");
		for (i=0;i<41;i++) {
			fscanf(fptr,"%d %s", &d, buffer);
			if (symbolics[i]==0) {
				strcpy(dimensionnames[n],buffer);
				n++;
			}
		}
		fclose(fptr);
	}

	void DisplayAxes(int p) {
		int tnaxes;
		int i,j;
		float x,y;

		if (display_axes_flag) {
			tnaxes = numaxes;
			if (pcaflags[p]==1) tnaxes = npcacols;
			glColor3f(1.0,1.0,1.0);
			glLineWidth(1);
			for (i=0;i<tnaxes;i++) { // draw axes
				glBegin(GL_LINES);
				glColor3f(0.5,0.5*(float)i/(float)tnaxes,0.5);
				glVertex3f(0.0,0.0,0.0);
				glVertex3f(axes[i].x,axes[i].y,0.0);
				glEnd();
				if (display_axes_names) {
					x = axes[i].x;
					y = axes[i].y;
					if (x<-1.2) { y = y*(-1.2/x); x = -1.2; }
					if (x>1.0) { y = y*(1.0/x); x = 1.0; }
					if (y<-1.2) { x = x*(-1.2/y); y = -1.2; }
					if (y>1.15) { x = x*(1.15/y); y = 1.15; }
					glColor3f(1.0,1.0,0.0);
					for (j=0;j<strlen(dimensionnames[i]);j++) {
						glRasterPos2f(x+0.02*(float)j,y);
						glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,dimensionnames[i][j]);
					}
				}

			}
		}
	};


	void OutputAxesNames() {
		int i;
		for (i=0;i<numaxes;i++) printf("%d %s\n", i, dimensionnames[i]);
	};



	void Display(Group *gptr, float threshold, int p, float depth) {

		int i;
		float x,y;
		float *c;
		float zoff;

		zoff = 0.0;
		if (background) zoff = -0.2;

		// draw the groups until they get smaller than threshold
		// find screen coordinates of the cluster
		Project(gptr, axes, expansion, &x, &y, p,NULL);
		// draw the cluster onto the screen
		glPointSize(2);
		if (highlightclass >= 0) {
			if (gptr->category==highlightclass) {
				if (transparent_on) {
					glColor4f(0.8,0.0,0.0,0.7);
				} else {
					glColor3f(0.8,0.0,0.0);
				}
			} else {
				if (transparent_on) {
					glColor4f(0.8,0.8,0.8,0.2);
				} else {
					glColor3f(0.8,0.8,0.8);
				}
			}
		} else {
			c = RadVizColors[gptr->category];
			if (transparent_on) {
				glColor4f(c[0],c[1],c[2],0.2);
			} else {
				glColor3f(c[0],c[1],c[2]);
			}
		}
		if (nocolor) {
			if (transparent_on) {
				glColor4f(1.0,1.0,1.0,0.5);
			} else {
				glColor3f(1.0,1.0,1.0);
			}
		}
		
		if ((highlightclass >= 0)&&(gptr->category==highlightclass)) {
			if (!NoShowHighClass) {
				glBegin(GL_POINTS);
				glVertex3f(x,y,0.6 + zoff + depth);
				glEnd();
			}
		} else {
			glBegin(GL_POINTS);
			glVertex3f(x,y,0.5 + zoff + depth);
			glEnd();
		}
		// recursively draw the children
		for (i=0;i<gptr->nchildren;i++) {
			if (gptr->children[i]->size>threshold) {
				Display(gptr->children[i],threshold,p,depth);
			}
		}
		
	};


	void DisplayZoom(Group *gptr, float threshold, Axis *alternAxes, float *alternExpan, int p, float depth) {

		int i;
		float x,y;
		float *c; // color

		int tnaxes;

		float zoff;

		zoff = 0.0;
		if (background) zoff = -0.2;

		tnaxes = numaxes;
		if (pcaflags[p]==1) tnaxes = npcacols;

		
		if (alternAxes != NULL) {
			Project(gptr, alternAxes, *alternExpan, &x, &y, p, lines);
		} else {
			Project(gptr, axes, expansion, &x, &y, p, lines);
		}
		glLineWidth(linethickness);
		if ((x>=zoomsx)&&(x<=zoomex)&&(y>=zoomsy)&&(y<zoomey)) {
			glPointSize(2);


			if (highlightclass >= 0) {
				if (gptr->category==highlightclass) {
					if (transparent_on) {
						glColor4f(0.8,0.0,0.0,0.7);
					} else {
						glColor3f(0.8,0.0,0.0);
					}
				} else {				
					if (transparent_on) {
						glColor4f(0.8,0.8,0.8,0.2);
					} else {
						glColor3f(0.8,0.8,0.8);
					}
				}
			} else {
				c = RadVizColors[gptr->category];
				
				if (transparent_on) {
					glColor4f(c[0],c[1],c[2],0.2);
				} else {
					glColor3f(c[0],c[1],c[2]);
				}
			}
			if (nocolor) {
				if (transparent_on) {
					glColor4f(1.0,1.0,1.0,0.5);
				} else {
					glColor3f(1.0,1.0,1.0);
				}
			}

			if ((highlightclass >= 0)&&(gptr->category==highlightclass)) {
				if (!NoShowHighClass) {
					glBegin(GL_POINTS);
					glVertex3f(x,y,0.1+depth);
					glEnd();
				}
			} else {
				glBegin(GL_POINTS);
				glVertex3f(x,y,0.0+depth);
				glEnd();
			}

			for (i=0;i<tnaxes;i++) {

				if ((highlightclass >= 0)&&(gptr->category==highlightclass)) {
					if (!NoShowHighClass) {
						glBegin(GL_LINES);
						glVertex3f(x,y,0.6+zoff);
						glVertex3f(x+lines[i][0],y+lines[i][1],0.6+zoff+depth);
						glEnd();
					}
				} else {
					glBegin(GL_LINES);
					glVertex3f(x,y,0.5+zoff);
					glVertex3f(x+lines[i][0],y+lines[i][1],0.5+zoff+depth);
					glEnd();
				}
			}
			// recursively draw the children
			for (i=0;i<gptr->nchildren;i++) {
				if (gptr->children[i]->size>threshold) {
					Display(gptr->children[i],threshold,p,depth);
				}
			}
		}

	
	  

	};

	void Project(Group *gptr, Axis *aptr, float expan, float *xptr, float *yptr, int p, float **lines) {

		int i;
		float x,y;
		int tnaxes;

		if (pcaflags[p]==1) {
			tnaxes = npcacols;
			for (i=0;i<numaxes;i++) {
				tempcoords[i] = gptr->startingcoords[i];
			}
			P1.Transform(tempcoords, coords, p, npcacols);
		} else {
			tnaxes = numaxes;
			for (i=0;i<numaxes;i++) {
				coords[i] = gptr->startingcoords[i];
			}
		}

		// find screen coordinates of the cluster
		x = y = 0.0;
		
		for (i=0;i<tnaxes;i++) {
			if (lines!=NULL) {
				lines[i][0] = coords[i]*aptr[i].x;
				lines[i][1] = coords[i]*aptr[i].y;
				x += lines[i][0];
				y += lines[i][1];
				lines[i][0] *= zoomshrink;
				lines[i][1] *= zoomshrink;
			} else {
				x += (coords[i]*aptr[i].x);
				y += (coords[i]*aptr[i].y);
			}
		}
		x = expan*x/(float)tnaxes;
		y = expan*y/(float)tnaxes;
		
		(*xptr) = x;
		(*yptr) = y;

		

	};

	int SelectAxis(float x, float y) {
		int i;
		float sx, sy;
		selectedaxis = -1;
		for (i=0;i<numaxes;i++) {
			/*
			if (((x-axes[i].x)*(x-axes[i].x)+
				(y-axes[i].y)*(y-axes[i].y)) < 0.03) {
				selectedaxis = i;
			}
			*/
			sx = axes[i].x;
			sy = axes[i].y;
			if (sx<-1.2) { sy = sy*(-1.2/sx); sx = -1.2; }
			if (sx>1.2) { sy = sy*(1.2/sx); sx = 1.2; }
			if (sy<-1.2) { sx = sx*(-1.2/sy); sy = -1.2; }
			if (sy>1.2) { sx = sx*(1.2/sy); sy = 1.2; }
			

			if (((x-sx)*(x-sx)+
				(y-sy)*(y-sy)) < 0.03) {
				selectedaxis = i;
			}

		}
		printf("just selected axis %d\n", selectedaxis);
		return selectedaxis;
	};

	void MoveAxis(float x, float y) {
		if (selectedaxis>=0) {
			printf("moving axis %d\n", selectedaxis);
			axes[selectedaxis].x = x;
			axes[selectedaxis].y = y;
		}
	};

	void Boost(float m) {
		if (lastaxis>=0) {
			axes[lastaxis].x = axes[lastaxis].x * m;
			axes[lastaxis].y = axes[lastaxis].y * m;
		}
	};

	void ResetAxis() { lastaxis = selectedaxis; selectedaxis = -1; };


};


#endif RADVIZ_H

