/* System includes */
#include <stdio.h>
#include <stdlib.h>

#ifdef WIN32            
#include <windows.h>    /* necessary because of the Microsoft OpenGL headers dependency */
#endif


#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

/* IM includes */
#include <im.h>
#include <im_capture.h>
#include <im_image.h>
#include <im_convert.h>
#include <im_process.h>
#include <im_format_avi.h>

/* Global module variables */
int width,height;
unsigned char *frame=NULL;
imImage *image=NULL;
imImage *aux_image=NULL;

/* External functions */
void arProcessMarcos5(unsigned char* gl_data, imImage *image,imImage *aux_image, int *flags, double value1, double value2);
extern int getCameraMarcos(double * cam_x, double * cam_y, double * cam_z, double modelview[], 
					double projection[]);

/* Internal functions */
static void ConvertGLData2Luminance(unsigned char* gl_data, imImage* image);


/********************** Exported functions *****************************************/
void arInit(int w, int h)
{
  width=w;
  height=h;
  if (image) imImageDestroy(image);
  if (aux_image) imImageDestroy(aux_image);
  image = imImageCreate(width, height, IM_GRAY, IM_BYTE);
  aux_image = imImageClone(image);
}

imImage * arFrameProcess(unsigned char * rgb, int *flags, double value1, double value2)
{
 ConvertGLData2Luminance(rgb, image);
 frame = rgb;  /* saves the pointer for this frame */

 arProcessMarcos5(rgb, image, aux_image, flags, value1, value2);

 return image;
}

void arRepaintRightCanvas(void)
{
  if (image)
  {
    /* Draws the captured image at (0,0) */
    glClear(GL_COLOR_BUFFER_BIT);  /* clears the back buffer */
    glRasterPos2f(0.f, 0.f); 
	glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, image->data[0] );
  }
}

void arRepaintLeftCanvas(void)
{
  double cam_x;
  double cam_y;
  double cam_z;
  double modelview[16];
  double projection[16];

  if (frame)
  {
    /* Draws the captured image at (0,0) */
    glClear(GL_COLOR_BUFFER_BIT);  /* clears the back buffer */
    glRasterPos2f(0.f, 0.f); 
    glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, frame );

	/* Se a camera foi identificada... */
	if(getCameraMarcos(&cam_x, &cam_y, &cam_z, modelview, projection) == 0) {

		float posicao_camera[4] = {cam_x, cam_y, cam_z, 0.f};
		float white[4] = {1.0f,1.0f,1.0f,1.0f};

		/* Inicializa a viewport de forma a coincidir com a imagem capturada */
        glViewport(0, 0, width, height);

		/* Salva a matriz de projecao atual e carrega a recem-calculada */
		glMatrixMode (GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glLoadMatrixd (projection);

		/* Salva a matriz de modelview atual e carrega a recem-calculada */
		glMatrixMode (GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glLoadMatrixd (modelview);

		/* Habilita o teste de z e limpa o buffer */
		glEnable(GL_DEPTH_TEST);
		glClear(GL_DEPTH_BUFFER_BIT);

		/* 
		Inicio do desenho de objetos 3d
		------------------------------- */

		/* Cria fonte de luz na posicao da camera */
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glLightfv(GL_LIGHT0,GL_POSITION,posicao_camera);

		glEnable(GL_COLOR_MATERIAL);
		glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
		glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,white);
		glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
		glMaterialfv(GL_FRONT,GL_SPECULAR,white);
		glMaterialf(GL_FRONT,GL_SHININESS,50.0);
		glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1);

		// Polygon offset: afasta para poder desenhar sobre sobre o quad */
		glPushAttrib(GL_ENABLE_BIT);
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(1.0, 1.0);

		/* Desenha quad. sobre a area branca interna do padrao */
		glColor3d(1.0, 0.0, 0.0);
		glBegin(GL_QUADS);
			glVertex3f(-65, -16.5, 0);
			glVertex3f( 54, -16.5, 0);
			glVertex3f( 54,  46.5, 0);
			glVertex3f(-65,  46.5, 0);
		glEnd();

		glPopAttrib();

		/* Desenha linhas ligando cada vertice do quad. ao centro do padrao */
		glColor3d(0.0, 1.0, 0.0);
		glBegin(GL_LINES);
			glVertex3f(0, 0, 0);
			glVertex3f(-65, -16.5, 0);
			glVertex3f(0, 0, 0);
			glVertex3f( 54, -16.5, 0);
			glVertex3f(0, 0, 0);
			glVertex3f( 54,  46.5, 0);
			glVertex3f(0, 0, 0);
			glVertex3f(-65,  46.5, 0);
		glEnd();

		/* Desenha Teapot no centro do padrao */
		glColor3f(0.0f,0.0f,1.0f);
		glPushMatrix();
		glTranslatef(0.f, 0.0f, 27.0f);
		glRotatef(90.f, 1.0f, 0.0f, 0.0f);
		glutSolidTeapot(36.0);
//		glutWireTeapot(36.0);
		glPopMatrix();

		/* 
		Final do desenho de objetos 3d
		------------------------------ */


		/* 
		Restaura contexto
		----------------- */

		glDisable(GL_COLOR_MATERIAL);
		glDisable(GL_LIGHTING);
		glDisable(GL_LIGHT0);
		glDisable(GL_DEPTH_TEST);
		glMatrixMode (GL_PROJECTION);
		glPopMatrix();
		glMatrixMode (GL_MODELVIEW);
		glPopMatrix();
	}
  }
}

void arEnd( )
{
  if (image) imImageDestroy(image);
  if (aux_image) imImageDestroy(aux_image);
}



/********************** Internal functions *******************************/
static void ConvertGLData2Luminance(unsigned char* gl_data, imImage *img)
{
	if (img)
	{
        int i;
		unsigned char* im_data = (unsigned char*)img->data[0];
        for (i = 0; i < img->count; i++)
            {
                *im_data++ = (unsigned char)( 0.2999*(*gl_data) + 0.587*(*(gl_data+1)) + 0.114*(*(gl_data+2)) );
				gl_data += 3;
			}
	}
}