// altalena su collina di equazione y=x*x+z*z
#include<GL/glut.h>
#include<GL/glu.h>
#include<GL/gl.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

typedef GLfloat GLTVector3[3];

GLfloat roty=0.0;
GLfloat rotx=0.0;
int enable_animation=0;
GLfloat angle=0.0;
GLint hits;
float alpha=0.01;

#define COLLINA 1
#define CATENA 2

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 vertice1,vertice2,vertice3;
 GLTVector3 vNormal;
 int intervalli_x=100;
 int intervalli_z=100;
 int dim_griglia=1;
 int x_count;
 float x,z;

 glNewList(COLLINA,GL_COMPILE);
   for (int z_count = 0; z_count < intervalli_z; z_count+=dim_griglia) {
	z=dim_griglia*z_count-(dim_griglia*intervalli_z)/2;
	glBegin(GL_QUAD_STRIP);
	x_count=0;
     for (int x_count = 0; x_count <= intervalli_x; x_count+=dim_griglia) {
	  x=dim_griglia*x_count-(dim_griglia*intervalli_x)/2;
	  vertice1[0]=x;             vertice1[1]= -alpha*(x*x+z*z);   vertice1[2]=z;
	  vertice2[0]=x;             vertice2[1]= -alpha*(x*x+(z+dim_griglia)*(z+dim_griglia));   vertice2[2]=z+dim_griglia;
	  vertice3[0]=x+dim_griglia; vertice3[1]= -alpha*((x+dim_griglia)*(x+dim_griglia) +z*z);   vertice3[2]=z;
	  gltGetNormalVector(vertice1, vertice2, vertice3, vNormal);
	  glNormal3fv(vNormal);
      glVertex3fv(  vertice1 );
      glVertex3fv(  vertice2 );
	}
    glEnd();
  }
 glEndList();

 glNewList(CATENA,GL_COMPILE);
  glPushMatrix();
  glColor3f(0.0,0.9,0.0);
  for (int i=0; i<10; i++)
  {  
    glTranslatef (0.0, -3.0, 0.0); 
    glutSolidTorus(0.5,2.0,30,30);
	glRotatef(90,0.0,1.0,0.0);
  }
  glPopMatrix(); 
 glEndList();
}

void Keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 'w':
         rotx-=1.0;
         break;
	  case 's':
         rotx+=1.0;
		 break;
	  case 'a':
         roty-=1.0;
         break;
	  case 'd':
         roty+=1.0;
         break;
      case '+':
         glDeleteLists(CATENA,1);
		 glDeleteLists(COLLINA,1);
         alpha-=0.0001;
		 init_scene();
         break;
	  case '-':
         glDeleteLists(CATENA,1);
		 glDeleteLists(COLLINA,1);
         alpha+=0.0001;
		 init_scene();
         break;
   }
   glutPostRedisplay();
}


GLfloat light0_position[] = { 80.0,80.0,80.0, 1.0 };
void init (void)
{
   GLfloat light0_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
   GLfloat light0_diffuse[] = { .8f, 0.8f, 0.8f, 1.0f };
   GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat specref[] = { 0.5f, 0.5f, 0.5f, 1.0f };
   GLfloat  ambientLightON[] = { 0.1f, 0.1f, 0.1f, 1.0f};
  
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLightON);

   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, light0_specular);
   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(1.0, 1.0, 1.0, 0.0);

   init_scene();
}

void display (void)
{
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
   glInitNames();
   glPushName(0);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glPushMatrix ();
   glTranslatef (0.0, 0.0, -220.0); 
   glRotatef(rotx,1.0,0.0,0.0);
   glRotatef(roty,0.0,1.0,0.0);

   glLightfv (GL_LIGHT0, GL_POSITION, light0_position);
   
   glColor3f(0.9,0.0,0.0);
   //glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
   glPushMatrix();
    glLoadName(COLLINA);
	glDisable(GL_CULL_FACE);
    glCallList(COLLINA);
	glEnable(GL_CULL_FACE);
	 glColor3f(0.0,0.0,0.0);
	glPushMatrix();
	 glTranslatef (-15.0, 22.0, 0.0); 
     glScalef(4.0, 50.0, 4.0);
	 glutSolidCube( 1.0 );
	glPopMatrix(); 
    glPushMatrix();
	 glTranslatef (+15.0, 22.0, 0.0); 
     glScalef(4.0, 50.0, 4.0);
	 glutSolidCube( 1.0 );
	glPopMatrix(); 
   glPopMatrix(); 

    glPushMatrix();
	glLoadName(CATENA);
    glTranslatef (0.0, 47.0, 0.0);
    glRotatef(((3.14/4)*sin(angle)*180)/3.14,1.0,0.0,0.0);
    glPushMatrix();
     glTranslatef (-8.0, 0.0, 0.0);
     glCallList(CATENA);
	glPopMatrix();
	glPushMatrix();
     glTranslatef (8.0, 0.0, 0.0);
     glCallList(CATENA);
	glPopMatrix();
	glPushMatrix();
	 glTranslatef (0.0, -32.0, 0.0); 
	 glPushMatrix();
      glScalef(16.0, 2.0, 5.0);
	  glColor3f(0.0,0.0,0.9);
	  glutSolidCube( 1.0 );
	 glPopMatrix();
	 glColor3f(0.9,0.9,0.0);
	 glTranslatef (0.0, 1.0, 0.0);
	 glRotatef(-90,1.0,0.0,0.0);
	 glutSolidCone(2.5,8,30,30);
	glPopMatrix();
	glPopMatrix();

	glPushMatrix();
	 glBegin(GL_LINES);
	  glVertex3f(-15.0, 46.0, 0.0);
	  glVertex3f(15.0, 46.0, 0.0);
     glEnd();
	glPopMatrix();
   glPopMatrix();
    
   glutSwapBuffers();
}

#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, 1480);
        display();

        // Restore the projection matrix
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity ();

        hits = glRenderMode(GL_RENDER);

        if (hits >= 1)
		 enable_animation=!enable_animation;    
        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);
}

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(45.0f, fAspect, 1.0, 1480);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

void IdleFunc()
{
  if (enable_animation)
   angle+=0.04;
   
  glutPostRedisplay(); 
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutInitWindowSize (800, 800);
   glutCreateWindow (argv[0]);
   init();
   glutReshapeFunc (reshape);
   glutDisplayFunc(display);
   glutMouseFunc(MouseCallback);
   glutIdleFunc(IdleFunc);
   glutKeyboardFunc(Keyboard);
   glutMainLoop();
   return 0; 
}
