/*********************************
********    drag.c
*********************************/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <GL/glut.h>

#ifndef M_PI
#define M_PI 3.1415926
#endif

/* VARIAVEIS GLOBAIS */
char usarTimer;
GLenum doubleBuffer;
GLint delay;

/* ... outras variaveis globais ... */
char str[255];
int botao, sentido;
int width, height;
double dist = -20, camx = -45.0, camz = 0.0;
double objx, objy, objz=0.5; 
double fovy=60.0, Near=1, Far=100; 
int objid = 0;

typedef struct {
		GLdouble pos[3];
		GLdouble cor[4];
}objecto;

objecto obj[6]={{{2, 4, 0.5},{1.0, 0.0, 0.0, 1.0}},
				{{-3, -1, 0.5},{1.0, 1.0, 0.0, 1.0}},
				{{2, 2, 0.5},{1.0, 0.0, 1.0, 1.0}},
				{{0.7, 0.1, 0.5},{0.0, 1.0, 0.0, 1.0}},
				{{1, -3, 0.5},{0.0, 1.0, 1.0, 1.0}},
				{{5, 2, 0.5},{0.0, 0.0, 1.0, 1.0}}
				};


#define NUM_OBJ     6


/* INICIALIZAR */



void
Init(void)
{

  strcpy(str,"Clique com o botão do lado direito num cubo e arraste-o");
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glEnable(GL_COLOR_MATERIAL);
}

/* CALLBACK PARA REDIMENSIONAR JANELA */

void setView(int x, int y,int tipo)
{
  GLint vp[4];

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(tipo) {
		glGetIntegerv(GL_VIEWPORT, vp);
		gluPickMatrix(x, height - y, 1, 1, vp);
  }
  gluPerspective(fovy, (double) width / (double) height, Near, Far);
  gluLookAt(15,15,15,0,0,0,0,0,1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void
Reshape(int w, int h)
{
  width = w;
  height = h;
  glViewport(0, 0, (GLint) w, (GLint) h);
  setView(0,0,0);
}



/* CALLBACK PARA INTERACCAO VIA TECLADO */
void
Key(unsigned char key, int x, int y)
{
  switch (key) {
    case 27:
      exit(1);
    /* ... accoes sobre outras teclas ... */
/*	case '4':
		camz += 5.0;
		break;
	case '6':
		camz -= 5.0;
		break;
	case '8':
		camx -= 5.0;
		break;
	case '2':
		camx += 5.0;
		break;
	case '9':
		camx -= 5.0;
		camz -= 5.0;
		break;
	case '7':
		camx -= 5.0;
		camz += 5.0;
		break;
	case '1':
		camz += 5.0;
		camx += 5.0;
		break;
	case '3':
		camz -= 5.0;
		camx += 5.0;
		break;


	case '+':
		if(dist < -5)
			dist /= 1.1;
		break;
	case '-':
		if(dist > -25)
			dist *= 1.1;
		break;
*/	default:
      return;
  }
/* OBRIGAR A REDESENHAR */
  glutPostRedisplay();
}

/* CALLBACK PARA INTERACCAO VIA TECLAS ESPECIAIS */
void SpecialKey(int key, int x, int y)
{
  switch (key) {
    /* ... accoes sobre outras teclas especiais ... */
    default:
      return;
  }
/* OBRIGAR A REDESENHAR */
  glutPostRedisplay();
}

/* ... definicao das rotinas auxiliares de desenho ... */

void strokeString(char *str,double x, double y, double z, double s)
{
	int i,n;
	
	n = strlen(str);
	glPushMatrix();
	glColor3d(0.0, 0.0, 0.0);
	glTranslated(x,y,z);
	glScaled(s,s,s);
	for(i=0;i<n;i++)
		glutStrokeCharacter(GLUT_STROKE_ROMAN,(int)str[i]);

	glPopMatrix();

}

void bitmapString(char *str, double x, double y)
{
	int i,n;

	n = strlen(str);
	glRasterPos2d(x,y);
	for (i=0;i<n;i++)
		glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,(int)str[i]);
}

void chao()
{
	glPushName(0);
		glColor3d(0.5, 0.5, 0.5);
    glNormal3f(0,0,1);
		glRectd(-8, -8, 8, 8);
	glPopName();

}

void objectos()
{
	double posx = -7.0, posy = -7.0;
	int dummy = 1;

	
	for(dummy=0;dummy<NUM_OBJ;dummy++)
	{
		glColor4dv(obj[dummy].cor);
		//glColor3fv(cores[dummy]);
		glPushName(dummy+1);
			glPushMatrix();
				glTranslated(obj[dummy].pos[0],obj[dummy].pos[1],obj[dummy].pos[2]);
				glutSolidCube(1);
			glPopMatrix();
		glPopName();
	}
}

void plano()
{
/*	int i=1,j=1;
	double posx = -7.0, posy = -7.0;
	int dummy = 1;

	glTranslated(0.0, 0.0, dist);
	glRotated(camx, 1.0, 0.0, 0.0);
	glRotated(camz, 0.0, 0.0, 1.0);
*/


//    glPushName(0);
		glPushMatrix();
			glTranslated(0.0, 0.0, objz);
			glScalef(100,100,0.1);
			glutSolidCube(1);
/*			glBegin(GL_POLYGON); 
				glVertex3f(-10, -10, objz);
				glVertex3f(10, -10, objz); 
				glVertex3f(10, 10, objz);
				glVertex3f(-10, 10, objz); 
			glEnd( );
*/
		glPopMatrix();
//	glPopName();
	
}



/* CALLBACK DE DESENHO */
void
Draw(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* ... chamada das rotinas auxiliares de desenho ... */
  glLoadIdentity();
  
  //strokeString(str,0,6,0,1);
  chao();
  objectos();
  //plano();

  glFlush();
  if (doubleBuffer)
    glutSwapBuffers();
}

void Drag(int x, int y)
{
	GLuint buffer[100];
	GLint vp[4];
	int n;
	GLdouble newx, newy, newz;
	GLdouble proj[16], mv[16];

	if (objid >= 1 && objid <= NUM_OBJ)
	{
		glSelectBuffer(100, buffer);
		glRenderMode(GL_SELECT);
		glInitNames();
		/*glPushMatrix();
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glGetIntegerv(GL_VIEWPORT, vp);
		gluPickMatrix(x, height - y - 1, 1, 1, vp);
		gluPerspective(fovy, (double) width / (double) height, Near, Far);
		glMatrixMode(GL_MODELVIEW);
    /* desenha plano para ver onde está o rato */
		setView(x,y,1);
		plano();
		n = glRenderMode(GL_RENDER);
		if (n > 0)
		{
			glGetIntegerv(GL_VIEWPORT, vp);
			glGetDoublev(GL_PROJECTION_MATRIX, proj);
			glGetDoublev(GL_MODELVIEW_MATRIX, mv);
			gluUnProject(x, height - y, (double) buffer[2] / 0xffffffff, mv, proj, vp, &newx, &newy, &newz);
			printf("%lf, %lf, %lf\n\n", newx, newy, newz);

    /* restrições da mesa */

			if (newx > 7.0)
				newx = 7.0;

			if (newy > 7.0)
				newy = 7.0;

			if (newx < -7.0)
				newx = -7.0;

			if (newy < -7.0)
				newy = -7.0;


			
			obj[objid-1].pos[0] = newx;
			obj[objid-1].pos[1] = newy;
			
		}
		/*glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();*/
		setView(x,y,0);
		glutPostRedisplay();
  } 
}


void Mouse(int bt, int st, int x, int y)
{
	int i, n;
	double zmin = 10.0;
	GLuint buffer[100], *ptr;
	GLint vp[4];
	GLdouble proj[16], mv[16];


	botao = bt;
	sentido = st;

	if (bt == GLUT_LEFT_BUTTON && st == GLUT_DOWN)
	{
		glSelectBuffer(100, buffer);
		glRenderMode(GL_SELECT);
		glInitNames();
/*		glPushMatrix();
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glGetIntegerv(GL_VIEWPORT, vp);
		gluPickMatrix(x, height - y - 1, 1, 1, vp);
		gluPerspective(fovy, (double) width / (double) height, Near, Far);
		glMatrixMode(GL_MODELVIEW);

*/		setView(x,y,1);
		objectos();
		n = glRenderMode(GL_RENDER);
		if (n > 0)
		{
			ptr = buffer;
			for (i = 0; i < n; i++)
			{
				if (zmin > (double) ptr[1] / UINT_MAX)
				{
					zmin = (double) ptr[1] / UINT_MAX;
					objid = ptr[3];
				}
				ptr += 4;
			}
			if (objid >= 1 && objid <= NUM_OBJ)
			{
/*				printf("Object: %d, Zmin = %lf\n", objid, zmin);
				glGetIntegerv(GL_VIEWPORT, vp);
				glGetDoublev(GL_PROJECTION_MATRIX, proj);
				glGetDoublev(GL_MODELVIEW_MATRIX, mv);
				gluUnProject(x, height - y - 1, zmin, mv, proj, vp, &objx, &objy, &objz);
				printf("%lf, %lf, %lf\n\n", objx, objy, objz);
*/				glutMotionFunc(Drag);
			}

		}
		else
			objid = 0;

/*		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();*/
		setView(x,y,0);
	}
	if (bt == GLUT_LEFT_BUTTON && st == GLUT_UP)
	{
		if (objid >= 1 && objid <= NUM_OBJ)
		{
			/* acabou o drag */
			glutMotionFunc(NULL);
		}
	}
}

/* PROCESSAR OPCOES NA LINHA DE COMANDOS */
GLenum
Args(int argc, char **argv)
{
  GLint i;

  doubleBuffer = GL_TRUE;
  delay = 10;
  for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-sb") == 0) {
      doubleBuffer = GL_FALSE;
    } else if (strcmp(argv[i], "-db") == 0) {
      doubleBuffer = GL_TRUE;
    } else if (strncmp(argv[i], "-td", 3) == 0) {
      delay = atof(&argv[i][3]);
      if (delay < 10)
        delay = 10;
    } else {
      printf("%s: opcao incorrecta\n", argv[i]);
      return GL_FALSE;
    }
  }
  return GL_TRUE;
}

void
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  if (Args(argc, argv) == GL_FALSE)
    exit(1);
  glutInitWindowPosition(0, 0);
  glutInitWindowSize(300, 300);
  glutInitDisplayMode((doubleBuffer) ? GLUT_DOUBLE : GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
  if (glutCreateWindow("Picking Com Drag") == GL_FALSE)
    exit(1);
  Init();
/* REGISTAR CALLBACKS */
  glutReshapeFunc(Reshape);
  glutKeyboardFunc(Key);
  glutSpecialFunc(SpecialKey);
  glutDisplayFunc(Draw);
  glutMouseFunc(Mouse);

/* COMECAR... */
  glutMainLoop();
}


