
#include <stdio.h>
#include "glut.h"
#include "quadtree.h"


int QuadTreeTexMap[64][64];
GLubyte TextureQuadTree[64][64][4];
GLuint texNameQuadTree;


QuadTree::QuadTree() { }

QuadTree::QuadTree(int sx, int sy, int s) : Visualization(sx,sy,s,s) { }


// use first 12 bits
// make a 64 X 64 texture map
void QuadTree::MakeTextureMap() {

	int i,k,n,xupper,xlower,yupper,ylower,x,y;


	for (k=0;k<64*64;k++) {

	  xupper = yupper = 63;
	  xlower = ylower = 0;

	  for (i=0;i<5;i++) {
	    n = (k >> ((5-i)*2)) & 3;
		if (n&1) { 
		    xlower += (1+xupper-xlower)/2;
		} else {
			xupper = xlower + (1+xupper-xlower)/2 - 1;
		}
		if (n&2) {
			ylower += (1+yupper-ylower)/2;
		} else {
			yupper = ylower + (1+yupper-ylower)/2 - 1;
		}
	  }
 
	  n = k & 3;
	  if (n&1) {
		x = xupper;
	  } else {
		x = xlower;
	  }
      if (n&2) {
        y = yupper;
	  } else {
        y = ylower;
	  }

	  QuadTreeTexMap[x][y] = k;

	}

	int val;
	int s,t;

	int defaultAlignment;

	glGetIntegerv(GL_UNPACK_ALIGNMENT, &defaultAlignment);

	glEnable(GL_TEXTURE_2D);

	for (s=0; s<64; s++) {	
		for (t=0; t<64; t++) {
			val = QuadTreeTexMap[s][t]/32;
			TextureQuadTree[s][t][0] = (GLubyte)val;
			TextureQuadTree[s][t][1] = (GLubyte)val;
			TextureQuadTree[s][t][2] = (GLubyte)val;
			TextureQuadTree[s][t][3] = 255;
		}
	}


	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(1, &texNameQuadTree);


	
	glBindTexture(GL_TEXTURE_2D, texNameQuadTree);


	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);


	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0,
				GL_RGBA, GL_UNSIGNED_BYTE, TextureQuadTree);


	glDisable(GL_TEXTURE_2D);

	glPixelStorei(GL_UNPACK_ALIGNMENT, defaultAlignment);


}


void QuadTree::Draw() {


	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0,xsize,0,ysize);
			// left, right, bottom, top
	glViewport(startx, starty, xsize, ysize);
			// startx, starty, xsize, ysize
			// coordinates begin from lower left corner of window
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	DrawSquare();

}

void QuadTree::DrawSquare() {


	glEnable(GL_TEXTURE_2D);

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, texNameQuadTree);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); glVertex3f(0.0,0.0,-0.5);
	glTexCoord2f(0.0, 1.0); glVertex3f(0.0,(float)ysize,-0.5);
	glTexCoord2f(1.0, 1.0); glVertex3f((float)xsize,(float)ysize,-0.5);
	glTexCoord2f(1.0, 0.0); glVertex3f((float)xsize,-0.5,-0.5);
	glEnd();


    glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);

}

void QuadTree::DrawSquareXZ() {


	glEnable(GL_TEXTURE_2D);

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glBindTexture(GL_TEXTURE_2D, texNameQuadTree);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); glVertex3f(0.0,0.0,0.0);
	glTexCoord2f(0.0, 1.0); glVertex3f(0.0,0.0,1.0);
	glTexCoord2f(1.0, 1.0); glVertex3f(1.0,0.0,1.0);
	glTexCoord2f(1.0, 0.0); glVertex3f(1.0,0.0,0.0);
	glEnd();


    glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);


}



void QuadTree::DataToScreen(unsigned int di, int mi, int &sx, int &sy, int &xs, int &ys) {

	int xupper, xlower, yupper, ylower;
	int i, n;

	xupper = yupper = xsize - 1;
	xlower = ylower = 0;

	for (i=0;i<mi;i++) {
		n = (di >> (31-i)) & 1;
		if (i%2) {
			if (n==1) {
				ylower += (1+yupper-ylower)/2;
			} else {
				yupper = ylower + (1+yupper-ylower)/2 - 1;
				if (yupper < ylower) yupper = ylower;
			}
		} else {
			if (n==1) {
				xlower += (1+xupper-xlower)/2;
			} else {
				xupper = xlower + (1+xupper-xlower)/2 - 1;
				if (xupper < xlower) xupper = xlower;
			}
		}
	}

	sx = xlower;
	sy = ylower;

	xs = xupper - xlower + 1;
	ys = yupper - ylower + 1;

}

void QuadTree::DataToScreenNormalized(unsigned int di, int mi, float &sx, float &sy) {

	int nx, ny, nxs, nys;
	DataToScreen(di, mi, nx, ny, nxs, nys);
	sx = (float)nx/(float)xsize;
	sy = (float)ny/(float)ysize;

}



void QuadTree::ScreenToData(int sx, int sy, unsigned int &di, int &mi) {

	int xupper, xlower, yupper, ylower;
	int n;

	sx = sx - startx;
	sy = sy - starty;

	xupper = yupper = xsize - 1;
	xlower = ylower = 0;

	mi = 0;
	n = 31;
	di = 0;

	while ((xupper>xlower)||(yupper>ylower)) {
		if (sx >= (1+xlower+xupper)/2) {
			di = di | (1 << n);
			xlower += (1+xupper-xlower)/2;
		} else {
			xupper = xlower + (1+xupper-xlower)/2 - 1;
		}
		if (sy >= (1+ylower+yupper)/2) {
			di = di | (1 << (n-1));
			ylower += (1+yupper-ylower)/2;
		} else {
			yupper = ylower + (1+yupper-ylower)/2 - 1;
		}
		printf("%d %d %d %d\n", xupper, xlower, yupper, ylower);
		mi+=2;
		n -= 2;
	}

}




SurroundingLines::SurroundingLines() { }

SurroundingLines::SurroundingLines(int sx, int sy, int s, int mar, int mn, int mx) : 
Visualization(sx, sy, s, s) , margin(mar) {
	min = mn;
	max = mx;
	p1 = min + (max-min)/4;
	p2 = min + (max-min)/2;
	p3 = min + 3*(max-min)/4; 
}

void SurroundingLines::Draw() {

	char str[16];

	glColor3f(0.1,0.1,0.1);
	glLineWidth(2);
	glBegin(GL_LINES);
	// lower horizontal line
	glVertex3f((float)startx, (float)(starty-margin), -0.5);
	glVertex3f((float)(startx+xsize), (float)(starty-margin), -0.5);
	// upper horizontal line
	glVertex3f((float)startx, (float)(starty+ysize+margin), -0.5);
	glVertex3f((float)(startx+xsize), (float)(starty+ysize+margin), -0.5);
	// left vertical line
	glVertex3f((float)(startx-margin), (float)starty, -0.5);
	glVertex3f((float)(startx-margin), (float)(starty+ysize), -0.5);
	// right vertical line
	glVertex3f((float)(startx+xsize+margin), (float)starty, -0.5);
	glVertex3f((float)(startx+xsize+margin), (float)(starty+ysize), -0.5);
	glEnd();


	glColor3f(0.1,0.1,0.1);
	sprintf(str,"%d",min);
	glRasterPos3f((float)(startx-margin-margin), (float)starty, 0.0);
	NT.font.Render(str);
	sprintf(str,"%d",p1);
	glRasterPos3f((float)(startx-margin-margin), (float)(starty+ysize), 0.0);
	NT.font.Render(str);
	sprintf(str,"%d",p2);
	glRasterPos3f((float)(startx+xsize), (float)(starty+ysize+margin), 0.0);
	NT.font.Render(str);
	sprintf(str,"%d",p3);
	glRasterPos3f((float)(startx+xsize+margin), (float)starty, 0.0);
	NT.font.Render(str);
	sprintf(str,"%d",max);
	glRasterPos3f((float)startx, (float)(starty-margin-margin), 0.0);
	NT.font.Render(str);



}

void SurroundingLines::DataToScreen(int di, int &x, int &y) {

	int i_per_line, which_line, pos_within_line;
	float temp;

	if (di<=p1) {
		which_line = 0;
	} else if (di<=p2) {
		which_line = 1;
	} else if (di<=p3) {
		which_line = 2;
	} else {
		which_line = 3;
	}

	if (which_line==0) { // if left line
		temp = (float)(di - min)/(float)(p1-min) * (float)ysize;
		pos_within_line = (int)temp;
		x = startx - margin;
		y = starty + pos_within_line;
	} else if (which_line==1) { // if top line
		temp = (float)(di - (p1+1))/(float)(p2-(p1+1)) * (float)xsize;
		pos_within_line = (int)temp;
		y = starty + ysize + margin;
		x = startx + pos_within_line;
	} else if (which_line==2) { // if right line
		temp = (float)(di - (p2+1))/(float)(p3-(p2+1)) * (float)ysize;
		pos_within_line = (int)temp;
		x = startx + xsize + margin;
		y = starty + ysize - pos_within_line;
	} else if (which_line==3) { // if bottom line
		temp = (float)(di - (p3+1))/(float)(max-(p3+1)) * (float)xsize;
		pos_within_line = (int)temp;
		y = starty - margin;
		x = startx + xsize - pos_within_line;
	}

}


int SurroundingLines::ClickDetected(int x, int y) {
	if (  (((y>=starty)&&(y<=starty+ysize)) &&
		   (((x>startx-margin-5)&&(x<startx-margin+5)) ||
		    ((x>startx+xsize+margin-5)&&(x<startx+xsize+margin+5)))) 
			||
		  (((x>=startx)&&(x<=startx+xsize)) &&
		   (((y>starty-margin-5)&&(y<starty-margin+5)) ||
		   ((y>starty+ysize+margin-5)&&(y<starty+ysize+margin+5)))) ) {
		return 1;
	} else {
		return 0;
	}
}

void SurroundingLines::ScreenToData(int sx, int sy, int &di) {

	int i_per_line;
	i_per_line = (max - min + 1) / 4 + 1;
	if ((sx>startx-margin-5) & (sx<startx-margin+5)) { // left line
		di = min + ((p1-min) * (sy-starty))/ysize;
	} else if ((sy>starty+ysize+margin-5)&&(sy<starty+ysize+margin+5)) { // top line
		di = p1+1 + ((p2-(p1+1)) * (sx-startx))/xsize;
	} else if ((sx>startx+xsize+margin-5) & (sx<startx+xsize+margin+5)) { // right line
		di = p2+1 + ((p3-(p2+1)) * ((starty+ysize)-sy))/ysize;
	} else if ((sy>starty-margin-5)&&(sy<starty-margin+5)) { // bottom line
		di = p3+1 + ((max-(p3+1)) * ((startx+xsize)-sx))/xsize;
	}

}
