//#include <windows.h>
//#include <glut.h>
#include<GL/glut.h>
#include<GL/glu.h>
#include<GL/gl.h>

#include <stdlib.h>
#include <stdio.h>
#include <math.h>


typedef struct {
   double x,y,z;
} XYZ;
typedef struct {
   double r,g,b;
} COLOUR;

#define TWOPI 6.283185307179586476925287
#define PI    3.141592653589793238462643

XYZ Eval(double,double);
void Normalise(XYZ *);
XYZ CalcNormal(XYZ,XYZ,XYZ);

GLfloat  ambientLight[] = { 0.6f, 0.6f, 0.6f, 1.0f};
GLfloat light0_position[] = { 1.0,1.0, 1.0, 0.0 };

#define Nu 200
#define Nv 200
#define CYCLES 1
#define R1 0.5
#define R2 0.2
#define PERIODLENGTH 2

GLfloat roty=0.0;
GLfloat rotx=0.0;

XYZ q[4],n[4];

void drawSurface()
{
   int i,j,k;
   double u,v,du,dv;
   
   COLOUR colour = {0.0,0.0,1.0};
   
   du = CYCLES * TWOPI / (double)Nu;
   dv = TWOPI / (double)Nv;

   glColor3f(0.5,0.0,0.0);
   glBegin(GL_QUADS);
	  for (i=0;i<Nu;i++) {
        u = i * du;
        for (j=0;j<Nv;j++) {
         v = j * dv;
         q[0] = Eval(u,v);
         n[0] = CalcNormal(q[0],Eval(u+du/10,v),Eval(u,v+dv/10));
         q[1] = Eval(u+du,v);
         n[1] = CalcNormal(q[1],Eval(u+du+du/10,v),Eval(u+du,v+dv/10));
         q[2] = Eval(u+du,v+dv);
         n[2] = CalcNormal(q[2],Eval(u+du+du/10,v+dv),Eval(u+du,v+dv+dv/10));
         q[3] = Eval(u,v+dv);
         n[3] = CalcNormal(q[3],Eval(u+du/10,v+dv),Eval(u,v+dv+dv/10));

		 glNormal3f(n[0].x,n[0].y,n[0].z);
		 glVertex3f(q[0].x,q[0].y,q[0].z);

		 glNormal3f(n[1].x,n[1].y,n[1].z);
		 glVertex3f(q[1].x,q[1].y,q[1].z);

		 glNormal3f(n[2].x,n[2].y,n[2].z);
		 glVertex3f(q[2].x,q[2].y,q[2].z);

		 glNormal3f(n[3].x,n[3].y,n[3].z);
		 glVertex3f(q[3].x,q[3].y,q[3].z);
	
     
         //printf("f4n ");
         //for (k=0;k<4;k++)
         //   printf("%g %g %g \n",q[k].x,q[k].y,q[k].z);
         //for (k=0;k<4;k++)
         //   printf("%g %g %g \n",n[k].x,n[k].y,n[k].z);
         //printf("%g %g %g\n",colour.r,colour.g,colour.b);
     
      }
     }  
	glEnd();
}

void init_OpenGL()
{
   GLfloat light0_ambient[] = { .4, .4, .4, 1.0 };
   GLfloat light0_diffuse[] = { .4, .4, .4, 1.0};
   GLfloat light0_specular[] = { 0.7, 0.7, 0.7, 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);

   glShadeModel(GL_SMOOTH);
   glEnable(GL_NORMALIZE);

   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
   glClearColor(0.9, 0.9, 0.9, 0.0);
}


void display (void)
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();
      glTranslatef (0.0, 0.0, -10.0);
	  glRotatef(rotx,1.0,0.0,0);
      glRotatef(roty,0.0,1.0,0);
	 
    drawSurface();

   glPopMatrix();

   glutSwapBuffers();
}

XYZ Eval(double u,double v)
{
   XYZ q;
   
   /*
   q.x = (4*R1 + 4*R2 * cos(v)) * cos(u);
   q.y = (4*R1 + 4*R2 * cos(v)) * sin(u);
   q.z = 4*R2 * sin(v);
   */
   
   q.x = 4*R1 * cos(u) * sin(v);
   q.y = 2*R1 * sin(u) * sin(v);
   q.z = 2*R1 * cos(v);
   

   return(q);
}

XYZ CalcNormal(XYZ p,XYZ p1,XYZ p2)
{
   XYZ n,pa,pb;

   pa.x = p1.x - p.x;
   pa.y = p1.y - p.y;
   pa.z = p1.z - p.z;
   pb.x = p2.x - p.x;
   pb.y = p2.y - p.y;
   pb.z = p2.z - p.z;
   Normalise(&pa);
   Normalise(&pb);

   n.x = pa.y * pb.z - pa.z * pb.y;
   n.y = pa.z * pb.x - pa.x * pb.z;
   n.z = pa.x * pb.y - pa.y * pb.x;
   Normalise(&n);

   return(n);
}

void Normalise(XYZ *p)
{
   double length;

   length = sqrt(p->x * p->x + p->y * p->y + p->z * p->z);
   if (length != 0) {
      p->x /= length;
      p->y /= length;
      p->z /= length;
   } else {
      p->x = 0;
      p->y = 0;
      p->z = 0;
   }
}

void reshape(int w, int h)
{
   // Calculate aspect ratio of the window
   float fAspect = (GLfloat)w/(GLfloat)h;

   // Prevent a divide by zero
	if(h == 0)
		h = 1;

	// Set Viewport to window dimensions
    glViewport(0, 0, w, h);

	// Set the perspective coordinate system
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f, fAspect, 1.0, 625.0);

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();

}

void Keyboard(unsigned char key, int x, int y)
{
   switch (key) {

      case 'a':
         rotx+=1.0;
         break;
	  case 'z':
         rotx-=1.0;
         break;
      case 'c':
         roty+=1.0;
         break;
	  case 'v':
         roty-=1.0;
         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_OpenGL();
   glutReshapeFunc (reshape);
   glutDisplayFunc(display);
   glutKeyboardFunc(Keyboard);

   glutMainLoop();
   return 0; 
}
