/** rubiks.c
 *
 * Brett Tsudama
 * 5 December 2003
 * CSC 471 - Final project
 *
 * This program will provide a graphical, interactive representation
 * of a Rubik's Cube.
 */

//include file
#include "rubiks.h"

//global variables
int GW;					//global width
int GH;					//global height
int mouseX;				//saves first mouse click coordinates
int mouseY;				//saves first mouse click coordinates
int endX;				//saves ending mouse coordinates (for trackball)
int endY;				//saves ending mouse coordinates (for trackball)
bool draw_axis = true;	//draw axis boolean
struct vector *v1;		//Used for rotation
struct vector *v2;		//Used for rotation
struct vector *axis;	//Used for rotation (axis of rotation)
struct cubelet *cubelets[3][3][3];	//array to hold cubelets
struct vertex *centerPositions[3][3][3];	//array to hold center positions
bool animate = false;		//animate boolean
struct cubelet *A_cubelet;	//cubelet for slice rotations
int A_axis;					//axis for slice rotations (X_AXIS, etc)
int A_direction;			//direction for slice rotations (CLOCKWISE, etc)
float A_angle;				//angle for slice rotations
int A_slice;				//slice we are rotating (BA,LE,FR,etc)
bool mouse_click = false;	//mouse clicked boolean
GLuint selectBuf[BUFSIZE];	//select buffer for picking
bool picked_cubelet = false;	//is a cubelet currently selected?
int selected_cubelet;			//id of the selected cubelet
int cubeletLocations[3][3][3];	//locations of the cubelets within the cube
int selected_face;				//the currently selected face (picking)
bool track = false;			//trackball
float angle_of_rotation=0;	//angle for rotation of the entire cube

//translation matrix for rotating the entire cube
float M[4][4] = {1,0,0,0,
                 0,1,0,0,
				 0,0,1,0,
				 0,0,0,1};

//translation matrix for rotating/animating slices
float rotate[4][4] = {1,0,0,0,
                      0,1,0,0,
				      0,0,1,0,
				      0,0,0,1};
//****************************************************************************
//DRAWING STUFF
//****************************************************************************
/** display
 *
 * The main display() method will be called whenever we refresh the screen
 */
void display() {
	glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);

	renderObjs();

	glutSwapBuffers();
}
/** renderObjs()
 *
 * This method takes care of drawing the cube to the screen.  It applies the
 * cube rotation matrix and the individual slice rotation matrix.
 */
void renderObjs() {
	glLoadIdentity();

	//update cube rotation matrix M if we are dynamically rotating the cube
    if (track) {
      //trick to use the hardware to compute what the rotation matrix would be
      glPushMatrix();
      glLoadIdentity();
      glRotatef(angle_of_rotation, axis->x, axis->y,axis->z);
      glMultMatrixf( (float *) M);
      glGetFloatv(GL_MODELVIEW_MATRIX, *M);
      glPopMatrix();
    }
    //apply the computed rotation
    glMultMatrixf( (float *) M);

	//draw vertices
	if (draw_axis) {
		glColor3f(0,1,1);
		glBegin(GL_LINES);
		glVertex3f(0,0,0);
		glVertex3f(1,0,0);
		glVertex3f(0,0,0);
		glVertex3f(0,1,0);
		glVertex3f(0,0,0);
		glVertex3f(0,0,1);
		glEnd();
	}
	//are we animating this?
	if (animate == true) {
		//draw all the non-slice cubelets
		for (int i=0;i<3;i++)
			for (int j=0;j<3;j++)
				for (int k=0;k<3;k++) {
					if (cubelets[i][j][k]->ID == A_cubelet->children[A_axis][0]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][1]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][2]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][3]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][4]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][5]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][6]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][7]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->ID)
					{
						//do nothing
					} else {
						drawCubelet(cubelets[i][j][k],centerPositions[i][j][k]);	
					}
				}
		//apply the slice rotation matrix
		glMultMatrixf(*rotate);
		//draw all the slice cubelets
		for (int i=0;i<3;i++)
			for (int j=0;j<3;j++)
				for (int k=0;k<3;k++) {
					if (cubelets[i][j][k]->ID == A_cubelet->children[A_axis][0]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][1]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][2]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][3]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][4]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][5]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][6]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->children[A_axis][7]->ID ||
						cubelets[i][j][k]->ID == A_cubelet->ID) 
					{
						drawCubelet(cubelets[i][j][k],centerPositions[i][j][k]);
					} 
				}	
	} else { //animate == false
		//draw picking faces so we can determine which face we are selecting
		drawFacesPicking();
		//draw the cube
		for (int i=0;i<3;i++)
			for (int j=0;j<3;j++)
				for (int k=0;k<3;k++)
					drawCubelet(cubelets[i][j][k],centerPositions[i][j][k]);
	}
}

/** drawFacesPicking()
 *
 * This method will draw 6 entire faces on the cube (NOT individual cubelets)
 * and will push the appropriate face name (FRONT,BACK,BOTTOM,etc) onto the
 * name stack when drawing.  This will allow us to determine which face
 * the mouse is clicked on.  Note that the face is drawn slightly "below"
 * the "real" outer face to ensure it will not block the "real" face.
 */
void drawFacesPicking() {
	glLineWidth(1);
	setColor(BLACK);
	//initialize the namestack
	glInitNames();

	//draw each respective face, pushing the name onto the stack before drawing
	glPushName(FRONT);
	glBegin(GL_POLYGON);
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glEnd();
	glPopName();

	glPushName(BACK);
	glBegin(GL_POLYGON);
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glEnd();
	glPopName();

	glPushName(LEFT);
	glBegin(GL_POLYGON);
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glEnd();
	glPopName();

	glPushName(RIGHT);
	glBegin(GL_POLYGON);
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glEnd();
	glPopName();

	glPushName(TOP);
	glBegin(GL_POLYGON);
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glEnd();
	glPopName();

	glPushName(BOTTOM);
	glBegin(GL_POLYGON);
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glVertex3f(-((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),-((C_W-.0001)*1.5));
	glVertex3f(((C_W-.0001)*1.5),-((C_W-.0001)*1.5),((C_W-.0001)*1.5));
	glEnd();
	glPopName();
}

/** drawCubelet()
 *
 * This method will draw each individual cubelet.  It takes a reference to
 * the cubelet and a vertex which represents it's center position in
 * world coordinates.
 */
void drawCubelet(struct cubelet *c, struct vertex *center) {
	//draw the center positions as points (debugging)
	setColor(WHITE);
	glPointSize(2.0);
	glBegin(GL_POINTS);
	glVertex3f(center->x,center->y,center->z);
	glEnd();

	//push the name of this cubelet for picking
	glInitNames();
	glPushName(c->ID);

	//draw edges
	setColor(BLACK);
	glLineWidth(2);
	glBegin(GL_LINE_LOOP);
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glEnd();
	glBegin(GL_LINES);
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z-(C_W/2));

	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z-(C_W/2));

	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glEnd();

	//front face
	setColor(c->faceColors[FRONT]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glEnd();

	//back face
	setColor(c->faceColors[BACK]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glEnd();

	//top face
	setColor(c->faceColors[TOP]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glEnd();

	//bottom face
	setColor(c->faceColors[BOTTOM]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glEnd();

	//left face
	setColor(c->faceColors[LEFT]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x-(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glEnd();

	//right face
	setColor(c->faceColors[RIGHT]);
	glBegin(GL_POLYGON);
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z+(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y+(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z-(C_W/2));
	glVertex3f(center->x+(C_W/2),center->y-(C_W/2),center->z+(C_W/2));
	glEnd();

	//for picking
	glPopName();
}



//****************************************************************************
//MOUSE STUFF
//****************************************************************************
/** mouse()
 *
 * This event callback is executed whenever there is a mouse event
 */
void mouse(int button, int state, int x, int y) {
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && animate == false) {
		mouse_click = true;
		//printf("mouse clicked at %d %d\n", x, y);
		mouseX = x;
		mouseY = GH-y-1;
		//check to see if we've selected a cubelet
		if ( (selected_cubelet = doPicking(x,y)) < 0)
			picked_cubelet = false;
		else
			picked_cubelet = true;

		//initialize v1,v2 for trackball rotations
		initialize_vectors();
	} else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP && animate == false) {
		mouse_click = false;
		angle_of_rotation = 0;
	}
}

/** mouseMove
 *
 * This even callback is executed whenver the mouse is moved
 */
 void mouseMove(int x, int y) {
	//printf("mouse moved at %d %d\n", x, GH-y-1);
	if (animate == false && mouse_click == true && picked_cubelet == false) {
		endX = x;
		endY = GH-y-1;
		track = true;
		//do trackball stuff
		trackBall();
		glutPostRedisplay();
	} else if (picked_cubelet == true) {
		int i;
		//check to see if the mouse has changed positions (from one cubelet
		//to another cubelet).  If so, then we need to do a slice
		//rotation.
		if (selected_cubelet != (i = doPicking(x,y))) {
			rotateSlice(selected_cubelet,i);
			picked_cubelet = false;
			mouse_click = false;
		}
	}
}

//****************************************************************************
//PICKING/ROTATION STUFF
//****************************************************************************
  /** doPicking()
 *
 * This method takes in two mouse coordinates and will return the name of
 * the cubelet that is selected.  It will also set the global variable
 * selected_face to the face that lies under the mouse coordinates.
 */
int doPicking(int x,int y) {	
	GLint viewport[4];

	//set the select buffer and enter SELECT mode
	glSelectBuffer(BUFSIZE,selectBuf);
	glRenderMode(GL_SELECT);

	//store the current viewing
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();

	//set up the viewing
	glGetIntegerv(GL_VIEWPORT,viewport);

	//set up pixel picking region near mouse
	gluPickMatrix((GLdouble)x,(GLdouble)viewport[3]-y,PICK_REGION,PICK_REGION,viewport);
	glOrtho( (-(float)GW/GH), ((float)GW/GH), -1, 1, -1, 1);

	//return to model matrix
	glMatrixMode(GL_MODELVIEW);

	//draw the cube
	renderObjs();

	// restoring the original projection matrix
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

	glutSwapBuffers();

	//get the number of hits
	unsigned int hits = glRenderMode(GL_RENDER);

	if (hits != 0) { //we clicked on the cube somewhere
		unsigned int i;
		GLuint names, *ptr, minZ,*ptrNames, numberOfNames;
	
		ptr = (GLuint *) selectBuf;
		ptrNames = ptr;
		minZ = 0xffffffff;
		for (i = 0; i < hits; i++) {	
			names = *ptr;
			ptr++;
			if (*ptr < minZ) {
				selected_face = *ptrNames;
				numberOfNames = names;
				minZ = *ptr;
				ptrNames = ptr+2;
			}

			ptr += names+2;
		}
		//printf ("Selected cubelet: [%d]\n",*ptrNames);
		//printf ("Selected face: [%d]\n",selected_face);
		//return the name of the selected cubelet
		return *ptrNames;
	} 
	return -1;
}

/** rotateSlice()
 *
 * This function is the entry point into the slice rotation functionality.
 * This function is called by the mouse_move function when we have determined
 * that we need to do a slice rotation.  The ID of the original cubelet (first
 * click) and the new cublet (adjacent cubelet that the mouse is dragged onto)
 * are passed in as arguments.
 */
void rotateSlice(int old_cubelet, int new_cubelet) {

	//check which face we are dealing with
	if (selected_face == FRONT) {
		if (getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 3 ||
			getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 6)
			animateTurn(X_AXIS,C_CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 1 ||
			getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 2)
			animateTurn(Y_AXIS,CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 0 ||
			getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 1)
			animateTurn(Y_AXIS,C_CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 5 ||
			getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 8)
			animateTurn(X_AXIS,C_CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 4 ||
			getCubeletLocation(old_cubelet) == 4 && getCubeletLocation(new_cubelet) == 5)
			animateTurn(Y_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 0 ||
			getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 3)
			animateTurn(X_AXIS,CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 4 && getCubeletLocation(new_cubelet) == 3 ||
			getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 4)
			animateTurn(Y_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 4 && getCubeletLocation(new_cubelet) == 1 ||
			getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 4)
			animateTurn(X_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 4 && getCubeletLocation(new_cubelet) == 7 ||
			getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 4)
			animateTurn(X_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 2 ||
			getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 5)
			animateTurn(X_AXIS,CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 7 ||
			getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 8)
			animateTurn(Y_AXIS,CLOCKWISE,TO);
		else if (getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 6 ||
			getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 7)
			animateTurn(Y_AXIS,C_CLOCKWISE,TO);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else if (selected_face == BACK) {
		if (getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 24 ||
			getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 21)
			animateTurn(X_AXIS,CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 19 && getCubeletLocation(new_cubelet) == 20 ||
			getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 19)
			animateTurn(Y_AXIS,C_CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 19 ||
			getCubeletLocation(old_cubelet) ==19 && getCubeletLocation(new_cubelet) == 18)
			animateTurn(Y_AXIS,CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 22 && getCubeletLocation(new_cubelet) == 25 ||
			getCubeletLocation(old_cubelet) == 19 && getCubeletLocation(new_cubelet) == 22)
			animateTurn(X_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 26 ||
			getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 23)
			animateTurn(X_AXIS,CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 22 && getCubeletLocation(new_cubelet) == 23 ||
			getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 22)
			animateTurn(Y_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 21 ||
			getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 18)
			animateTurn(X_AXIS,C_CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 22 ||
			getCubeletLocation(old_cubelet) == 22 && getCubeletLocation(new_cubelet) == 21)
			animateTurn(Y_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 22 ||
			getCubeletLocation(old_cubelet) == 22 && getCubeletLocation(new_cubelet) == 19)
			animateTurn(X_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 23 ||
			getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 20)
			animateTurn(X_AXIS,C_CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 26 ||
			getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 25)
			animateTurn(Y_AXIS,C_CLOCKWISE,TO);
		else if (getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 25 ||
			getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 24)
			animateTurn(Y_AXIS,CLOCKWISE,TO);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else if (selected_face == BOTTOM) {
		if (getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 19 ||
			getCubeletLocation(old_cubelet) == 19 && getCubeletLocation(new_cubelet) == 20)
			animateTurn(Z_AXIS,CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 9 ||
			getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 0)
			animateTurn(X_AXIS,C_CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 19 && getCubeletLocation(new_cubelet) == 18 ||
			getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 19)
			animateTurn(Z_AXIS,C_CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 19 && getCubeletLocation(new_cubelet) == 10 ||
			getCubeletLocation(old_cubelet) == 10 && getCubeletLocation(new_cubelet) == 1)
			animateTurn(X_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 11 ||
			getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 2)
			animateTurn(X_AXIS,C_CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 10 ||
			getCubeletLocation(old_cubelet) == 10 && getCubeletLocation(new_cubelet) == 11)
			animateTurn(Z_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 18 ||
			getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 9)
			animateTurn(X_AXIS,CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 10 && getCubeletLocation(new_cubelet) == 19 ||
			getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 10)
			animateTurn(X_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 10 && getCubeletLocation(new_cubelet) == 9 ||
			getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 10)
			animateTurn(Z_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 20 ||
			getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 11)
			animateTurn(X_AXIS,CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 1 ||
			getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 2)
			animateTurn(Z_AXIS,CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 1 && getCubeletLocation(new_cubelet) == 0 ||
			getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 1)
			animateTurn(Z_AXIS,C_CLOCKWISE,FR);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else if (selected_face == TOP) {
		if (getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 26 ||
			getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 25)
			animateTurn(Z_AXIS,C_CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 6 ||
			getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 15)
			animateTurn(X_AXIS,CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 25 ||
			getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 24)
			animateTurn(Z_AXIS,CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 16 && getCubeletLocation(new_cubelet) == 7 ||
			getCubeletLocation(old_cubelet) == 25 && getCubeletLocation(new_cubelet) == 16)
			animateTurn(X_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 8 ||
			getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 17)
			animateTurn(X_AXIS,CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 16 && getCubeletLocation(new_cubelet) == 17 ||
			getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 16)
			animateTurn(Z_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 15 ||
			getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 24)
			animateTurn(X_AXIS,C_CLOCKWISE,LE);
		else if (getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 16 ||
			getCubeletLocation(old_cubelet) == 16 && getCubeletLocation(new_cubelet) == 25)
			animateTurn(X_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 16 ||
			getCubeletLocation(old_cubelet) == 16 && getCubeletLocation(new_cubelet) == 15)
			animateTurn(Z_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 17 ||
			getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 26)
			animateTurn(X_AXIS,C_CLOCKWISE,RI);
		else if (getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 8 ||
			getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 7)
			animateTurn(Z_AXIS,C_CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 7 ||
			getCubeletLocation(old_cubelet) == 7 && getCubeletLocation(new_cubelet) == 6)
			animateTurn(Z_AXIS,CLOCKWISE,FR);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else if (selected_face == LEFT) {
		if (getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 0 ||
			getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 9)
			animateTurn(Y_AXIS,CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 24 ||
			getCubeletLocation(old_cubelet) == 18 && getCubeletLocation(new_cubelet) == 21)
			animateTurn(Z_AXIS,C_CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 9 ||
			getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 18)
			animateTurn(Y_AXIS,C_CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 12 && getCubeletLocation(new_cubelet) == 15 ||
			getCubeletLocation(old_cubelet) == 9 && getCubeletLocation(new_cubelet) == 12)
			animateTurn(Z_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 6 ||
			getCubeletLocation(old_cubelet) == 0 && getCubeletLocation(new_cubelet) == 3)
			animateTurn(Z_AXIS,C_CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 18 ||
			getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 21)
			animateTurn(Z_AXIS,CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 12 && getCubeletLocation(new_cubelet) == 3 ||
			getCubeletLocation(old_cubelet) == 21 && getCubeletLocation(new_cubelet) == 12)
			animateTurn(Y_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 12 ||
			getCubeletLocation(old_cubelet) == 12 && getCubeletLocation(new_cubelet) == 9)
			animateTurn(Z_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 12 ||
			getCubeletLocation(old_cubelet) == 12 && getCubeletLocation(new_cubelet) == 21)
			animateTurn(Y_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 3 ||
			getCubeletLocation(old_cubelet) == 3 && getCubeletLocation(new_cubelet) == 0)
			animateTurn(Z_AXIS,CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 6 ||
			getCubeletLocation(old_cubelet) == 24 && getCubeletLocation(new_cubelet) == 15)
			animateTurn(Y_AXIS,CLOCKWISE,TO);
		else if (getCubeletLocation(old_cubelet) == 6 && getCubeletLocation(new_cubelet) == 15 ||
			getCubeletLocation(old_cubelet) == 15 && getCubeletLocation(new_cubelet) == 24)
			animateTurn(Y_AXIS,C_CLOCKWISE,TO);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else if (selected_face == RIGHT) {
		if (getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 2 ||
			getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 11)
			animateTurn(Y_AXIS,C_CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 26 ||
			getCubeletLocation(old_cubelet) == 20 && getCubeletLocation(new_cubelet) == 23)
			animateTurn(Z_AXIS,CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 11 ||
			getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 20)
			animateTurn(Y_AXIS,CLOCKWISE,BO);
		else if (getCubeletLocation(old_cubelet) == 14 && getCubeletLocation(new_cubelet) == 17 ||
			getCubeletLocation(old_cubelet) == 11 && getCubeletLocation(new_cubelet) == 14)
			animateTurn(Z_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 8 ||
			getCubeletLocation(old_cubelet) == 2 && getCubeletLocation(new_cubelet) == 5)
			animateTurn(Z_AXIS,CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 14 && getCubeletLocation(new_cubelet) == 5 ||
			getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 14)
			animateTurn(Y_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 23 ||
			getCubeletLocation(old_cubelet) == 23 && getCubeletLocation(new_cubelet) == 20)
			animateTurn(Z_AXIS,C_CLOCKWISE,BA);
		else if (getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 14 ||
			getCubeletLocation(old_cubelet) == 14 && getCubeletLocation(new_cubelet) == 11)
			animateTurn(Z_AXIS,C_CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 14 ||
			getCubeletLocation(old_cubelet) == 14 && getCubeletLocation(new_cubelet) == 23)
			animateTurn(Y_AXIS,CLOCKWISE,MID);
		else if (getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 5 ||
			getCubeletLocation(old_cubelet) == 5 && getCubeletLocation(new_cubelet) == 2)
			animateTurn(Z_AXIS,C_CLOCKWISE,FR);
		else if (getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 8 ||
			getCubeletLocation(old_cubelet) == 26 && getCubeletLocation(new_cubelet) == 17)
			animateTurn(Y_AXIS,C_CLOCKWISE,TO);
		else if (getCubeletLocation(old_cubelet) == 8 && getCubeletLocation(new_cubelet) == 17 ||
			getCubeletLocation(old_cubelet) == 17 && getCubeletLocation(new_cubelet) == 26)
			animateTurn(Y_AXIS,CLOCKWISE,TO);
		else
			printf("SHOULD NEVER HAPPEN\n");
	} else {
		printf("SHOULD NEVER HAPPEN\n");
	}

}
/** rotateSlice
 *
 * This function will "rotate" a slice of the cube by changing the cubelts[][][]
 * array.  It updates the pointers contained in this array to reflect the
 * cubelet's new positions in the cube.
 */
void rotateSlice(int axis, int direction, int slice) {
	if (axis == Z_AXIS) {
		if (direction == C_CLOCKWISE) {
			//corner pieces
			struct cubelet *c = cubelets[LE][BO][slice];
			cubelets[LE][BO][slice] = cubelets[RI][BO][slice];
			rotateCubelet(axis,direction,cubelets[LE][BO][slice]);

			cubelets[RI][BO][slice] = cubelets[RI][TO][slice];
			rotateCubelet(axis,direction,cubelets[RI][BO][slice]);

			cubelets[RI][TO][slice] = cubelets[LE][TO][slice];
			rotateCubelet(axis,direction,cubelets[RI][TO][slice]);

			cubelets[LE][TO][slice] = c;
			rotateCubelet(axis,direction,cubelets[LE][TO][slice]);

			//edge pieces
			c = cubelets[LE][MID][slice];
			cubelets[LE][MID][slice] = cubelets[MID][BO][slice];
			rotateCubelet(axis,direction,cubelets[LE][MID][slice]);

			cubelets[MID][BO][slice] = cubelets[RI][MID][slice];
			rotateCubelet(axis,direction,cubelets[MID][BO][slice]);

			cubelets[RI][MID][slice] = cubelets[MID][TO][slice];
			rotateCubelet(axis,direction,cubelets[RI][MID][slice]);

			cubelets[MID][TO][slice] = c;
			rotateCubelet(axis,direction,cubelets[MID][TO][slice]);
		} else if (direction == CLOCKWISE) {
			struct cubelet *c = cubelets[LE][BO][slice];
			cubelets[LE][BO][slice] = cubelets[LE][TO][slice];
			rotateCubelet(axis,direction,cubelets[LE][BO][slice]);

			cubelets[LE][TO][slice] = cubelets[RI][TO][slice];
			rotateCubelet(axis,direction,cubelets[LE][TO][slice]);

			cubelets[RI][TO][slice] = cubelets[RI][BO][slice];
			rotateCubelet(axis,direction,cubelets[RI][TO][slice]);

			cubelets[RI][BO][slice] = c;
			rotateCubelet(axis,direction,cubelets[RI][BO][slice]);

			c = cubelets[LE][MID][slice];
			cubelets[LE][MID][slice] = cubelets[MID][TO][slice];
			rotateCubelet(axis,direction,cubelets[LE][MID][slice]);

			cubelets[MID][TO][slice] = cubelets[RI][MID][slice];
			rotateCubelet(axis,direction,cubelets[MID][TO][slice]);

			cubelets[RI][MID][slice] = cubelets[MID][BO][slice];
			rotateCubelet(axis,direction,cubelets[RI][MID][slice]);

			cubelets[MID][BO][slice] = c;
			rotateCubelet(axis,direction,cubelets[MID][BO][slice]);

		} else {
			printf("should never happen\n");
		}
	} else if (axis == Y_AXIS) {
		if (direction == C_CLOCKWISE) {
			//corner pieces
			struct cubelet *c = cubelets[LE][slice][FR];
			cubelets[LE][slice][FR] = cubelets[RI][slice][FR];
			rotateCubelet(axis,direction,cubelets[LE][slice][FR]);

			cubelets[RI][slice][FR] = cubelets[RI][slice][BA];
			rotateCubelet(axis,direction,cubelets[RI][slice][FR]);

			cubelets[RI][slice][BA] = cubelets[LE][slice][BA];
			rotateCubelet(axis,direction,cubelets[RI][slice][BA]);

			cubelets[LE][slice][BA] = c;
			rotateCubelet(axis,direction,cubelets[LE][slice][BA]);

			//edge pieces
			c = cubelets[LE][slice][MID];
			cubelets[LE][slice][MID] = cubelets[MID][slice][FR];
			rotateCubelet(axis,direction,cubelets[LE][slice][MID]);

			cubelets[MID][slice][FR] = cubelets[RI][slice][MID];
			rotateCubelet(axis,direction,cubelets[MID][slice][FR]);

			cubelets[RI][slice][MID] = cubelets[MID][slice][BA];
			rotateCubelet(axis,direction,cubelets[RI][slice][MID]);

			cubelets[MID][slice][BA] = c;
			rotateCubelet(axis,direction,cubelets[MID][slice][BA]);
		} else if (direction == CLOCKWISE) {
			struct cubelet *c = cubelets[LE][slice][FR];
			cubelets[LE][slice][FR] = cubelets[LE][slice][BA];
			rotateCubelet(axis,direction,cubelets[LE][slice][FR]);

			cubelets[LE][slice][BA] = cubelets[RI][slice][BA];
			rotateCubelet(axis,direction,cubelets[LE][slice][BA]);

			cubelets[RI][slice][BA] = cubelets[RI][slice][FR];
			rotateCubelet(axis,direction,cubelets[RI][slice][BA]);

			cubelets[RI][slice][FR] = c;
			rotateCubelet(axis,direction,cubelets[RI][slice][FR]);

			c = cubelets[LE][slice][MID];
			cubelets[LE][slice][MID] = cubelets[MID][slice][BA];
			rotateCubelet(axis,direction,cubelets[LE][slice][MID]);

			cubelets[MID][slice][BA] = cubelets[RI][slice][MID];
			rotateCubelet(axis,direction,cubelets[MID][slice][BA]);

			cubelets[RI][slice][MID] = cubelets[MID][slice][FR];
			rotateCubelet(axis,direction,cubelets[RI][slice][MID]);

			cubelets[MID][slice][FR] = c;
			rotateCubelet(axis,direction,cubelets[MID][slice][FR]);

		} else {
			printf("should never happen\n");
		}
	} else if (axis == X_AXIS) {
		if (direction == C_CLOCKWISE) {
			//corner pieces
			struct cubelet *c = cubelets[slice][BO][FR];
			cubelets[slice][BO][FR] = cubelets[slice][BO][BA];
			rotateCubelet(axis,direction,cubelets[slice][BO][FR]);

			cubelets[slice][BO][BA] = cubelets[slice][TO][BA];
			rotateCubelet(axis,direction,cubelets[slice][BO][BA]);

			cubelets[slice][TO][BA] = cubelets[slice][TO][FR];
			rotateCubelet(axis,direction,cubelets[slice][TO][BA]);

			cubelets[slice][TO][FR] = c;
			rotateCubelet(axis,direction,cubelets[slice][TO][FR]);

			//edge pieces
			c = cubelets[slice][MID][FR];
			cubelets[slice][MID][FR] = cubelets[slice][BO][MID];
			rotateCubelet(axis,direction,cubelets[slice][MID][FR]);

			cubelets[slice][BO][MID] = cubelets[slice][MID][BA];
			rotateCubelet(axis,direction,cubelets[slice][BO][MID]);

			cubelets[slice][MID][BA] = cubelets[slice][TO][MID];
			rotateCubelet(axis,direction,cubelets[slice][MID][BA]);

			cubelets[slice][TO][MID] = c;
			rotateCubelet(axis,direction,cubelets[slice][TO][MID]);
		} else if (direction == CLOCKWISE) {
			struct cubelet *c = cubelets[slice][BO][FR];
			cubelets[slice][BO][FR] = cubelets[slice][TO][FR];
			rotateCubelet(axis,direction,cubelets[slice][BO][FR]);

			cubelets[slice][TO][FR] = cubelets[slice][TO][BA];
			rotateCubelet(axis,direction,cubelets[slice][TO][FR]);

			cubelets[slice][TO][BA] = cubelets[slice][BO][BA];
			rotateCubelet(axis,direction,cubelets[slice][TO][BA]);

			cubelets[slice][BO][BA] = c;
			rotateCubelet(axis,direction,cubelets[slice][BO][BA]);

			//edge pieces
			c = cubelets[slice][MID][FR];
			cubelets[slice][MID][FR] = cubelets[slice][TO][MID];
			rotateCubelet(axis,direction,cubelets[slice][MID][FR]);

			cubelets[slice][TO][MID] = cubelets[slice][MID][BA];
			rotateCubelet(axis,direction,cubelets[slice][TO][MID]);

			cubelets[slice][MID][BA] = cubelets[slice][BO][MID];
			rotateCubelet(axis,direction,cubelets[slice][MID][BA]);

			cubelets[slice][BO][MID] = c;
			rotateCubelet(axis,direction,cubelets[slice][BO][MID]);

		} else {
			printf("should never happen\n");
		}
	} else {
		printf("SHOULD NEVER HAPPEN\n");
	}

	updateHierarchy();
}

/** rotateCubelet()
 *
 * This function will "rotate" a cubelet by changing the 
 * cubelet->faceColors[] array.  This is called whenever a slice
 * rotation is done (cubelets change position as well as orientation!)
 */
void rotateCubelet(int axis, int direction, struct cubelet *c) {
	if (axis == Z_AXIS) {
		int top = c->faceColors[TOP];
		if (direction == C_CLOCKWISE) {
			c->faceColors[TOP] = c->faceColors[LEFT];
			c->faceColors[LEFT] = c->faceColors[BOTTOM];
			c->faceColors[BOTTOM] = c->faceColors[RIGHT];
			c->faceColors[RIGHT] = top;
		} else if (direction == CLOCKWISE) {
			c->faceColors[TOP] = c->faceColors[RIGHT];
			c->faceColors[RIGHT] = c->faceColors[BOTTOM];
			c->faceColors[BOTTOM] = c->faceColors[LEFT];
			c->faceColors[LEFT] = top;
		} else {
			printf("should never happen!\n");
		}
	} else if (axis == Y_AXIS) {
		int front = c->faceColors[FRONT];
		if (direction == C_CLOCKWISE) {	
			c->faceColors[FRONT] = c->faceColors[RIGHT];
			c->faceColors[RIGHT] = c->faceColors[BACK];
			c->faceColors[BACK] = c->faceColors[LEFT];
			c->faceColors[LEFT] = front;
		} else if (direction == CLOCKWISE) {
			c->faceColors[FRONT] = c->faceColors[LEFT];
			c->faceColors[LEFT] = c->faceColors[BACK];
			c->faceColors[BACK] = c->faceColors[RIGHT];
			c->faceColors[RIGHT] = front;
		} else {
			printf("should never happen!\n");
		}
	} else if (axis == X_AXIS) {
		int front = c->faceColors[FRONT];
		if (direction == C_CLOCKWISE) {	
			c->faceColors[FRONT] = c->faceColors[BOTTOM];
			c->faceColors[BOTTOM] = c->faceColors[BACK];
			c->faceColors[BACK] = c->faceColors[TOP];
			c->faceColors[TOP] = front;
		} else if (direction == CLOCKWISE) {
			c->faceColors[FRONT] = c->faceColors[TOP];
			c->faceColors[TOP] = c->faceColors[BACK];
			c->faceColors[BACK] = c->faceColors[BOTTOM];
			c->faceColors[BOTTOM] = front;
		} else {
			printf("should never happen!\n");
		}
	} else {
		printf("should never happen\n");
	}
}

//****************************************************************************
//ANIMATION STUFF
//****************************************************************************

/** animateTurn()
 *
 * This function is the entry point into the animation functionality of this
 * program.  It will set the animate boolean to true and it will set the 
 * global A_** variables so the idle() function will know which cubelet
 * is the "parent" and what direction/axis for rotation.
 */
void animateTurn(int axis, int direction, int slice) {
	animate = true;
	A_axis = axis;
	A_direction = direction;
	A_slice = slice;
	A_angle = 0;
	if (A_axis == X_AXIS) 
		A_cubelet = cubelets[slice][MID][MID];
	else if (A_axis == Y_AXIS)
		A_cubelet = cubelets[MID][slice][MID];
	else if (A_axis == Z_AXIS)
		A_cubelet = cubelets[MID][MID][slice];
}
/** idle()
 *
 * This function is always being executed.  It will handle the slice
 * rotation animations.
 */
void idle() {
	if (animate == true) {
		//adjust the A_angle
		if (A_direction == CLOCKWISE)
			A_angle = A_angle + ANGLE_INCREMENT;
		else
			A_angle = A_angle - ANGLE_INCREMENT;
		
		//load the slice rotation matrix
		glPushMatrix();
		glLoadIdentity();
		if (A_axis == X_AXIS)
			glRotatef(A_angle,1,0,0);
		if (A_axis == Y_AXIS)
			glRotatef(A_angle,0,1,0);
		if (A_axis == Z_AXIS)
			glRotatef(A_angle,0,0,1);
		glGetFloatv(GL_MODELVIEW_MATRIX,*rotate);
		glPopMatrix();
		
		//check here to see if rotation is finished
		if (A_angle >= 90 || A_angle <= -90) {
			animate = false;
			//do the actual "logical" rotation (update the cubelets[][][])
			rotateSlice(A_axis,A_direction,A_slice);
		}
	} else {
		animate = false;
	}
	glutPostRedisplay();
}
//****************************************************************************
//HELPER functions
//****************************************************************************
/** getCubeletLocation()
 *
 * This helper function will take in the ID of a cubelet and will return the
 * location of the cubelet within the cube.
 */
int getCubeletLocation(int ID) {
	for (int i=0;i<3;i++) 
		for (int j=0;j<3;j++) 
			for (int k=0;k<3;k++) 
				if (cubelets[i][j][k]->ID == ID)
					return cubeletLocations[i][j][k];

	//should never happen
	return -1;
}


/** initCube()
 *
 * This method will allocate memory for the 27 cubelets and will store 
 * references to these cubelets in the cubelets[][][] array.  It will
 * also initialize the cubeletLocations[][][] array
 */
void initCube() {
	struct cubelet *c;
	int id = 0;
	for (int i=0;i<3;i++) {
		for (int j=0;j<3;j++) {
			for (int k=0;k<3;k++) {
				c = (struct cubelet *)malloc(sizeof(struct cubelet));
				c->ID = id;
				resetCubeletFaces(c);
				cubelets[k][j][i] = c;
				cubeletLocations[k][j][i] = id;
				id++;
			}
		}
	}


	setFaceColor(FRONT,RED);
	setFaceColor(BACK,ORANGE);
	setFaceColor(TOP,WHITE);
	setFaceColor(BOTTOM,BLUE);
	setFaceColor(LEFT,GREEN);
	setFaceColor(RIGHT,YELLOW);

	//update the child/parent references
	updateHierarchy();
}

/** resetCubeletFaces()
 * 
 * This helper function will set all faces of a cubelet to grey.
 */
void resetCubeletFaces(struct cubelet *c) {
	for (int i=0;i<6;i++)
		c->faceColors[i] = GREY;
}
/** updateHierarchy()
 *
 * This function will update every cubelet's children[] references.  This is 
 * called after every move since cubelets will have changed positions.
 */
void updateHierarchy() {
	//update front face references
	cubelets[MID][MID][FR]->children[Z_AXIS][0] = cubelets[LE][BO][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][1] = cubelets[LE][MID][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][2] = cubelets[LE][TO][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][3] = cubelets[MID][TO][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][4] = cubelets[RI][TO][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][5] = cubelets[RI][MID][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][6] = cubelets[RI][BO][FR];
	cubelets[MID][MID][FR]->children[Z_AXIS][7] = cubelets[MID][BO][FR];
	
	//update back face references
	cubelets[MID][MID][BA]->children[Z_AXIS][0] = cubelets[LE][BO][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][1] = cubelets[LE][MID][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][2] = cubelets[LE][TO][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][3] = cubelets[MID][TO][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][4] = cubelets[RI][TO][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][5] = cubelets[RI][MID][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][6] = cubelets[RI][BO][BA];
	cubelets[MID][MID][BA]->children[Z_AXIS][7] = cubelets[MID][BO][BA];

	//update top face references
	cubelets[MID][TO][MID]->children[Y_AXIS][0] = cubelets[LE][TO][FR];
	cubelets[MID][TO][MID]->children[Y_AXIS][1] = cubelets[LE][TO][MID];
	cubelets[MID][TO][MID]->children[Y_AXIS][2] = cubelets[LE][TO][BA];
	cubelets[MID][TO][MID]->children[Y_AXIS][3] = cubelets[MID][TO][BA];
	cubelets[MID][TO][MID]->children[Y_AXIS][4] = cubelets[RI][TO][BA];
	cubelets[MID][TO][MID]->children[Y_AXIS][5] = cubelets[RI][TO][MID];
	cubelets[MID][TO][MID]->children[Y_AXIS][6] = cubelets[RI][TO][FR];
	cubelets[MID][TO][MID]->children[Y_AXIS][7] = cubelets[MID][TO][FR];

	//update bottom face references
	cubelets[MID][BO][MID]->children[Y_AXIS][0] = cubelets[LE][BO][FR];
	cubelets[MID][BO][MID]->children[Y_AXIS][1] = cubelets[LE][BO][MID];
	cubelets[MID][BO][MID]->children[Y_AXIS][2] = cubelets[LE][BO][BA];
	cubelets[MID][BO][MID]->children[Y_AXIS][3] = cubelets[MID][BO][BA];
	cubelets[MID][BO][MID]->children[Y_AXIS][4] = cubelets[RI][BO][BA];
	cubelets[MID][BO][MID]->children[Y_AXIS][5] = cubelets[RI][BO][MID];
	cubelets[MID][BO][MID]->children[Y_AXIS][6] = cubelets[RI][BO][FR];
	cubelets[MID][BO][MID]->children[Y_AXIS][7] = cubelets[MID][BO][FR];

	//update right face references
	cubelets[RI][MID][MID]->children[X_AXIS][0] = cubelets[RI][BO][FR];
	cubelets[RI][MID][MID]->children[X_AXIS][1] = cubelets[RI][MID][FR];
	cubelets[RI][MID][MID]->children[X_AXIS][2] = cubelets[RI][TO][FR];
	cubelets[RI][MID][MID]->children[X_AXIS][3] = cubelets[RI][TO][MID];
	cubelets[RI][MID][MID]->children[X_AXIS][4] = cubelets[RI][TO][BA];
	cubelets[RI][MID][MID]->children[X_AXIS][5] = cubelets[RI][MID][BA];
	cubelets[RI][MID][MID]->children[X_AXIS][6] = cubelets[RI][BO][BA];
	cubelets[RI][MID][MID]->children[X_AXIS][7] = cubelets[RI][BO][MID];

	//update left face references
	cubelets[LE][MID][MID]->children[X_AXIS][0] = cubelets[LE][BO][FR];
	cubelets[LE][MID][MID]->children[X_AXIS][1] = cubelets[LE][MID][FR];
	cubelets[LE][MID][MID]->children[X_AXIS][2] = cubelets[LE][TO][FR];
	cubelets[LE][MID][MID]->children[X_AXIS][3] = cubelets[LE][TO][MID];
	cubelets[LE][MID][MID]->children[X_AXIS][4] = cubelets[LE][TO][BA];
	cubelets[LE][MID][MID]->children[X_AXIS][5] = cubelets[LE][MID][BA];
	cubelets[LE][MID][MID]->children[X_AXIS][6] = cubelets[LE][BO][BA];
	cubelets[LE][MID][MID]->children[X_AXIS][7] = cubelets[LE][BO][MID];

	//update middle slice references
	//rotations about X_AXIS
	cubelets[MID][MID][MID]->children[X_AXIS][0] = cubelets[MID][MID][FR];
	cubelets[MID][MID][MID]->children[X_AXIS][1] = cubelets[MID][BO][FR];
	cubelets[MID][MID][MID]->children[X_AXIS][2] = cubelets[MID][BO][MID];
	cubelets[MID][MID][MID]->children[X_AXIS][3] = cubelets[MID][BO][BA];
	cubelets[MID][MID][MID]->children[X_AXIS][4] = cubelets[MID][MID][BA];
	cubelets[MID][MID][MID]->children[X_AXIS][5] = cubelets[MID][TO][BA];
	cubelets[MID][MID][MID]->children[X_AXIS][6] = cubelets[MID][TO][MID];
	cubelets[MID][MID][MID]->children[X_AXIS][7] = cubelets[MID][TO][FR];

	//rotations about Y_AXIS
	cubelets[MID][MID][MID]->children[Y_AXIS][0] = cubelets[MID][MID][FR];
	cubelets[MID][MID][MID]->children[Y_AXIS][1] = cubelets[LE][MID][FR];
	cubelets[MID][MID][MID]->children[Y_AXIS][2] = cubelets[LE][MID][MID];
	cubelets[MID][MID][MID]->children[Y_AXIS][3] = cubelets[LE][MID][BA];
	cubelets[MID][MID][MID]->children[Y_AXIS][4] = cubelets[MID][MID][BA];
	cubelets[MID][MID][MID]->children[Y_AXIS][5] = cubelets[RI][MID][BA];
	cubelets[MID][MID][MID]->children[Y_AXIS][6] = cubelets[RI][MID][MID];
	cubelets[MID][MID][MID]->children[Y_AXIS][7] = cubelets[RI][MID][FR];

	//rotations about Z_AXIS
	cubelets[MID][MID][MID]->children[Z_AXIS][0] = cubelets[LE][MID][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][1] = cubelets[LE][TO][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][2] = cubelets[MID][TO][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][3] = cubelets[RI][TO][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][4] = cubelets[RI][MID][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][5] = cubelets[RI][BO][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][6] = cubelets[MID][BO][MID];
	cubelets[MID][MID][MID]->children[Z_AXIS][7] = cubelets[LE][BO][MID];

}


/** trackBall()
 *
 * This function handles the virtual trackball stuff.  Thanks to Dr. Zoe Wood
 * for providing this code.
 */
void trackBall() {
	float startx_i, starty_i, endx_i, endy_i;

	//first set to image coordinates
	startx_i = p2i_x(mouseX);
	starty_i = p2i_y(mouseY);
	endx_i = p2i_x(endX);
	endy_i = p2i_y(endY);

	//first project the start  to the unit sphere
	float r = 1.0 - startx_i*startx_i - starty_i*starty_i;
	if (r > 0) {
		v1->x = startx_i; v1->y = starty_i; v1->z = sqrt(r);
	} else { //point outside of ball
		float d = sqrt(startx_i*startx_i + starty_i*starty_i);
		//assert(d != 0);
		v1->x = startx_i/d; v1->y = starty_i/d; v1->z = 0.0;
	}

	//then project the end to the unit sphere
	r = 1.0 - endx_i*endx_i - endy_i*endy_i;
	if (r > 0) {
		v2->x = endx_i; v2->y = endy_i; v2->z = sqrt(r);
	} else { //point outside of ball
		float d = sqrt(endx_i*endx_i + endy_i*endy_i);
		//assert(d != 0);
		v2->x = endx_i/d; v2->y = endy_i/d; v2->z = 0.0;
	}

	//compute the axis of rotation
	axis = cross_product(v1,v2);
	//compute the angle of rotation
	angle_of_rotation = 180.0/3.14*acos(dot(v1, v2));
	if (angle_of_rotation < -180.0 || angle_of_rotation > 180)
		angle_of_rotation = 0.0;

	mouseX = endX;
	mouseY = endY;
}
/** keyboard()
 *
 * This callback will be executed whenver a key is pressed
 */
void keyboard(unsigned char key, int x, int y )
{
	if (animate == false) {
		switch( key ) {
		case 'q': case 'Q' :
			exit( EXIT_SUCCESS );
			break;
		case 'a':
			printf("toggling axis\n");
			if (draw_axis == true) {
				draw_axis = false;
			} else {
				draw_axis = true;
			}
			glutPostRedisplay();
			break;
		case 'r':
			printf("RESET!\n");
			//load identity matrix into our transformation matrix
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glGetFloatv(GL_MODELVIEW_MATRIX,*M);
			initCube();
			glutPostRedisplay();
			break;
		}
	}
}


/** reshape()
 *
 * This callback method is executed whenver the window is resized
 */
void reshape(int w, int h) {
  GW = w;
  GH = h;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho( -(float)w/h, (float)w/h, -1, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glViewport(0, 0, w, h);
}


//***************************************************************************
//UTILITY FUNCTIONS
//***************************************************************************
/** dot()
 *
 * This method calculates the dot product of two vectors
 */
float dot(struct vector *a, struct vector *b) {
	return a->x*b->x + a->y*b->y + a->z*b->z;
}

/** cross_product
 *
 * This method calculates the cross product of two vectors
 */
struct vector *cross_product(struct vector *a, struct vector *b) {
	struct vector *v = (struct vector *)malloc(sizeof(struct vector));

	v->posx = a->posx;
	v->posy = a->posy;
	v->posz = a->posz;

	v->x = a->y*b->z - a->z*b->y;
	v->y = a->z*b->x - a->x*b->z;
	v->z = a->x*b->y - a->y*b->x;

	return v;
}

/** p2w_x()
 *
 * Pixel->world mapping
 */
float p2w_x(int p_x) {
  float x_i = ( (float)p_x - ((GW-1.0)/2.0) )*2.0/GW;
  return(((float)GW/(float)GH)*x_i);
}

/** p2w_y()
 *
 * Pixel->world mapping
 */
float p2w_y(int p_y) {
  return( ( (float)p_y - ((GH-1.0)/2.0) )*2.0/GH);
}

/** p2i_x()
 *
 * Pixel->image mapping
 */
float p2i_x(int p_x) {
  float x_i = ( (float)p_x - ((GW-1.0)/2.0) )*2.0/GW;
  return(x_i);
}

/** p2i_y()
 *
 * Pixel->image mapping
 */
float p2i_y(int p_y) {
  return( ( (float)p_y - ((GH-1.0)/2.0) )*2.0/GH);
}
/** setColor()
 *
 * This helper method will call glColor3f with the appropriate values.
 * It takes a single color argument (RED, GREEN, etc)
 */
void setColor(int color) {
	switch (color) {
	case RED:
		//printf("red");
		glColor3f(1,0,0);
	break;
	case GREEN:
		glColor3f(0,1,0);
	break;
	case BLUE:
		glColor3f(0,0,1);
	break;
	case WHITE:
		glColor3f(1,1,1);
	break;
	case YELLOW:
		glColor3f(1,1,0);
	break;
	case ORANGE:
		glColor3f(1,.5,0);
	break;
	case BLACK:
		glColor3f(0,0,0);
	break;
	case GREY:
		glColor3f(.7,.7,.7);
	break;
	}
}
/** initialize_vectors()
 *
 * This helper function is used to initialize our rotation vectors
 */
void initialize_vectors() {
	//free up our previously malloc'd vectors
	free(v1);
	free(v2);
	free(axis);
	//malloc new vectors
	v1 = (struct vector *)malloc(sizeof(struct vector));
	v2 = (struct vector *)malloc(sizeof(struct vector));
	axis = (struct vector *)malloc(sizeof(struct vector));

	//set vectors with default values
	v1->posx = 0.0;
	v1->posy = 0.0;
	v1->posz = 0.0;
	v2->posx = 0.0;
	v2->posy = 0.0;
	v2->posz = 0.0;
	axis->posx = 0.0;
	axis->posy = 0.0;
	axis->posz = 0.0;
	v1->x = 0.0;
	v1->y = 0.0;
	v1->z = 0.0;
	v2->x = 0.0;
	v2->y = 0.0;
	v2->z = 0.0;
	axis->x = 0.0;
	axis->y = 0.0;
	axis->z = 0.0;
}
/** setFaceColor
 *
 * This helper function will set a single face of the cube to a single
 * color.  It does this by iterating through all cubelets on the
 * specified face, and setting the cubelet->faceColors[] array 
 * appropriately.
 */
void setFaceColor(int face, int color) {
	int i,j;
	switch (face) {
	case FRONT: //front
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[i][j][FR]->faceColors[face] = color;
	break;
	case BACK: //back
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[i][j][BA]->faceColors[face] = color;
	break;
	case TOP: //top
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[i][TO][j]->faceColors[face] = color;
	break;
	case BOTTOM: //bottom
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[i][BO][j]->faceColors[face] = color;
	break;
	case RIGHT: //right
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[RI][i][j]->faceColors[face] = color;
	break;
	case LEFT: //left
		for (i=0;i<3;i++)
			for (j=0;j<3;j++) 
				cubelets[LE][i][j]->faceColors[face] = color;
	break;
	}

}


/** initCenterPositions()
 *
 * This helper method will initialize the centerPositions[][][] array
 * with vertices that represent cubelet center positions.
 */
void initCenterPositions() {
	struct vertex *v;

	for (int i=0;i<3;i++)
		for (int j=0;j<3;j++)
			for (int k=0;k<3;k++) {
				v = (struct vertex *)malloc(sizeof(struct vertex));
				v->x = -C_W + (i*C_W);
				v->y = -C_W + (j*C_W);
				v->z = C_W + (k*-C_W);
				centerPositions[i][j][k] = v;
			}
}

//*******************************************************************************
//*******************************************************************************
//MAIN FUNCTION
//*******************************************************************************
//*******************************************************************************
/** main()
 */
int main(int argc, char** argv) {

	glutInit(&argc, argv);
	glutInitWindowSize(300, 300);
	glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE);
	glutCreateWindow("Rubik's Cube");

	glutIdleFunc(idle);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMouseFunc(mouse);
	glutMotionFunc(mouseMove);
	glutKeyboardFunc(keyboard);
	glClearColor(0.0, 0.0, 0.0, 0.0);

	//set up the viewing
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glMatrixMode(GL_MODELVIEW);
	glViewport(0, 0, 300, 300); //image to pixel

	//initialize globals
	GW = 300;
	GH = 300;

	initialize_vectors();	

	//initialize the array that holds the center vertices for all cubelets
	//in the cube
	initCenterPositions();

	//initialize the cube
	initCube();
	glutMainLoop();
}