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

GLfloat angle;

static void _InitMaterial(void)
{
  /* define material properties */
  GLfloat material_ambient[] = {0.25f, 0.25f, 0.25f};
  GLfloat material_diffuse[] = {0.90f, 0.90f, 0.90f};
  GLfloat material_specular[] = {0.90f, 0.90f, 0.90f};
  GLfloat material_shininess = 25.0f;

  /* load material properties */
  glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse);
  glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular);
  glMaterialf(GL_FRONT, GL_SHININESS, material_shininess);
}

static void _InitLights(void)
{
  /* define light properties */
  GLfloat light0_diffuse[] = {1.0, 0.0, 0.0, 1.0};
  GLfloat light0_position[] = {-1.0, 0.0, 1.0, 0.0};
  GLfloat light1_diffuse[] = {0.0, 0.0, 1.0, 1.0};
  GLfloat light1_position[] = {1.0, 0.0, 1.0, 0.0};

  /* enable lights 0 and 1*/
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHT1);

  /* load light properties */
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
  glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
  glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
}

static void _Init(void)
{  
  /* set the clear colour (R, G, B, A) */
  glClearColor(0.0, 0.0, 0.0, 1.0f);

  /* enable depth testing */
  glEnable(GL_DEPTH_TEST);

  /* enabled lighting */
  glEnable(GL_LIGHTING);

  /* initialise materials */
  _InitMaterial();

  /* initialise lights */
  _InitLights();

  /* position and orient the camera */ 
  gluLookAt(0.0, 0.0, 10.0, /* eye point */
	    0.0, 0.0, 0.0,  /* reference point */
	    0.0, 1.0, 0.0); /* up vector */
}

static void _Draw(void)
{
  /* clear the color and depth buffers */
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  /* preserve modelview matrix */
  glPushMatrix();

  /* rotate by angle degrees around the axis y = x */
  glRotatef(angle, 1.0f, 1.0f, 0.0f);

  /* render a solid teapot */
  glutSolidTeapot(2.0f);

  /* return to previous modelview matrix */
  glPopMatrix();

  /* swap front and back buffers */
  glutSwapBuffers();
}

static void _Reshape(int width, int height)
{
  /* set the viewport to the window width and height */
  glViewport(0, 0, width, height);

  /* load a projection matrix that matches the window aspect ratio */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0, (double)width/(double)height, 1.0, 100.0);

  /* return to modelview matrix mode*/
  glMatrixMode(GL_MODELVIEW);
}

static void _Key(unsigned char key, int x, int y)
{
  switch (key)
  {
  case 27: /* escape key */
    exit(0);
    break;
  }
}

static void _Timer(int value)
{
  /* increment angle */
  angle++;

  /* send redisplay event */
  glutPostRedisplay();

  /* call this function again in 10 milliseconds */
  glutTimerFunc(10, _Timer, 0);
}

int main(int argc, char *argv[])
{
  glutInitWindowSize(640, 480);                   /* set window size */
  glutInitDisplayMode(GLUT_RGBA | 
		      GLUT_DOUBLE | 
		      GLUT_DEPTH);                /* set display mode */
  glutCreateWindow("OpenGL workshop program");    /* create a window */

  glutReshapeFunc(_Reshape);   /* register callback for window resize */
  glutDisplayFunc(_Draw);      /* register callback for window redraw */
  glutKeyboardFunc(_Key);      /* register callback for keyboard input */

  glutTimerFunc(10, _Timer, 0); /* register callback for a timer */

  _Init(); /* set initial OpenGL state and initialise data */

  glutMainLoop(); /* start the main event loop */

  return 1;
}
