/*
 * Test program for glpng
 * by Ben Wyatt ben@wyatt100.freeserve.co.uk
 * Featuring a shameless plug for my stunt course program
 * Available from the same site as glpng
 * http://www.wyatt100.freeserve.co.uk/download.htm
 *
 * Modified by slr 10/2005
 *		to use orthographic window
 *		to remove idle callback
 *		to remove double buffering
 *		added logic for displaying the image and the histogram
 *		added logic to display thresholded binary image based on 
 *			mouse position on histogram
 *		to use luminance rather than RGB
 *		with GUI change
 */

#include <gl/glpng.h>
#include <gl/glut.h>
#include <stdlib.h>

/*************************************************
  Change the following to reflect your png image.
  ***********************************************/
#define IMG_WIDTH 256	// image width
#define IMG_HEIGHT 256	// image height
#define MARGIN 10  /* black border size around image */
#define FILENAME "blklab.png"


#define drawOneLine(x1,y1,x2,y2)  glBegin(GL_LINES);  \
   glVertex2f ((x1),(y1)); glVertex2f ((x2),(y2)); glEnd();

int angle = 0;
int action = 0; /* 0 for initial value, 1 for display image, and 2 for display histogram */
int mx0, my0, mxf, myf;
GLubyte *image;
GLubyte *bimage;
GLubyte *timage, *aimage, *uimage, *vimage, *wimage, *ximage, *yimage, *zimage;

void reversePixels(void) {
	GLubyte *holdImage, *saveHoldImage, *pixelPtr;
	int i, j;
	
	holdImage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (holdImage == NULL) {
		puts("no holdImage allocated: program ending");
		exit(0);
	}
	else
		puts("holdImage allocated");
	saveHoldImage = holdImage;
	pixelPtr = image+(IMG_WIDTH*(IMG_HEIGHT-1)*sizeof(GLubyte));
	for (j=0; j<IMG_HEIGHT; j++){
		for (i=0; i<IMG_WIDTH; i++){
		*holdImage++ = *pixelPtr++; 
		}
		pixelPtr-=((IMG_WIDTH*2)*(sizeof(GLubyte))); //up two rows
	}
	free(image);
	image = saveHoldImage;
}
/***** GLUT callback functions *****/

void KeyPress(unsigned char key, int x, int y) {
	switch (key) {
		case 'q':
		case 'Q':
		case 27: /*ESC*/ 
			glutDestroyWindow(glutGetWindow()); 
			exit(0); 
		break;

	}
}
void displayImage(int x, int y, GLubyte *ip) {
	glRasterPos2i(x, y);
	glDrawPixels(256, 256, GL_LUMINANCE, GL_UNSIGNED_BYTE, ip);
}

void displayHistogram(int x0, int y0) {
/***************************************
displays the histogram
x0 - initial lower left x coordinate position of histogram display
y0 - initial lower left y coordinate position of histogram display
***************************************/
	int i;
	long int max, x, y;
	float scale;
	long int histogram[256];
	GLubyte *saveimage;
	displayImage(x0, y0, bimage);
	glColor3f (1,1,1);		//line color
	glLineWidth (1.0);			//line thickness in pixels
	drawOneLine (x0, y0, x0+IMG_WIDTH, y0);	
	saveimage = image;
	for (i=0; i<256; i++)
		histogram[i] = 0;
	for (i=0; i<IMG_WIDTH*IMG_HEIGHT; i++,image++) {
		histogram[(int)*image]++;
		//printf("%d ",*image);
	}
	for (i=0, max=0; i <256; i++){
		if (max < histogram[i])
			max = histogram[i];
	}
	scale = 256.0/(float)max;
	printf("max = %d, scale = %f\n",max,scale);
	for (i=0,x=x0,y=y0; i<256; i++,x++) {
		drawOneLine(x,y,x,(int)(y + (scale*histogram[i])));
		//printf("%f %d \n",scale*(y+histogram[i]), (int)(scale*(y+histogram[i])));
		printf("%d %d, %d, %d\n",x,y,x,(int)(y + (scale*histogram[i])));
	}
	image=saveimage;
}



void threshold(int thresh) {
	GLubyte *ip, *tip;
	int i;

	for (ip=image, tip=timage, i=0; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++,tip++) { //for  each pixel
		if (*ip < thresh) 
			*tip = 0;
		else
			*tip = 255;
	}
	displayImage((IMG_HEIGHT*2)+3*MARGIN, MARGIN,timage);
	
}
void motion(int x, int y) {
	int dx;
	if((x > (2*MARGIN + IMG_WIDTH)) && (x <= 2*MARGIN + 2*IMG_WIDTH)
		&& (y > (2*MARGIN + IMG_HEIGHT)) && (y <= 2*MARGIN + 2*IMG_HEIGHT)) {
			displayHistogram(2*MARGIN+IMG_WIDTH, 2*MARGIN+IMG_HEIGHT);
			glColor3f (0.0, 0.0, 0.0);		//line color
			glLineWidth (1.0);			//line thickness in pixels
			drawOneLine (x, 2*MARGIN + IMG_HEIGHT, x, 2*MARGIN + 2*IMG_HEIGHT);	
			threshold(x - (2*MARGIN+IMG_WIDTH));
		}
	else if ((x > MARGIN) && (x <= MARGIN+IMG_WIDTH)
		&& (y > MARGIN) && (y <= MARGIN + IMG_HEIGHT)) {
			mxf=x;
			myf=(4*MARGIN+3*IMG_HEIGHT) - y;
			displayImage(MARGIN, (IMG_HEIGHT*2)+3*MARGIN, image);
			glColor3f (1.0, 1.0, 1.0);		//line color
			glLineWidth (1.0);			//line thickness in pixels
			drawOneLine (mx0, my0, mxf, my0);
			drawOneLine (mxf, my0, mxf, myf);	
			drawOneLine (mxf, myf, mx0, myf);	
			drawOneLine (mx0, myf, mx0, my0);	
			printf("in caught movement mxf=%d, myf=%d\n",mxf,myf);
			glFlush();
		}
}

void mouse(int button, int state, int x, int y) {
	if ( button == GLUT_LEFT_BUTTON && state ==  GLUT_DOWN) {
		if (((x > MARGIN) && (x <= MARGIN+IMG_WIDTH))
		&& ((y > MARGIN) && (y <= MARGIN + IMG_HEIGHT))) {
			mx0=x;
			my0=(4*MARGIN+3*IMG_HEIGHT) - y;
			printf("in caught mouse mx0=%d, my0=%d\n",mx0,my0);
		}
	}
	printf("in mouse x=%d, y=%d\n",x,y);
}
void display(void) {
	float r=0.0, g=0.0, b=0.0;
	glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
	displayImage(MARGIN, (IMG_HEIGHT*2)+3*MARGIN, image);
	displayHistogram(2*MARGIN+IMG_WIDTH, 2*MARGIN+IMG_HEIGHT);
	threshold(100);
	displayImage(MARGIN, (IMG_HEIGHT)+2*MARGIN, uimage);
	displayImage(MARGIN, MARGIN, vimage);
	displayImage(MARGIN*2+IMG_WIDTH, MARGIN, wimage);
	displayImage(MARGIN*2+IMG_WIDTH, (IMG_HEIGHT*2)+3*MARGIN, ximage);
	displayImage(MARGIN*3+(2*IMG_WIDTH), (IMG_HEIGHT*2)+3*MARGIN, yimage);
	displayImage(MARGIN*3+(2*IMG_WIDTH), (IMG_HEIGHT)+2*MARGIN, zimage);
	glFlush();
}

void reshape(int w, int h)
{
   glViewport(0, 0, (GLsizei)w, (GLsizei) h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D(0.0,(GLdouble)w, 0.0, (GLdouble) h);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glutPostRedisplay();
}
void allocImages(void) {
	//allocates and initializes images
	// bimage needs to be initialized, the others may not
	int i;
	GLubyte *ip;

	image = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (image == NULL) {
		printf("image could not be allocated\n");
		exit (0);
	}
	bimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (bimage == NULL) {
		printf("bimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=bimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 100;
	}
	timage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (timage == NULL) {
		printf("timage could not be allocated\n");
		exit (0);
	}
	uimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (uimage == NULL) {
		printf("uimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=uimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 150;
	}
	vimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (vimage == NULL) {
		printf("vimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=vimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 70;
	}
	wimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (wimage == NULL) {
		printf("wimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=wimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 180;
	}
	ximage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (ximage == NULL) {
		printf("ximage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=ximage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 200;
	}
	yimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (yimage == NULL) {
		printf("yimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=yimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 130;
	}
	zimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (zimage == NULL) {
		printf("zimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=zimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 160;
	}
	aimage = (GLubyte *) (malloc (IMG_WIDTH * IMG_HEIGHT * sizeof(GLubyte)));
	if (aimage == NULL) {
		printf("bimage could not be allocated\n");
		exit (0);
	}
	for (i=0, ip=aimage; i<IMG_WIDTH*IMG_HEIGHT; i++,ip++){
		*ip = 60;
	}
}
/***** Main function *****/



void main() {
	pngInfo info;
	GLuint  texture;

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize((IMG_WIDTH*3)+4*MARGIN, (IMG_HEIGHT*3)+4*MARGIN);
	glutCreateWindow("project starting point");
	
	#if 0 // Using pngLoad and setting texture parameters manually.
		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

		if (!pngLoad(FILENAME, PNG_NOMIPMAP, PNG_SOLID, &info)) {
			puts("Can't load file");
			exit(1);
		}
	#else // Using pngLoadAndBind to set texture parameters automatically.
		texture = pngBind(FILENAME, PNG_NOMIPMAP, PNG_ALPHA, &info, GL_CLAMP, GL_NEAREST, GL_NEAREST);

		if (texture == 0) {
			puts("Can't load file");
			exit(1);
		}
	#endif

	printf("Size=%i,%i Depth=%i Alpha=%i\n", info.Width, info.Height, info.Depth, info.Alpha);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, texture);
	allocImages();
	if (image == NULL) {
		puts("no image allocated: program ending");
		exit(0);
	}
	else
		puts("image allocated");
	glGetTexImage(GL_TEXTURE_2D, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
	
	reversePixels();
	
	glEnable(GL_CULL_FACE);
	glColor3f(1, 1, 1);

	glutKeyboardFunc(KeyPress);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);

	glutMainLoop();
}
