/**************************************************************************
 *                    simple glut framework SS2001                        *
 **************************************************************************
 * 2000 by GK                                                             *
 * 2001 modified by BR                                                    *
 **************************************************************************/

/* 
 * system includes
 */

#include <stdlib.h>
#include <stdio.h>


/*
 * local includes
 */

#include <GL/glut.h>


/*
 * global constants
 */

/* initial window extent */
#define WIN_SIZE_X 400
#define WIN_SIZE_Y 400

/* menu ids */
#define MODE_1       1
#define MODE_2       2
#define MODE_3       3


/*
 * global variables  
 */

/* current window extent */
int win_width;
int win_height;

/* mouse pointer location */
int mouse_x = WIN_SIZE_X / 2;
int mouse_y = WIN_SIZE_Y / 2;


/*
 * drawMyObjects
 *
 * called by display 
 *
 * renders application specific objects into the framebuffer
 */

void drawMyObjects (GLfloat x, GLfloat y, GLfloat z) {
  GLfloat knotVector[] = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
  GLfloat ctlPoints[][3]  = {{-1.0,-1.0,0.0}, 
			     {0.0, -1.0,0.0}, 
			     {0.0, 1.0,0.0}, 
			     {1.0, 1.0,0.0},
			     {1.0, 1.0, 0.0},
			     {1.0, 1.0, 0.0}}; 

  GLUnurbsObj *gluNurbsRenderer = gluNewNurbsRenderer();

  /* draw red frame */
  glColor3f(1.0,0.0,0.0);
  glBegin(GL_LINE_LOOP);
  glVertex3f(-1.9f,-1.9f, 0.0f);
  glVertex3f(-1.9f, 1.9f, 0.0f);
  glVertex3f( 1.9f, 1.9f, 0.0f);
  glVertex3f( 1.9f,-1.9f, 0.0f);
  glEnd();

  /* draw a NURBS curve */

  gluNurbsProperty(gluNurbsRenderer, GLU_SAMPLING_TOLERANCE, 25.0);
  /* maximum number of pixels per line tesselated line segment */

  gluNurbsProperty(gluNurbsRenderer, GLU_DISPLAY_MODE, GLU_FILL);
  /* the curve is displayed by a line strip */

  gluBeginCurve(gluNurbsRenderer);
  gluNurbsCurve(gluNurbsRenderer, 
		8, knotVector,     /* numOfKnots / knotVector */ 
		3,                 /* number of values between each vertex def. */
		(float *)ctlPoints,/* control points */
		4,                 /* order of the Nurbs curve */
		GL_MAP1_VERTEX_3); 
  gluEndCurve(gluNurbsRenderer);
 
  gluDeleteNurbsRenderer(gluNurbsRenderer);
}

/*
 * display
 *
 * this function is called by the glut-main loop to create
 * application specific window content.
 *
 * Keep in mind that render operations won't appear on the screen
 * until glutSwapBuffers is called. This is due to the 
 * double buffer mode in which each rendering operation 
 * applies to an invisible back buffer
 */

void display (void) {
  glClearColor (0.3f, 0.3f, 0.3f, 1.0f);  /* set background color   */
  glClear (GL_COLOR_BUFFER_BIT|           /* erase the color buffer */ 
           GL_DEPTH_BUFFER_BIT);          /* and z-buffer */

  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();

  glFrustum(-2.0, 2.0, -2.0, 2.0, 20.0, 24.0);
  glTranslated(0.0, 0.0, -22.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity ();
  drawMyObjects (0.0, 0.0, 0.0);
  glutSwapBuffers ();
}

/*
 * reshape
 *
 * called after altering the window's position / size 
 */

void reshape (int w, int h) {
  win_width  = w;
  win_height = h;

  glViewport (0, 0, w, h);
}

/*
 * myInit
 *
 * application specific preparation/setup tasks 
 */

void myInit (void) {

  /* general material settings */
  GLfloat light_ambient[]  = {1.0, 1.0, 1.0, 1.0};
  GLfloat light_diffuse[]  = {1.0, 1.0, 1.0, 1.0};
  GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0};

  /* light_position */
  GLfloat light_position[] = {0.0, 10.0, 0.0, 0.0};

  /* shading model of light 0 */
  glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular);
  glLightfv (GL_LIGHT0, GL_POSITION, light_position);

  /* setup some gl-flags */
  glFrontFace (GL_CW);       /* clockwise vertex sequence */
  glDisable (GL_LIGHTING);   /* disables GL lighting      */ 
  glDisable (GL_LIGHT0);
  glEnable (GL_AUTO_NORMAL); /* automatic generation of normal vectors
				for GLU and glut objects */
  glEnable (GL_NORMALIZE);   /* normal vectors are normalized before 
				use by lighting calculations */
  glDepthFunc (GL_LESS);     /* alternative: GL_LEQUAL */

  glEnable (GL_DEPTH_TEST);  /* use depth buffering for HSR */

  /* setup transformation matrices */
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();

  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
}

/*
 * some user interface related callback functions
 *
 * keyboard    - called on keyboard events
 * mouseButton - called on mouse button events
 * mouseMotion - called when mouse move events are 
 *               detected within the gl-window
 * menu        - popup actions
 */

void keyboard (unsigned char key, int x, int y) {
  if (key == 27)		/* ascii-code for escape */
    exit(0);
  else
    printf("key: %c\n", key);
}

void mouseButton (int button, int state, int x, int y) {
  if (state == GLUT_DOWN) {
    if (button == GLUT_LEFT_BUTTON)
      printf ("you pressed the left mousebutton at (%i/%i)\n", x, y);
  } else
    printf ("you released a mousebutton\n");
}

void mouseMotion (int x, int y) {
  printf ("current position: (%i/%i)\n", x, y);
  mouse_x = x;
  mouse_y = y;
  glutPostRedisplay ();
}

void menu (int state) {
  switch (state) {
  case MODE_1:
    printf ("you chose mode 1\n");
    break;
  case MODE_2:
    printf ("you chose mode 2\n");
    break;
  case MODE_3:
    printf ("you chose mode 3\n");
    break;
  default:			/* define your default action */ ;
  }
}


/*
 * InitialGLUT
 *
 * creates the GL-Window and adds the callback functions defined 
 * above to the glut-kernel
 */

void initialGLUT (void) {
  int  menu_idx;
  char title[] = "my window";

  win_width  = WIN_SIZE_X;
  win_height = WIN_SIZE_Y;

  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
  glutInitWindowSize (win_width, win_height);
  glutInitWindowPosition (0, 0);
  glutCreateWindow (title);

  myInit ();

  glutDisplayFunc (display);
  glutReshapeFunc (reshape);
  glutKeyboardFunc (keyboard);
  glutMouseFunc (mouseButton);
  glutMotionFunc (mouseMotion);

  menu_idx = glutCreateMenu (menu);
  glutSetMenu (menu_idx);
  glutAddMenuEntry ("mode 1", MODE_1);
  glutAddMenuEntry ("------", -1);
  glutAddMenuEntry ("mode 2", MODE_2);
  glutAddMenuEntry ("mode 3", MODE_3);
  glutAttachMenu (GLUT_RIGHT_BUTTON);
}


/*
 * the main function
 */

void main (int argc, char **argv) {
  glutInit (&argc, argv);
  initialGLUT ();
  glutMainLoop ();
}
