#include "exp004mouse.h"
#include "exp004processhits.h"
#include "exp004reshape.h"
#include "../view/exp004geometry.h"
#include "../view/exp004state0.h"
#include "../util/check_error.h"
#include "../util/pick_convert.h"
#include <GL/glut.h>
#include <stdio.h>

/*
 * A simple alias to make the code more readable.
 */
#define S exp004state0

void
exp004mouse (int button, int state, int x, int y)
{
  if (button == GLUT_LEFT_BUTTON && state == GLUT_UP)
    {
      if (S.selecting == true && S.zoom == true)
	{
	  GLint viewport[4];
	  glGetIntegerv (GL_VIEWPORT, viewport);

	  glMatrixMode (GL_PROJECTION);
	  glLoadIdentity ();

	  /*
	   * Convert the selection boundary from window coordinates to
	   * world coordinates.
	   */

	  gluOrtho2D(-20.0,
		     20.0,
		     -20.0,
		     20.0);      

	  glutPostRedisplay ();
	}

      /*
       * Complete a selection if one was started and not cancelled.
       */
      if (S.selecting == true && S.zoom == false)
	{
	  
	  /*
	   * "Specify the array to be used for the returned hit records
	   * with glSelectBuffer () [Redbook]."
	   */
	  GLuint select_buf[ROWS];
	  glSelectBuffer (ROWS, select_buf);
	  
	  /*
	   * "Enter selection mode by specifying GL_SELECT with
	   * glRenderMode () [Redbook]."
	   */
	  glRenderMode (GL_SELECT);
	  
	  /*
	   * "Initialize the name stack using glInitNames () and glPush
	   * Names () [Redbook]."
	   */
	  glInitNames ();
	  glPushName (0);
	  
	  /*
	   * "Define the viewing volume you want to use for selection.
	   * Usually this is different from the viewing volume you
	   * originally used to draw the scene, so you probably want to
	   * save and then restore the current transformation state with
	   * glPushMatrix () and glPopMatrix () [Redbook]."
	   */
	  glMatrixMode (GL_PROJECTION);
	  glPushMatrix ();
	  glLoadIdentity ();
	  
	  GLint viewport[4];
	  glGetIntegerv (GL_VIEWPORT, viewport);
	  
	  double c_x = 0.0;
	  double c_y = 0.0;
	  double w = 0.0;
	  double h = 0.0;
	  pick_convert (S.select_x, S.select_y, x, y,
			&c_x, &c_y, &w, &h);

	  gluPickMatrix (c_x,
			 (GLdouble)viewport[3] - c_y,
			 w,
			 h,
			 viewport);	  
	  
	  gluOrtho2D(S.ortho.min_x,
		     S.ortho.max_x,
		     S.ortho.min_y,
		     S.ortho.max_y);      
	  
	  /*
	   * "Alternately issue primitive drawing commands and commands to
	   * manipulate the name stack so that each primitive of interest
	   * has appropriate names assigned [Redbook]."
	   */
	  exp004geometry (GL_SELECT);
	  
	  glMatrixMode (GL_PROJECTION);      
	  glPopMatrix ();
	  glutSwapBuffers ();
	  
	  /*
	   * "Exit selection mode and process the returned selection data
	   * (the hit records) [Redbook]."
	   */
	  GLint hits = glRenderMode (GL_RENDER);
	  check_error (__FILE__, __LINE__);
	  
	  /* "process hits from selection mode rendering [Angel,2008]." */
	  exp004processhits (hits, select_buf);
	  
	  /* "normal render [Angel,2008]." */      
	  glutPostRedisplay ();	 
	}

    }

  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
      S.selecting = true;
      S.select_x = x;
      S.select_y = y;
    }

  return;
}