//	sample4.C
//
//	This example code extends sample1.C to allow the user
//      to toggle the colour of the cube; it also rotates the cube.
//
//	CS414 - The University of British Columbia
//	Dept. of Computer Science
//	Last update: January 24, 1997
//
//#include <stream.h> // &lt;stream.h&gt;
#include <stdlib.h> // &lt;stdlib.h&gt;
#include <GL/glut.h> // &lt;GL/glut.h&gt;

//  Prototypes for module global functions

static void initWin();
static void winDisplay();
static void winKbd( unsigned char, int, int );
static void winReshape( int, int );
static void winMouse( int, int, int, int );
static void setupCameraView( void );
static void drawScene( void );
static void drawCube( void );
static void checkHits( int, int );
static void processHits( int, GLuint * );

// Local Defines
#define	ROTATION_INC	13.0	// advances rotation by 13 degrees
#define CUBE_NAME       0
#define X_PICK_SIZE     5       // pick region is 5 x 5 pixels in size
#define Y_PICK_SIZE     5       


// Global variables
float Rotation = 0.0;
int   Active[1] = { 0 };


int main( int argc, char *argv[] )
{
  glutInit( &argc, argv );
  glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
  
  initWin();
  
  glutMainLoop();
  return 1;
}


static void initWin()
//  This function initializes the OpenGL window and registers the callback
//  routines
{
  glutCreateWindow( "Sample 4 OpenGL/GLUT" );
  
  glutDisplayFunc( winDisplay );	// Window callbacks
  glutKeyboardFunc( winKbd );
  glutReshapeFunc( winReshape );
  glutMouseFunc( winMouse );
  
  glClearColor( 0, 0, 0, 0 );		// Default background colour
  
  glEnable( GL_DEPTH_TEST );		// We have to enable depth buffering

  glMatrixMode( GL_MODELVIEW );
  glLoadIdentity();

  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
}                                       // End function initWin


static void winDisplay()
//  This callback function handles window redisplay requests
{
  setupCameraView();
  drawScene();

  glutSwapBuffers();
}                                        // End function winDisplay


static void winKbd( unsigned char key, int x, int y )
//  This callback function handles keyboard strokes
//
//  key:  Key pressed by user
//  x:    X-position of mouse
//  y:    Y-position of mouse
{
  switch( key ) {
   case 'q':			// Quit program
   case 'Q':
    exit( 0 );
   case 'r':			// Rotate the object
   case 'R':
    Rotation += ROTATION_INC;
    // check limits on the value of Rotation
    if( Rotation >= 360.0 ) Rotation -= 360.0;
    break;
   default:
    break;
  }
  // The next call is VERY important! Without it, the
  // program doesn't display anything
  glutPostRedisplay();
}                                       // End function winKbd


static void winReshape( int w, int h )
//  This callback function resets the viewport to cover the entire window
//
//  w:  Current width of window
//  h:  Current height of window
{
  glViewport( 0, 0, w, h );
}					// End function winReshape


static void winMouse( int button, int state, int x, int y )
//  This callback function handles mouse button events
//
//  button: The button with the altered state
//  state:  The up/down state of the mouse button
//  x, y:   The position of the mouse cursor
{
  int hits;

  if( state == GLUT_DOWN ) {

    switch( button ) {
    case GLUT_LEFT_BUTTON:

      checkHits( x, y );

      break;
    }
  }
}					// End function winMouse


#define	BUFSIZE		256     // select buffer will be 256 ints in size

static void checkHits( int x, int y )
//  This function sets up picking and calls the processing function
//
//  x, y:   The position at which picking is to be performed
{
  GLuint selectBuf[BUFSIZE];
  GLint hits;
  GLint viewport[4];
  GLdouble matrix[16];

  glGetIntegerv( GL_VIEWPORT, viewport );  // get viewport coordinates

  glSelectBuffer( BUFSIZE, selectBuf );
  glRenderMode( GL_SELECT );
  glInitNames();

  glMatrixMode( GL_PROJECTION );
  glPushMatrix();
    setupCameraView();

    glMatrixMode( GL_PROJECTION );
    glGetDoublev( GL_PROJECTION_MATRIX, matrix );

    glLoadIdentity();
    gluPickMatrix( (double)x, (double)( viewport[3] - y ), 
		  X_PICK_SIZE, Y_PICK_SIZE, viewport );
             // creates picking region near cursor location
    glMultMatrixd( matrix );

    drawScene();

  glMatrixMode( GL_PROJECTION );
  glPopMatrix();

  hits = glRenderMode( GL_RENDER );
  processHits( hits, selectBuf );
}					// End function checkHit


static void processHits( int hits, GLuint *buffer )
//  This function steps through the selection buffer, and updates
//  any info which is affected by the newly picked objects in the scene.
//  IMPORTANT: This is a simple implementation (it won't do everything) and
//  many of the things it does are not really needed in this case...
//
//  hits:    The number of hits which occurred
//  buffer:  A pointer to the selection buffer
{
  int i, j;
  GLuint *ptr = buffer;
  GLuint nameStackDepth, currname, zmin, zmax;

  for( i = 0; i < hits; i++ ) {         // There will really only be <=1 hit
    nameStackDepth = *ptr++;            // Not really needed here
    zmin = *ptr++;                      // Not really needed here
    zmax = *ptr++;                      // Not really needed here
    for( j = 0; j < nameStackDepth; j++ ) {      // Not really needed here
      currname = *ptr++;                // Not really needed here
    }
    Active[currname] = !Active[currname]; // Toggle the state of the cube
  }

  glutPostRedisplay();
}					// End function processHits


static void drawCube()
//  This function draws a cube which is rotated according to the global
//  variable Rotation
{
  glPushMatrix();
    glRotatef( Rotation, 1.0, 1.0, 1.0 );
    glutWireCube( 1.0 );		// draws cube of size 1.0
  glPopMatrix();
}					// End function drawCube


static void setupCameraView( void )
//  This procedure sets up the projection, camera position, and orientation.
{
  glMatrixMode( GL_PROJECTION );

  glLoadIdentity();
  gluPerspective( 45.0, 1.0, 0.1, 100.0 );
  glTranslatef( 0, 0, -5 );		// Move default camera position
                                        // away from origin
}					// End function setupCameraView


static void drawScene( void )
//  This procedure draws the current scene, assuming that the camera view
//  has been set-up.
{
  glMatrixMode( GL_MODELVIEW );

  // glClear below clears also the depth buffer now
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  glColor3f( 1.0, 1.0, 0.0 );          // Set colour to yellow

  if( Active[CUBE_NAME] )
    glColor3f( 1.0, 1.0, 0.0 );          // Set colour to yellow
  else
    glColor3f( 0.0, 1.0, 0.0 );          // Set colour to green

  glPushName( CUBE_NAME );               // Identify object to be drawn
                                         // (no effect if not picking)
  drawCube();
  glPopName();

  glFlush();
}					// End function drawScene

