//
// selectiondemo.cpp
// demo of using OpenGL selection mode
// Bill Thibault April 2005
//

#include <windows.h>
#include <GL/glut.h>
#include <stdio.h>

#define BUFFERSIZE 100
#define PICKSLOP 4

// display window size, set in reshape()
int W;
int H;

float xrot = 0.0;
float yrot = 0.0;

void 
init(void) 
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glEnable ( GL_DEPTH_TEST ); // use the depth buffer
}


void
drawObjects ()
{
	int mode;

	// ask opengl for the current render mode
	glGetIntegerv ( GL_RENDER_MODE, &mode );

	// rotate the objects
	glPushMatrix();
	glRotatef ( xrot, 1,0,0 );
	glRotatef ( yrot, 0,1,0 );

	// draw object 1 (red line)
	if ( mode == GL_SELECT )
		glLoadName ( 1 );
	glColor3f ( 1,0,0 );
	glBegin ( GL_LINES );
		glVertex3f ( 0,0,0 );
		glVertex3f ( 1,0,0 );
	glEnd();

	// draw object 2 (green line)
	if ( mode == GL_SELECT )
		glLoadName ( 2 );
	glColor3f ( 0,1,0 );
	glBegin ( GL_LINES );
		glVertex3f ( 0,2,0 );
		glVertex3f ( 1,2,0 );
	glEnd();

	// draw object 3 (blue line)
	if ( mode == GL_SELECT )
		glLoadName ( 3 );
	glColor3f (  0,0,1 );
	glBegin ( GL_LINES );
		glVertex3f ( 0,4,0 );
		glVertex3f ( 1,4,0 );
	glEnd();

	glPopMatrix();


}

void 
display(void)
{
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	drawObjects ();

	glutSwapBuffers();

}

void 
reshape (int w, int h)
{
	W = w;
	H = h;
   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 50.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -10.0);
}

void 
keyboard (unsigned char key, int x, int y)
{
   switch (key) {
      case 27: /* ESC */
         exit(0);
         break;
	  case 'x':
		  xrot += 1.0;
		  break;
	  case 'y':
		  yrot += 1.0;
		  break;
      default:
         break;
   }
}

void
setupSelectionMode ( int x, int y, GLuint *selBuffer )
{
	GLint    viewport[4];

	// setup name stack
	glSelectBuffer ( BUFFERSIZE, selBuffer ); // this must be called while in render mode!
	glRenderMode ( GL_SELECT );
	glInitNames();							  // this must be called in selection mode!
	glPushName ( 0 );

	// setup pick matrix
	glGetIntegerv ( GL_VIEWPORT, viewport );
	glMatrixMode ( GL_PROJECTION );
	glPushMatrix();
	glLoadIdentity();
	gluPickMatrix ( (GLdouble) x, (GLdouble) ( viewport[3] - y ),
		            PICKSLOP, PICKSLOP, viewport );
	gluPerspective(65.0, (GLfloat) viewport[2]/(GLfloat) viewport[3], 
					1.0, 50.0); // same as in reshape()
	glMatrixMode ( GL_MODELVIEW );
}

void
processHits ( GLint numHits, GLuint *selBuffer )
{
	GLuint *ptr = selBuffer;
	GLuint numNames;
	GLuint zmin;
	GLuint zmax;
	GLuint hitName;
	GLuint nearest = 0xffffffff;
	GLuint picked = -1;

	// each pick record looks like this (for this example):
	// 1        - number of names on name stack at the time (should always be 1 in this example, in general can be >1 )
	// zmin     - range of z values for the part of object in pick window
	// zmax
	// name     - the contents of the name stack, i.e., the object name loaded just before object was drawn
	// 1
	// zmin
	// zmax
	// name
	// etc.

	for ( int i = 0; i < numHits; i++ )
	{
		numNames = *ptr++;
		zmin = *ptr++;
		zmax = *ptr++;
		for ( GLuint j = 0; j < numNames; j++ )
		{
			hitName = *ptr++;
			if ( zmin < nearest ) {
				nearest = zmin;
				picked = hitName;
			}
		}
	}

	printf ( "picked object %d\n", picked );
}


void
mouse ( int button, int state, int x, int y )
{
	GLuint   selBuffer[BUFFERSIZE];
	GLint    numHits;

	if ( button != GLUT_LEFT_BUTTON || state != GLUT_DOWN )
		return;

	// setup pick matrix and selection buffer,
	// enter selection mode
	setupSelectionMode ( x, y, selBuffer );

	// draw objects
	drawObjects ();

	// undo pick matrix
	glMatrixMode ( GL_PROJECTION );
	glPopMatrix();
	glFlush();
	glMatrixMode ( GL_MODELVIEW );

	// check for hits
	numHits = glRenderMode ( GL_RENDER );

	// process them
	processHits ( numHits, selBuffer );	

}


int 
main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
   glutInitWindowSize (300, 300); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutMouseFunc ( mouse );
   glutDisplayFunc(display);
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutIdleFunc(display); 
   glutMainLoop();
   return 0;
}
