// pallina che rimbalza da scala
#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];

GLfloat roty=0.0;
GLfloat rotx=0.0;

#define GRADINO 1
#define CONO 2
#define NUM 10
#define MAX_LARGHEZZA 4

GLfloat l=30.0;
int gradino_larghezza=1;
double x_pallina=0;
double y_pallina_min=60;
double x_pallina_min=-100;
int rimbalzo=0;
int oggetto=1;

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_gradino(void)
{
 GLTVector3 vNormal;

 glNewList(GRADINO,GL_COMPILE);
 glColor3f(0.4,0,0);
 GLTVector3 vPoints[6] =  {{  -5.0f,  0.0f,  0}, 
							{ 0.0f,  -8.0f,  0}, 
							{ 5.0f,  0.0f,  0}, 
							{ -5.0f,  0.0f, -gradino_larghezza*l},
                            { 0.0f,  -8.0f, -gradino_larghezza*l}, 
							{ 5.0f,  0.0f, -gradino_larghezza*l}};
 glBegin(GL_QUADS);
 // 1
 gltGetNormalVector(vPoints[4], vPoints[5], vPoints[2], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[1]);

 // 2
 gltGetNormalVector(vPoints[3], vPoints[4], vPoints[1], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[0]);

 // 3
 gltGetNormalVector(vPoints[5], vPoints[3], vPoints[0], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[0]);
 glVertex3fv(vPoints[2]);
 glEnd();

 glBegin(GL_TRIANGLES);
 // FRONT
 gltGetNormalVector(vPoints[3], vPoints[5], vPoints[4], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[4]);

 // BACK
 gltGetNormalVector(vPoints[1], vPoints[2], vPoints[0], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[0]);
 glEnd();
 glEndList();
}

void init_cono(void)
{
 glNewList(CONO,GL_COMPILE);
 float res=50;
 float raggio=5.0;
 GLfloat d = 2*3.14159265/ res ;
 GLfloat a = 0;
 glBegin(GL_TRIANGLE_FAN);
 glNormal3f(0,0,-1);
 glVertex3f(0.0, 0.0, 0.0);
 for (int i = 0 ; i <= (res) ; i++ ){
  glNormal3f(0,0,-1);
  glVertex3f(raggio*cos(a), -raggio*sin(a), 0.0);
  a += d ; 
 }
 glEnd() ;
 a = 0.0  ;
 glBegin(GL_TRIANGLE_FAN);
  glVertex3f(0.0, 0.0, raggio);
  for (int i = 0 ; i <= res ; i++ ){
   // essendo altezza=raggio del cono le normali si possono calcolare direttamente
   // tramite la seguente formula
   glNormal3f(raggio*cos(a), raggio*sin(a), raggio);
   glVertex3f(raggio*cos(a), raggio*sin(a), 0.0);
   a += d ;
   }
 glEnd() ; 
 glEndList();
}

GLfloat light0_position[] = { 0.0,-1.0, 1.0, 0.0 };
void init (void)
{
   GLfloat  ambientLight[] = { 0.3f, 0.3f, 0.3f, 1.0f};

   GLfloat light0_ambient[] = { .8, .8, .8, 1.0 };
   GLfloat light0_diffuse[] = { .8, .8, .8, 1.0};
   GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat specref[] = { 1.0,1.0, 1.0, 1.0  };
  
   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,120);
   
   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_gradino();
   init_cono();
}

void display (void)
{
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glPushMatrix ();
   glTranslatef (0.0, 20.0, -330.0); 
   glRotatef(rotx,1.0,0.0,0.0);
   glRotatef(roty,0.0,1.0,0.0);

   // parabola con concavità rivolta verso il basso
   // coefficiente "a" della parabola negativo
   double a=-0.2;
   if ((-100+x_pallina)>-80)
     {
	   rimbalzo+=1;
	   if (rimbalzo==10)
		{
         rimbalzo=0;
         x_pallina=0;
		 y_pallina_min=60;
         x_pallina_min=-100;
		}
	   else
	   {
	    x_pallina=0;
	    y_pallina_min=(4-rimbalzo)*20-20;
	    x_pallina_min=-100+rimbalzo*20;
	   }
     }

   for (int npallina=0; npallina<gradino_larghezza; npallina++)
   {
   glPushMatrix ();
     glTranslatef (x_pallina_min+x_pallina, y_pallina_min + a*x_pallina*x_pallina -(20*a+1)*x_pallina+20+5  , l/2-npallina*l);
     glColor3f(0,0.4,0);
	 if (oggetto)
	  glutSolidSphere(5,30,30);
	 else
	   {   
        glPushMatrix ();
		 glRotatef(90,1,0,0);
	     glTranslatef (0,0,1);
		 glCallList(CONO);
	    glPopMatrix ();
	   }
   glPopMatrix ();
   }

   for (int k=0;k<NUM;k++)
     {
	   glPushMatrix ();
        glTranslatef ((k-5)*20, (4-k)*20 , l);
	    glCallList(GRADINO);
       glPopMatrix ();
	 }

   glPopMatrix ();
   glutSwapBuffers();
}

void IdleFunc()
{
  x_pallina+=0.5; 
  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(45.0f, fAspect, 1.0, 825.0);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

void KeyboardFunc(unsigned char k, int x, int y){

	switch(k){
		case 'w':
			rotx+=2;;
			break;
        case 's':
			rotx-=2;;
			break;
		case 'a':
			roty+=2;
		    break;
		case 'd':
			roty-=2;
			break;
        case 'c':
			oggetto=!oggetto;
			break;
        case 'x':
			if (gradino_larghezza<MAX_LARGHEZZA)
			{
             glDeleteLists(GRADINO, 1);
			 gradino_larghezza+=1;
			 init_gradino();
			}
		    break;
		case 'z':
            if (gradino_larghezza>1)
			{
             glDeleteLists(GRADINO, 1);
			 gradino_larghezza-=1;
			 init_gradino();
		    }
			break;
	}
	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);

   glutKeyboardFunc(KeyboardFunc);
   glutIdleFunc(IdleFunc);
   glutMainLoop();
   return 0; 
}
