#include <GL/glut.h>
#include <GL/glu.h>
#include <GL/gl.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

typedef GLfloat GLTVector3[3];

int rot_cl1=0;
int rot_cl2=0;

GLfloat roty_cl1=0.0;
GLfloat roty_cl2=0.0;
GLint hits;

GLfloat roty=0.0;
GLfloat rotx=0.0;

#define TRONCO 1
#define CL1 2
#define CL2 3
#define CLESSIDRA 4


GLfloat gltGetVectorLength(const GLTVector3 vVector){
    return (GLfloat)sqrt((vVector[0]*vVector[0]) +
                (vVector[1]*vVector[1]) + (vVector[2]*vVector[2]) );
}

void gltSubtractVectors(const GLTVector3 vFirst, const GLTVector3 vSecond, GLTVector3 vResult) {
    vResult[0] = vFirst[0] - vSecond[0];
    vResult[1] = vFirst[1] - vSecond[1];
    vResult[2] = vFirst[2] - vSecond[2];
}

void gltScaleVector(GLTVector3 vVector, const GLfloat fScale){
    vVector[0] *= fScale; vVector[1] *= fScale; vVector[2] *= fScale;
    }

void gltVectorCrossProduct(const GLTVector3 vU, const GLTVector3 vV, GLTVector3 vResult)
        {
        vResult[0] = vU[1]*vV[2] - vV[1]*vU[2];
        vResult[1] = -vU[0]*vV[2] + vV[0]*vU[2];
        vResult[2] = vU[0]*vV[1] - vV[0]*vU[1];
        }

void gltNormalizeVector(GLTVector3 vNormal)
    {
    GLfloat fLength = 1.0f / gltGetVectorLength(vNormal);
    gltScaleVector(vNormal, fLength);
    }

// Given three points on a plane in counter clockwise order, calculate the unit normal
void gltGetNormalVector(const GLTVector3 vP1, const GLTVector3 vP2, const GLTVector3 vP3, GLTVector3 vNormal)
    {
    GLTVector3 vV1, vV2;
    gltSubtractVectors(vP2, vP1, vV1);
    gltSubtractVectors(vP3, vP1, vV2);
    gltVectorCrossProduct(vV1, vV2, vNormal);
    gltNormalizeVector(vNormal);
    }


void init_scene (void)
{
 GLTVector3 vNormal;

 glNewList(TRONCO,GL_COMPILE);

 glBegin(GL_QUADS);

 GLTVector3 vPoints[8] =  {{ -0.5f,  1.0f,  0.5f}, 
			   { 0.5f,  1.0f,  0.5f}, 
			   { 0.5f,  1.0f, -0.5f}, 
			   { -0.5f,  1.0f, -0.5f},
			   { -1.0f, -1.0f,  1.0f},
			   { -1.0f, -1.0f, -1.0f}, 
			   { 1.0f, -1.0f, -1.0f}, 
			   { 1.0f, -1.0f,  1.0f}};
 // FRONT
 gltGetNormalVector(vPoints[7], vPoints[1], vPoints[0], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[7]);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[0]);
 // BACK
 gltGetNormalVector(vPoints[3], vPoints[2], vPoints[6], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[6]);

 // LEFT
 gltGetNormalVector(vPoints[0], vPoints[3], vPoints[5], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[0]);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[5]);

 // RIGHT
 gltGetNormalVector(vPoints[2], vPoints[1], vPoints[7], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[6]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[7]);

 // TOP
 gltGetNormalVector(vPoints[1], vPoints[2], vPoints[3], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[0]);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[3]);

 // BOTTOM
 gltGetNormalVector(vPoints[5], vPoints[6], vPoints[7], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[6]);
 glVertex3fv(vPoints[7]);
 glEnd();

 glEndList();

 glNewList(CLESSIDRA,GL_COMPILE);
 glCallList(TRONCO);
 glTranslatef (0.0, 2.0, 0.0);
 glRotatef(180,1.0,0.0,0.0);
 glCallList(TRONCO);
 glEndList();
}

void init (void)
{
   GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f};

   GLfloat light0_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat light0_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat light0_position[] = { 1.0,0.0, 0.3, 1.0 };
   GLfloat specref[] = { 0.7f, 0.7f, 0.7f, 1.0f };
  
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);

   glLightfv (GL_LIGHT0, GL_AMBIENT, light0_ambient);
   glLightfv (GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
   glLightfv (GL_LIGHT0, GL_SPECULAR, light0_specular);
   glLightfv (GL_LIGHT0, GL_POSITION, light0_position);
   glEnable(GL_LIGHT0);

   glEnable(GL_COLOR_MATERIAL);
   glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
   glMaterialfv(GL_FRONT, GL_SPECULAR,specref);
   glMateriali(GL_FRONT, GL_SHININESS,30);
   
   glEnable (GL_LIGHTING);
   glEnable(GL_CULL_FACE);
   glEnable(GL_DEPTH_TEST);
   //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

   glShadeModel(GL_SMOOTH); 
   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

   glClearColor(0.0, 0.0, 0.0, 0.0);

   init_scene();

}

void display (void)
{
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glPushMatrix ();
   glTranslatef (0.0, 0.0, -10.0); 
   glRotatef(roty,0.0,1.0,0.0);
   glRotatef(rotx,1.0,0.0,0.0);

   glInitNames();
   glPushName(0);

   glPushMatrix ();
   glColor3f(0.0f, 0.3f, 0.0f);
   glTranslatef (-2.0, 0.0 , 0.0);
   glRotatef(roty_cl1,0.0,1.0,0.0);
   glLoadName(CL1);
   glCallList(CLESSIDRA);
   glPopMatrix ();

   glPushMatrix ();
   glColor3f(0.3f, 0.0f, 0.0f);
   glTranslatef (2.0, 0.0, 0.0);
   glRotatef(roty_cl2,0.0,1.0,0.0);
   glLoadName(CL2);
   glCallList(CLESSIDRA);
   glPopMatrix ();
   
   glPopMatrix ();
   glutSwapBuffers();
}

void ProcessResults(GLuint *pSelectBuff)
 {
   int id;
   id = pSelectBuff[3];
  
   printf("id=%d",id);

   if (hits==1)
   {
   if (id==2)
	   rot_cl1=!rot_cl1;
   else
	   rot_cl2=!rot_cl2;
   }
   else
   {
     printf("buf[1]=%d buf[5]=%d\n",pSelectBuff[1],pSelectBuff[5]);
	 if (pSelectBuff[1]<pSelectBuff[5])
         rot_cl1=!rot_cl1;
	 else
         rot_cl2=!rot_cl2;
   }
 }

#define BUFFER_LENGTH 64
void ProcessSelection(int xPos, int yPos)
        {
        GLfloat fAspect;        // Screen aspect ratio
        GLuint selectBuff[BUFFER_LENGTH];
        GLint viewport[4];
        glSelectBuffer(BUFFER_LENGTH, selectBuff);
        glGetIntegerv(GL_VIEWPORT, viewport);
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glRenderMode(GL_SELECT);

        glLoadIdentity();
        gluPickMatrix(xPos, viewport[3] - yPos, 4,4, viewport);

        fAspect = (float)viewport[2] / (float)viewport[3];
        //gluPerspective(45.0f, fAspect, 1.0, 425.0);
        gluPerspective(75.0, fAspect, 1.0, 40.0);
        display();

        // Restore the projection matrix
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();

        // Go back to modelview for normal rendering
        glMatrixMode(GL_MODELVIEW);

        hits = glRenderMode(GL_RENDER);

        if(hits >= 1)
                ProcessResults(selectBuff);
        else
                glutSetWindowTitle("You clicked empty space!");
}

void IdleFunc()
{
  if (rot_cl1){
	  roty_cl1+=0.5;
	  if (roty_cl1>=360)
        roty_cl1=0;
	  
  }
	  
  if (rot_cl2){
	  roty_cl2+=0.5;
	  if (roty_cl2>=360)
        roty_cl2=0;
	  
  } 
  glutPostRedisplay();
}


void MouseCallback(int button, int state, int x, int y)
        {
        if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
                ProcessSelection(x, y);
}


void reshape(int w, int h)
{
   // Prevent a divide by zero
	if(h == 0)
		h = 1;

	// Set Viewport to window dimensions
    glViewport(0, 0, w, h);

	// Calculate aspect ratio of the window
	float fAspect = (GLfloat)w/(GLfloat)h;

	// Set the perspective coordinate system
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(75.0, (GLfloat) w/(GLfloat) h, 1.0, 40.0);
    //gluPerspective(45.0f, fAspect, 1.0, 425.0);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

void KeyboardFunc(unsigned char k, int x, int y){
	switch(k){
		
		case 'z':
			roty-=2;
			break;
		case 'x':
			roty+=2;
			break;
        	case 'c':
			rotx+=2;
			break;
		case 'v':
			rotx-=2;
	
			break;
	}
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutInitWindowSize (500, 500);
   glutCreateWindow (argv[0]);
   init ();
   glutReshapeFunc (reshape);
   glutDisplayFunc(display);
   glutKeyboardFunc(KeyboardFunc);
   glutMouseFunc(MouseCallback);
   glutIdleFunc(IdleFunc);
   glutMainLoop();
   return 0; 
}
