//-------------------------------------------------------------
// Created by Aaron Severn and Fabricio Anastacio for CPSC 453
// Modified by John Brosz
//-------------------------------------------------------------

#include <math.h>
#include <GL/glut.h>
#include <stdio.h>

#define PI 3.141592653;

// for rotating the cube
float matrix[16];
float axis[3];
float angle = 0;
// previous mouse position
float previousX = 0;
float previousY = 0;
float previousZ = 0;
// mouse state
bool isLeftButtonPressed = false;
// store width and height of screen
int scrWidth;
int scrHeight;

// Project screen coordinates onto a unit hemisphere
void projScreenCoord(int x, int y, float& hemiX, float& hemiY, float& hemiZ)
{
	// find projected x & y coordinates
	
	// test length of (hemiX,hemiY) to see if > 1, if so normalize

	// test for rounding issue, set hemiZ = 0 if |(hemiX,hemiY)| > 1, else hemiZ = result of hemisphere equation
	
} // end proj screen coord

// basic openGL initiation stuff
void init()
{
	glClearColor(0,0,0,0);
	glShadeModel(GL_FLAT);
	glEnable(GL_DEPTH_TEST);
	// initialize matrix to identity
	for (int i=0; i<4; i++)
		for (int j=0; j<4; j++)
			if (i==j)
				matrix[i*4 + j] = 1;
			else
				matrix[i*4 + j] = 0;
} // end init

// window resizing
void OnReshape(int w, int h)
{
	// set the drawable region of the window
	glViewport(0,0,w,h);
	// store width & height so we can use these elsewhere
	scrWidth = w;
	scrHeight = h;
	// set up the projection matrix 
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-3, 3, -3, 3, 1.5, 20);
	// go back to modelview matrix
	glMatrixMode(GL_MODELVIEW);
}

// mouse button press event handling
//  if left button pressed, start rotation, if release, end rotation
void mouse(int button, int state, int x, int y)
{
	switch(button) {
		case GLUT_LEFT_BUTTON:
			if (GLUT_DOWN == state)
			{
				isLeftButtonPressed = true;
				projScreenCoord(x,y,previousX,previousY,previousZ);
				angle = 0;
			} else if (GLUT_UP == state)
				isLeftButtonPressed = false;
			break;
	} // end switch
	glutPostRedisplay();
} // end mouse

// mouse motion handling
//  here is where we do the trackball stuff
void mouseMotion(int x, int y)
{
	if (isLeftButtonPressed)
	{
		// project current screen coordinates onto hemi cube
		float hx, hy, hz;
		projScreenCoord(x,y,hx,hy,hz);

		// find axis by taking cross product of current and previous hemi points
		axis[0] = previousY * hz - hy * previousZ;
		axis[1] = previousZ * hx - hz * previousX;
		axis[2] = previousX * hy - hx * previousY;

		// angle can be found from magnitude of cross product
		
		// 'add' this rotation matrix to our 'total' rotation matrix
		glPushMatrix(); // save the old matrix so we don't mess anything up
		glLoadIdentity();
		glRotatef(angle, axis[0], axis[1], axis[2]); // our newly calculated rotation
		glMultMatrixf(matrix); // our previous rotation matrix
		glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*) matrix); // we've let OpenGL do our matrix mult for us, now get this result & store it
		glPopMatrix(); // return modelview to its old value;

		// set current to previous
		previousX = hx;
		previousY = hy;
		previousZ = hz;

		glutPostRedisplay();
	} // end if
} // end mouse motion


// our display function, mostly the usual stuff, be sure to multiply in our rotation matrix
void display() 
{
	// clear the screen & depth buffer
	glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);

	// clear any previous transform
	glLoadIdentity();

	// set the camera position
	gluLookAt(	0,0,5,	//	eye pos
				0,0,0,	//	aim point
				0,1,0);	//	up direction

	// do our calculate rotation matrix
	glMultMatrixf(matrix);

	// draw a simple teapot (thank you glut library)
	glutWireTeapot(2.0);

	// currently we've been drawing to the back buffer, we need to swap the back buffer with the front one to make the image visible
	glutSwapBuffers();
}

int main(int argc,char** argv) {

	// initialise glut
	glutInit(&argc,argv);

	// we want depth buffer, RGBA display mode, and double buffering
	glutInitDisplayMode(GLUT_DEPTH|GLUT_RGBA|GLUT_DOUBLE);

	// set the window size
	glutInitWindowSize(500,500);

	// create the window
	glutCreateWindow("Virtual Trackball");

	// set the callbacks
	glutDisplayFunc(display);
	glutReshapeFunc(OnReshape);
	glutMotionFunc(mouseMotion);
	glutMouseFunc(mouse);

	// do our intialization
	init();

	glutMainLoop();
	return 0;
}

