//display.cpp

#include "display.h"


Display::Display(GLdouble wxmin, GLdouble wxmax,
	  GLdouble wymin, GLdouble wymax,
	  GLdouble wzmin, GLdouble wzmax,
	  GLint vxmin, GLint vxmax,
	  GLint vymin, GLint vymax,
	  GLint vzmin, GLint vzmax,
	  GLint width, GLint height)
{
	_wxmin = wxmin;
	_wxmax = wxmax;
	_wymin = wymin;
	_wymax = wymax;
	_wzmin = wzmin;
	_wzmax = wzmax;
	_vxmin = vxmin;
	_vxmax = vxmax;
	_vymin = vymin;
	_vymax = vymax;
	_vzmin = vzmin;
	_vzmax = vzmax;
	_width = width;
	_height = height;

	_eye[0] = 0.0;
	_eye[1] = 0.0;
	_eye[2] = 3.0;
        _eye[3] = 1.0;
	_look[0] = .0;
	_look[1] = .0;
	_look[2] = 0.0;
    _look[3] = 0.0;
	_up[0] = 0.0;
	_up[1] = 1.0;
	_up[2] = 0.0;
    _up[3] = 0.0;
	_myLightPos[0] = 0.0;
	_myLightPos[1] = 0.5;
	_myLightPos[2] = -1.0;
	_myLightPos[3] = .25;

	_theta[0]=_theta[1]=_theta[2]=_theta[3]=0.0;
	
	_frustum[0] = -.10;
	_frustum[1] = .10;
	_frustum[2] = -.10;
	_frustum[3] = .10;
	_frustum[4] = 0.1;
	_frustum[5] = 50.0;
    _frustum[6] = .1;
    _frustum[7] = 50.0;

	_d[0]=_d[1]=_d[2]=_d[3] = 0.0;
	_d[2] = _d[3] = -3.0;
	_freeLook = true;
}


//display initializer
void Display::initDisplay(int & argc, char ** argv)
{
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  glEnable(GL_DEPTH_TEST | GL_CULL_FACE | GL_FOG);
  glDepthFunc(GL_LESS);
  glCullFace(GL_FRONT_AND_BACK);
  glutInitWindowSize(_width, _height);
  glutInitWindowPosition(100,100);
  glutCreateWindow("Test 3d prog");      
}


//idle function for glui
void Display::myGlutIdle(void)
{
 
}

//redisplay event handler
void Display::redisplay()
{
  int i = 0;
	 //4d rendering
  GLdouble hypercube[16][5] = {
		//front four vertices
		{-1.0, -1.0, 1.0, 1.0, 1.0},
		{ 1.0, -1.0, 1.0, 1.0, 1.0},
		{ 1.0,  1.0, 1.0, 1.0, 1.0},
		{-1.0,  1.0, 1.0, 1.0, 1.0},
		//back four vertices
		{-1.0, -1.0, -1.0, 1.0, 1.0},
		{ 1.0, -1.0, -1.0, 1.0, 1.0},
		{ 1.0,  1.0, -1.0, 1.0, 1.0},
		{-1.0,  1.0, -1.0, 1.0, 1.0},
		//same in 4d (back square)
		//front four vertices
		{-1.0, -1.0, 1.0, -1.0, 1.0},
		{ 1.0, -1.0, 1.0, -1.0, 1.0},
		{ 1.0,  1.0, 1.0, -1.0, 1.0},
		{-1.0,  1.0, 1.0, -1.0, 1.0},
		//back four vertices
		{-1.0, -1.0, -1.0, -1.0, 1.0},
		{ 1.0, -1.0, -1.0, -1.0, 1.0},
		{ 1.0,  1.0, -1.0, -1.0, 1.0},
		{-1.0,  1.0, -1.0, -1.0, 1.0},
	};

  GLdouble transformedHypercube[16][5];

  GLdouble modelview4d[5][5] = {
	  {1.0, 0.0, 0.0, 0.0, 0.0},
	  {0.0, 1.0, 0.0, 0.0, 0.0},
	  {0.0, 0.0, 1.0, 0.0, 0.0},
	  {0.0, 0.0, 0.0, 1.0, 0.0},
	  {0.0, 0.0, 0.0, 0.0, 1.0},
  };
  GLdouble projection[5][5] = {
	  {1.0, 0.0, 0.0, 0.0, 0.0},
	  {0.0, 1.0, 0.0, 0.0, 0.0},
	  {0.0, 0.0, 1.0, 0.0, 0.0},
	  {0.0, 0.0, 0.0, 1.0, 0.0},
	  {0.0, 0.0, 0.0, 0.0, 0.0},
  };
  
  
  //rotate and tranlsate
  if(_freeLook)
    translate4d(modelview4d, _eye);

  rotate4d(modelview4d, _theta[0], 0);
  rotate4d(modelview4d, _theta[1], 1);
  rotate4d(modelview4d, _theta[2], 2);
  rotate4d(modelview4d, _theta[3], 3);

  if(!_freeLook)
    translate4d(modelview4d, _eye);



  translate4d(modelview4d, _d);
  //printMatrix(modelview4d);
  //transform hypercube
  for(i = 0; i < 16; ++i){   
    matrixCoordinateProduct(modelview4d, hypercube[i], transformedHypercube[i]);  
  }

  //set 4d projection matrix
  projection[4][3] = 1.0 / _frustum[6];


  //project into 3d
  for(i = 0; i < 16; ++i){   
    matrixCoordinateProduct(projection, transformedHypercube[i], transformedHypercube[i]);
    
  }

  //perspective division
  for(i = 0; i < 16; ++i){   
    perspectiveDivision(transformedHypercube[i]);
    printCoordinate(transformedHypercube[i]);
  }

  //give to OpenGL
  
  
  glClearColor(0.0, 0.0, .0, 0.0);
  //glClearDepth(1.0);
  glClear(GL_COLOR_BUFFER_BIT );
  //glClear(GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  //render the hypercube in 3d
  
  drawWireHypercube(transformedHypercube) ;

  //set the camera matrix (V)
  //Translation before rotation is free looking.  Rotation before
  //translation is fixed looking.
  //*old note
  //[Note that is is important to rotate before translating.  This 
  //gives the effect that camera is rotating rather than the world.]
  //create the viewing volume

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(_frustum[0], _frustum[1], _frustum[2], _frustum[3], _frustum[4], _frustum[5]);

  glutSwapBuffers(); 
}
  
//resize event handler
void Display::reShape(GLsizei x, GLsizei y)
{
}
  
//mouse event handelers
void Display::myMouse(GLint button, GLint state, GLint x, GLint y)
{
}



void Display::myWADSKeyboard(unsigned char key, GLint x, GLint y)
{
  switch(key)
	{
	case 'y':
		_theta[1] += 1.0;
		break;
	case 'Y':
		_theta[1] -= 1.0;
		break;
	case 'u':
		_theta[0] += 1.0;
		break;
	case 'U':
		_theta[0] -= 1.0;
		break;
	case 'i':
		_theta[2] += 1.0;
		break;
	case 'I':
		_theta[2] -= 1.0;
		break;
    case 'o':
		_theta[3] += 1.0;
		break;
	case 'O':
		_theta[3] -= 1.0;
		break;

//new eye controls
//FPS WADS style in 4d
	case 'w':
		_eye[2] -= .1;
		break;
	case 's':
		_eye[2] += .1;
		break;
	case 'a':
		_eye[0] -= .1;
		break;
	case 'd':
		_eye[0] += .1;
		break;
	case ' ':
		_eye[1] += .1;
		break;
	case 'c':
		_eye[1] -= .1;
		break;
    case 'z':
		_eye[3] -= .1;
		break;
	case 'x':
		_eye[3] += .1;
		break;
  
//translate world keyboard commands
    case 'g':
		_d[0] += .1;
		break;
	case 'G':
		_d[0] -= .1;
		break;
	case 'h':
		_d[1] += .1;
		break;
	case 'H':
		_d[1] -= .1;
		break;
	case 'j':
		_d[2] += .1;
		break;
	case 'J':
		_d[2] -= .1;
		break;
    case 'k':
		_d[3] += .1;
		break;
	case 'K':
		_d[3] -= .1;
		break;

	case 'f':
		if(_freeLook){
			_freeLook = false;		
		}else{
			_freeLook = true;
		}
		cout << "freeLook: " << _freeLook << endl;
		break;

	case 'q':
	case 'Q':
		cout << "quit" << endl;
		exit(0);
		
	}
	//make 0 <= thetas <= 360
    for(int i = 0; i < 4; ++i)
	  if(_theta[i] >= 360)
		_theta[i] -= 360;	

	//for(int i = 0; i < 3; ++i)
		//cout << "_up[] " << _up[0] << ' ' << _up[1] << ' ' << _up[2] << endl;
	cout << "_frustum x:" << _frustum[0] << ' ' << _frustum[1] 
		<< " y: " << _frustum[2] << ' ' << _frustum[3]
		<< " z: " << _frustum[4] << ' ' << _frustum[5]
        << " w: " << _frustum[6] << ' ' << _frustum[7] << ' ' << endl;
	cout << "_eye: " << _eye[0] <<  " "  << _eye[1] << " " << _eye[2] << " " << _eye[3] << endl;
	cout << "translation: " << _d[0] << " " << _d[1] << " " << _d[2] << " " << _d[3] << endl;
	cout << "rotation: " << _theta[0] << " " << _theta[1] << " " << _theta[2] << " " << _theta[3] << endl;
	
	glutPostRedisplay();

}


//3d simplex in strips
void Display::simplexTStrip()
{
 
  
  glBegin(GL_TRIANGLE_STRIP);
	glVertex3f(-1.0, -1.0, 0.0);
	
	glVertex3f(1.0, -1.0, 0.0);
	
	glVertex3f(0.0, 1.0, 0.0);
	
	glColor3f(.5, .5, .5);
	glVertex3f(0.0, 0.0, 1.0);

	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(-1.0, -1.0, 0.0);

	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(1.0, -1.0, 0.0);

	glVertex3f(0.0, 1.0, 0.0);
  glEnd();
  
}


//3d simplex in GL_TRIANGLES
void Display::simplexTriangles()
{
	glBegin(GL_TRIANGLES);
	
	//front face (blue)
	glColor3f(0.0, 0.0, 1.0);
	glVertex3f(-1.0, -1.0, 0.0);	
	glVertex3f(1.0, -1.0, 0.0);	
	glVertex3f(0.0, 1.0, 0.0);
	
	//back right face (green)
	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glVertex3f(1.0, -1.0, 0.0);
	glVertex3f(0.0, 0.0, 1.0);

	//back left face(red)
	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 0.0, 1.0);
	glVertex3f(-1.0, -1.0, 0.0);

	//back bottom face (grey)
	glColor3f(.5, .5, .5);
	glVertex3f(-1.0, -1.0, 0.0);
	glVertex3f(1.0, -1.0, 0.0);
	glVertex3f(0.0, 0.0, 1.0);

	glEnd();
}

//3d simplex in polygons
void Display::simplexPolygons()
{
	//front face (blue)
	glBegin(GL_TRIANGLES);

	glColor3f(0.0, 0.0, 1.0);		
	
	glVertex3f(0.0, 1.0, 0.0);			
    glVertex3f(-1.0, -1.0, 0.0);
	glVertex3f(1.0, -1.0, 0.0);
	glEnd();

	//back right face (green)
	glBegin(GL_TRIANGLES);

	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 0.0, -1.0);
	glVertex3f(1.0, -1.0, 0.0);

	glEnd();

	//back left face(red)	
	glBegin(GL_TRIANGLES);

	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);	
	glVertex3f(-1.0, -1.0, 0.0);
	glVertex3f(0.0, 0.0, -1.0);
	
	glEnd();

	//back bottom face (grey)
	glBegin(GL_TRIANGLES);

	glColor3f(.5, .5, .5);
	glVertex3f(-1.0, -1.0, 0.0);
	glVertex3f(1.0, -1.0, 0.0);
	glVertex3f(0.0, 0.0, -1.0);

	glEnd();
	
}



void Display::drawWireHypercube(GLdouble c[16][5])
{
  glColor3f(1.0, 0.0, 0.0);
 //front face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[0][0], c[0][1], c[0][2]);  
  glVertex3f(c[1][0], c[1][1], c[1][2]);  
  glVertex3f(c[2][0], c[2][1], c[2][2]);  
  glVertex3f(c[3][0], c[3][1], c[3][2]);  
  glEnd();

  //top face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[3][0], c[3][1], c[3][2]);  
  glVertex3f(c[2][0], c[2][1], c[2][2]);  
  glVertex3f(c[6][0], c[6][1], c[6][2]);  
  glVertex3f(c[7][0], c[7][1], c[7][2]);  
  glEnd();

//right face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[1][0], c[1][1], c[1][2]);  
  glVertex3f(c[5][0], c[5][1], c[5][2]);  
  glVertex3f(c[6][0], c[6][1], c[6][2]);  
  glVertex3f(c[2][0], c[2][1], c[2][2]);  
  glEnd();

  //left face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[0][0], c[0][1], c[0][2]);  
  glVertex3f(c[3][0], c[3][1], c[3][2]);  
  glVertex3f(c[7][0], c[7][1], c[7][2]);  
  glVertex3f(c[4][0], c[4][1], c[4][2]);  
  glEnd();

  //bottom face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[0][0], c[0][1], c[0][2]);  
  glVertex3f(c[4][0], c[4][1], c[4][2]);  
  glVertex3f(c[5][0], c[5][1], c[5][2]);  
  glVertex3f(c[1][0], c[1][1], c[1][2]);  
  glEnd();

  //back face front cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[4][0], c[4][1], c[4][2]);  
  glVertex3f(c[7][0], c[7][1], c[7][2]);  
  glVertex3f(c[6][0], c[6][1], c[6][2]);  
  glVertex3f(c[5][0], c[5][1], c[5][2]);  
  glEnd();


  glColor3f(0.0, 0.0, 1.0);
 //back face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+0][0], c[8+0][1], c[8+0][2]);  
  glVertex3f(c[8+1][0], c[8+1][1], c[8+1][2]);  
  glVertex3f(c[8+2][0], c[8+2][1], c[8+2][2]);  
  glVertex3f(c[8+3][0], c[8+3][1], c[8+3][2]);  
  glEnd();

  //top face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+3][0], c[8+3][1], c[8+3][2]);  
  glVertex3f(c[8+2][0], c[8+2][1], c[8+2][2]);  
  glVertex3f(c[8+6][0], c[8+6][1], c[8+6][2]);  
  glVertex3f(c[8+7][0], c[8+7][1], c[8+7][2]);  
  glEnd();

//right face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+1][0], c[8+1][1], c[8+1][2]);  
  glVertex3f(c[8+5][0], c[8+5][1], c[8+5][2]);  
  glVertex3f(c[8+6][0], c[8+6][1], c[8+6][2]);  
  glVertex3f(c[8+2][0], c[8+2][1], c[8+2][2]);  
  glEnd();

  //left face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+0][0], c[8+0][1], c[8+0][2]);  
  glVertex3f(c[8+3][0], c[8+3][1], c[8+3][2]);  
  glVertex3f(c[8+7][0], c[8+7][1], c[8+7][2]);  
  glVertex3f(c[8+4][0], c[8+4][1], c[8+4][2]);  
  glEnd();

  //bottom face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+0][0], c[8+0][1], c[8+0][2]);  
  glVertex3f(c[8+4][0], c[8+4][1], c[8+4][2]);  
  glVertex3f(c[8+5][0], c[8+5][1], c[8+5][2]);  
  glVertex3f(c[8+1][0], c[8+1][1], c[8+1][2]);  
  glEnd();

  //back face back cube
  glBegin(GL_LINE_LOOP);
  glVertex3f(c[8+4][0], c[8+4][1], c[8+4][2]);  
  glVertex3f(c[8+7][0], c[8+7][1], c[8+7][2]);  
  glVertex3f(c[8+6][0], c[8+6][1], c[8+6][2]);  
  glVertex3f(c[8+5][0], c[8+5][1], c[8+5][2]);  
  glEnd();

  glColor3f(0.0, 1.0, 0.0);
  //connect the vertices
  glBegin(GL_LINES);
  glVertex3f(c[0][0], c[0][1], c[0][2]);
  glVertex3f(c[8+0][0], c[8+0][1], c[8+0][2]);

  glVertex3f(c[1][0], c[1][1], c[1][2]);
  glVertex3f(c[8+1][0], c[8+1][1], c[8+1][2]);

  glVertex3f(c[2][0], c[2][1], c[2][2]);
  glVertex3f(c[8+2][0], c[8+2][1], c[8+2][2]);

  glVertex3f(c[3][0], c[3][1], c[3][2]);
  glVertex3f(c[8+3][0], c[8+3][1], c[8+3][2]);

  glVertex3f(c[4][0], c[4][1], c[4][2]);
  glVertex3f(c[8+4][0], c[8+4][1], c[8+4][2]);

  glVertex3f(c[5][0], c[5][1], c[5][2]);
  glVertex3f(c[8+5][0], c[8+5][1], c[8+5][2]);

  glVertex3f(c[6][0], c[6][1], c[6][2]);
  glVertex3f(c[8+6][0], c[8+6][1], c[8+6][2]);

  glVertex3f(c[7][0], c[7][1], c[7][2]);
  glVertex3f(c[8+7][0], c[8+7][1], c[8+7][2]);

  glEnd();

}
