#include <GL/glut.h>
#include <GL/glu.h>
#include <GL/gl.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>


GLfloat rotz=0.0;
GLfloat rotx=0.0;

int ingranaggi=8;
int verso=1;
int hits=0;
int light_on=1;
GLfloat speed=0.3;

#define INGRANAGGIO 1


GLfloat light0_position[] = { 1.0,1.0, 1.0, 0.0 };
GLfloat  ambientLight_on[] = { 0.5f, 0.5f, 0.5f, 1.0f};
GLfloat  ambientLight_off[] = { 0.0f, 0.0f, 0.0f, 1.0f};

void init_scene (void)
{
 int lati=80;
 double angle;
 double r;

 r= 60.0;
 glNewList(INGRANAGGIO,GL_COMPILE);
 
 glBegin(GL_QUAD_STRIP);
  for (int i = 0; i <= lati; i++) {
    angle = i * 2.0 * 3.14159 / lati;
    glNormal3f(cos(angle), 0.0 , -sin(angle));
    glVertex3f(r * cos(angle), 10 ,  -r * sin(angle));
    glVertex3f(r * cos(angle), -10 , -r * sin(angle));
  }
  glEnd();

  glBegin(GL_TRIANGLE_FAN);
         glNormal3f(0, 1.0 , 0);
         glVertex3f(0.0, 10.0, 0.0); // origine
         for(int i = 0; i <= (lati);i++) {
	    angle = i * 2.0 * 3.14159 / lati;
	    glNormal3f(0, 1.0 , 0);
            glVertex3f(r * cos(angle), 10, -r*sin(angle));
         }
  glEnd();

  
  glBegin(GL_TRIANGLE_FAN);
         glNormal3f(0.0, -1.0 ,0.0);
         glVertex3f(0.0, -10.0, 0.0); // origine
         for(int i = 0; i <= (lati);i++) {
	    angle = i * 2.0 * 3.14159 / lati;
	    glNormal3f(0, -1.0 , 0);
            glVertex3f(r * cos(angle), -10, +r*sin(angle));
         }
  glEnd();
  
  for (int i = 0; i < ingranaggi; i++) {
   glPushMatrix ();
   angle = i * 2.0 * 3.14159 / ingranaggi;
   glTranslatef(r * sin(angle) , 0 , r * cos(angle));
   glRotatef(angle*360/(2*3.14159),0,1.0,0);
   glScalef((2*3.14159*r)/(2*ingranaggi),20,20);
   glutSolidCube(1.0);
   glPopMatrix ();
  }

 glEndList();
}

void init (void)
{
   GLfloat light0_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
   GLfloat light0_diffuse[] = { 0.4, 0.4, 0.4, 1.0 };
   GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };

   GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
  
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight_on);

   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, 128);
   
   glEnable (GL_LIGHTING);
   glEnable(GL_CULL_FACE);
   glEnable(GL_DEPTH_TEST);

   glShadeModel(GL_SMOOTH);
   glEnable(GL_NORMALIZE);
   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, -200.0);
   glRotatef(rotx,1.0,0,0);

   glColor3f(0.6,0,0);
   glPushMatrix ();
   glTranslatef (-40.0, 0.0, -40.0);
   glRotatef(-rotz,0.0,1,0);
   glCallList(INGRANAGGIO);
   glPopMatrix ();
   
   glColor3f(0,0.6,0);
   glPushMatrix ();
   glTranslatef (55.0, 0.0, 55.00);
   glRotatef(rotz+360/(2*ingranaggi),0.0,1,0);
   glCallList(INGRANAGGIO);
   glPopMatrix ();

   glPopMatrix ();
   
   glutSwapBuffers();
}


void IdleFunc()
{
 
  if (verso)
   rotz+=speed;
  else 
   rotz-=speed;
	  
  glutPostRedisplay();
}

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(90.0, fAspect, 1.0, 500.0);
	
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

void KeyboardFunc(unsigned char k, int x, int y){
	switch(k){
		case 'q':
			rotx+=1;
			break;	
    
		case 'a':
			rotx-=1;
			break;	

        case 'l':
			light_on=!light_on;
			if (light_on==1)
				{
				 glEnable(GL_LIGHT0);
			     glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight_on);
				}
			else
				{
				 glDisable(GL_LIGHT0);
			     glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight_off);
				}
			break;

		case '+':
			speed+=0.1;
			break;	
    
		case '-':
			speed-=0.1;
			break;	

	}
	glutPostRedisplay();
}

#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(90.0, fAspect, 1.0, 500.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)
                verso=!verso;
        else
                glutSetWindowTitle("You clicked empty space!");
}


void MouseCallback(int button, int state, int x, int y)
        {
        if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
                ProcessSelection(x, y);
}

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; 
}
