/*
 * picksquare.cpp
 *
 * Use of names and picking are demonstrated.  
 * A 3x3 grid of squares is drawn.  When the left mouse 
 * button is pressed, all squares under the cursor position 
 * have their color changed.
 */
#include "cs148.h"

// amount of blue for each square
int board[3][3];

// The size of our OpenGL selection buffer
#define BUFSIZE 512

void processHits (GLint hits, GLuint buffer[]);
void drawSquares(GLenum mode);


// Initialization function
void init(void) {

  // Everyone starts with no blue
  int i, j;
  for (i = 0; i < 3; i++) 
    for (j = 0; j < 3; j ++)
       board[i][j] = 0;

  glClearColor (0.0, 0.0, 0.0, 0.0);
}


// Display function
void display(void) {
  glClear(GL_COLOR_BUFFER_BIT);

  // Draws the squares to the screen
  drawSquares(GL_RENDER);
  glutSwapBuffers();
}


// Nine squares are drawn.  Each square is given
// a unique name if we're in selection mode.
void drawSquares(GLenum mode) {

  GLuint i, j;
  for (i = 0; i < 3; i++) {

    for (j = 0; j < 3; j ++) {

       // Give this square a name from 0 to 8
       if (mode == GL_SELECT) glPushName (i*3+j);

       // I could draw a million vertices now and they would
       // all have this name...

       // Give this square a color
       glColor3f ((GLfloat) i/3.0, (GLfloat) j/3.0, 
                  (GLfloat) board[i][j]/3.0);

       // Draw this square
       glRecti (i, j, i+1, j+1);

       // We're done with this name
       if (mode == GL_SELECT) glPopName ();

    } // for each row
  } // for each column

}



/* 
 * pickSquares() is our mouse-click callback.  
 *
 * It sets up selection mode, a name stack, 
 * and a projection matrix for picking.  Then the 
 * objects are drawn.
 */
void pickSquares(int button, int state, int x, int y) {

  GLuint selectBuf[BUFSIZE];
  GLint hits;
  GLint viewport[4];

  // We only care about left mouse clicks
  if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)
    return;

  // Tell GL where to put selection results
  glSelectBuffer(BUFSIZE, selectBuf);

  // Turn on selection mode
  glRenderMode(GL_SELECT);

  // Clear the name stack
  glInitNames();

  // Get the current viewport dimensions
  glGetIntegerv(GL_VIEWPORT, viewport);

  // We're going to build a new projection matrix so everything far 
  // away from the mouse click gets displayed   
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();

  // Create a 5x5 pixel picking region near cursor location
  gluPickMatrix((GLdouble) x, (GLdouble) (viewport[3] - y), 5.0, 5.0, viewport);
  gluOrtho2D(0.0, 3.0, 0.0, 3.0);

  // Do our rendering (nothing will be drawn to the framebuffer, because
  // we're in selection mode)
  drawSquares(GL_SELECT);

  // Fix the projection matrix
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glFlush();

  // How many hits did we get?
  hits = glRenderMode(GL_RENDER);

  // See what the user clicked on...
  processHits(hits, selectBuf);

  // We might have changed colors, so post a redisplay
  glutPostRedisplay();
} 


/*  
 * processHits prints out the contents of the 
 * selection array and changes the color of 
 * squares that got clicked on.
 *
 * OpenGL generates some values in the selection buffer
 * every time you change names or change rendering modes,
 * if something was actually selected.
 *
 * A "hit record" looks like this:
 *
  
   [number of active names]
   [minimum depth rendered during this block]
   [maximum depth rendered during this block]
   [bottom name on the stack]
   [next name on the stack]
   ...
   [unsigned int: top name on the stack]
 
 *
 * Depth values range from 0 (at the near clip plane) to whatever
 * the largest unsigned integer value is (at the far clip plane)
 *
 */
void processHits(GLint hits, GLuint buffer[]) {   

   // How many total hits did we get?
   printf("\n\nTotal hits = %d\n", hits);

   // We'll use this pointer to walk through the buffer
   int* ptr = (int*)buffer;
   
   // For each hit
   for(int curhit = 0; curhit < hits; curhit++) {

      // How many names were there for this hit?
      int numnames = *ptr;
      printf(" number of names for this hit = %d\n", numnames); ptr++;

      // What were the minimum and maximum depth values?

      // We like to convert from the integer depth range to the
      // range (0,1) to make the results more readable
      printf("  min depth is %f;",  (float) *ptr/0x7fffffff);            ptr++;
      printf("  max depth is %f\n", (float) *ptr/0x7fffffff);            ptr++;

      // What were the active names during this hit?
      printf("   names are ");
            
      for(int j = 0; j < numnames; j++) { /*  for each name */

         int curname = *ptr;
         printf ("%d ",curname);

         // This name refers to one of our squares
         int row = curname / 3;
         int col = curname % 3;
         ptr++;

         // Mess around with this squares blue-ness to show that it
         // was clicked on
         board[row][col] = (board[row][col] + 1) % 3;
      }
      printf ("\n");        
   }
}


void reshape(int w, int h) {
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D (0.0, 3.0, 0.0, 3.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

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

