#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;

GLfloat rad=0.0;
int lati=8;

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 disegna_tronco (void)
{
 GLTVector3 vNormal;

 glBegin(GL_QUADS);

 GLTVector3 vPoints[6] =  {{  0.0f,  0.0f,  -2.0f}, 
							{ 2*sin(2*3.14/3),  0.0f,  -2*cos(2*3.14/3)}, 
							{ 2*sin(4*3.14/3),  0.0f,  -2*cos(4*3.14/3)}, 
							{ 0.0f,  3.0f, rad-1.0f},
                            { 1.0*sin(2*3.14/3),  3.0f, rad-1.0*cos(2*3.14/3)}, 
							{ 1.0*sin(4*3.14/3),  3.0f, rad-1.0*cos(4*3.14/3)}};
 // 1
 gltGetNormalVector(vPoints[5], vPoints[2], vPoints[1], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[1]);

 // 2
 gltGetNormalVector(vPoints[4], vPoints[1], vPoints[0], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[4]);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[0]);

 // 3
 gltGetNormalVector(vPoints[3], vPoints[0], vPoints[2], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[0]);
 glVertex3fv(vPoints[2]);
 glEnd();

 glBegin(GL_TRIANGLES);
 // TOP
 gltGetNormalVector(vPoints[3], vPoints[5], vPoints[4], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[3]);
 glVertex3fv(vPoints[5]);
 glVertex3fv(vPoints[4]);
 

 // BOTTOM
 gltGetNormalVector(vPoints[1], vPoints[2], vPoints[0], vNormal);
 glNormal3fv(vNormal);
 glVertex3fv(vPoints[1]);
 glVertex3fv(vPoints[2]);
 glVertex3fv(vPoints[0]);
 glEnd();

}

void init (void)
{
   GLfloat  ambientLight[] = { 0.8f, 0.8f, 0.8f, 1.0f};

   GLfloat light0_ambient[] = { 0.6, 0.6, 0.6, 1.0 };
   GLfloat light0_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };
   GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };
   GLfloat light0_position[] = { 1.0,0.0, 1.0, 0.0 };
   GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };

   glEnable (GL_LIGHTING);
   glEnable(GL_CULL_FACE);
   glEnable(GL_DEPTH_TEST);
   glShadeModel(GL_SMOOTH);
  
   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, 64);
   
   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    
   GLfloat fogColor[4] = {0.6, 0.6, 0.6, 1.0};

   GLint fogMode = GL_LINEAR;
   glFogi (GL_FOG_MODE, fogMode);
   glFogfv (GL_FOG_COLOR, fogColor);
   glFogf (GL_FOG_DENSITY, 0.15);
   glHint (GL_FOG_HINT, GL_DONT_CARE);
   glFogf (GL_FOG_START, 0.2);
   glFogf (GL_FOG_END, 20.0);
   
   glClearColor(0.0, 0.0, 0.0, 0.0);
}

void display (void)
{
   double angle;
   double r=10;

   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -10.0);
   glRotatef(roty,0.0,1.0,0);
   glRotatef(rotx,1.0,0,0);
   glPushMatrix ();

   for (int i=0; i< lati ; i++)
   { 
    angle = i * 2.0 * 3.14 / lati;
	glPushMatrix ();
     glTranslatef(r * sin(angle), 0 , -r * cos(angle));
     glRotatef(-angle*360/(2*3.14),0,1.0,0);
	 glColor3f(0,0,0.4);
     disegna_tronco();
    
	 glTranslatef(0,4,rad);
	 glColor3f(0.3,0,0.0);
	 glutSolidSphere(1,60,60);
	glPopMatrix ();
   }

   glPopMatrix ();
   glutSwapBuffers();
}


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();

	GLfloat nRange = 12.0f;
	if (w <= h)
     glOrtho (-nRange, nRange, -nRange* h/w, nRange*h/w,- nRange* 2.0f, nRange*2.0f);
    else
     glOrtho (-nRange* w/h, nRange*w/h,- nRange, nRange,- nRange* 2.0f, nRange*2.0f);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

int nebbia=0;

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;
        case '+':
			rad+=0.1;
			break;
		case '-':
			rad-=0.1;
			break;
        case 'w':
			if (lati<12)
			lati+=1;
			break;
		case 's':
			if (lati>2)
			lati-=1;
			break;
        case 'n':
			if (nebbia)
				glDisable(GL_FOG);
			else
				glEnable(GL_FOG);
			nebbia=!nebbia;
			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);
   glutMainLoop();
   return 0; 
}
