/*********************************************************************
 * Lighting Demo
 * Copyright (C) 1999
 * By Nathan Buggia
 *
 * File: main.c
 * Date: 12.20.1999
 * Auth: Nathan Buggia <nbuggia@wheatonma.edu>
 *
 * Creates a simple scene in which the users can adjust all of the
 * lighting values.
 ********************************************************************/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include <GL/glut.h>

int RotateX = -63;			// for mouse rotation of the entire scene
int RotateY = 30;
int PrevX = 0;
int PrevY = 0;
int MouseDown = GL_FALSE;

int WindowWidth = 400;
int WindowHeight = 410;

float incrmt = 0.1;

float light_ambient[] = {0.0, 0.5, 0.0, 1.0};  
float light_diffuse[] = {0.5, 0.5, 0.5, 0.6};  
float light_specular[] = {1.0, 1.0, 1.0, 1.0};  
float light_position[] = {-3.0, 2.0, 1.0, 0.0};

float ambient[] = {0.3f, 0.3f, 0.3f, 0.5f};
float diffuse[] = {0.5, 0.5, 0.5, 0.5};
float specular[] = {0.5, 0.5, 0.5, 0.5};
float emission[] = {0.0, 0.0, 0.0, 1.0};
float shiness[] = {50.0};

/*| Prints the message to the screen.
 *|
 *| IN: msg - The message to print
 *|		x - x value to print the message
 *|		y - y value to print the message
*/
void
WriteMessage(char* msg, int x, int y){
	unsigned int i;

	glRasterPos2i(x, y);
	for (i = 0; i < strlen(msg); i++)
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, msg[i]);
}

/*| Called when the window is created and reshaped. Resets the viewport size
 *| to the real size of the window.
 *|
 *| IN: width - new width of the window
 *|		height - new height of the window
*/
void
Reshape(GLint width, GLint height){
	WindowWidth = width;
	WindowHeight = height;
	glViewport(0, 0, width, height);
	glutPostRedisplay();
}

/*| Called when the window needs to be drawn or redrawn. Draws the scene
 *| and all of the lighting parameters to the screen.
 *|
*/
void
Display(void){
	int i;
	char dispText[32];
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glMaterialfv(GL_FRONT, GL_EMISSION, emission );
	glMaterialfv(GL_FRONT, GL_AMBIENT, ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse  );
	glMaterialfv(GL_FRONT, GL_SPECULAR, specular );
	glMaterialfv(GL_FRONT, GL_SHININESS, shiness ); 

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);

	// clear our matricies
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glPushMatrix();
		glOrtho(-10.0, 10.0, -10.0, 10.0, -15.0, 15.0);
		glTranslatef(0.0, -2, 0.0);
		glScalef(2, 2, 2);
		glRotatef(-RotateX, 0.0, 1.0, 0.0);
		glRotatef(RotateY, 1.0, 0.0, 0.0);

		// draw axis
		glColor3f(1.0, 1.0, 1.0);
		glBegin(GL_LINES);
			glVertex3i( -10.0, 0, 0 );		// x-axis
			glVertex3i( 10.0, 0, 0 );

			glVertex3i( 0, -5.0, 0 );		// y-axis
			glVertex3i( 0, 5.0, 0 );

			glVertex3i( 0, 0, -10.0 );		// z-axis
			glVertex3i( 0, 0, 10.0 );

			// grid on the x/z plane
			for(i = 0; i < 10; i++){
				glVertex3i(-10, 0, i);
				glVertex3i(10, 0, i);
				glVertex3i(-10, 0, -i);
				glVertex3i(10, 0, -i);
				//other way
				glVertex3i(i, 0, -10);
				glVertex3i(i, 0, 10);
				glVertex3i(-i, 0, -10);
				glVertex3i(-i, 0, 10);
			}
		glEnd();
		
		// CONE
		glPushMatrix();
			glColor3f(1.0, 0.0, 0.0);
			glTranslatef( 0, 0, 3);
			glRotatef(-90.0, 1.0, 0.0, 0.0);
			glutSolidCone(2.0, 4.0, 15, 15);
		glPopMatrix();

		// CUBE
		glPushMatrix();
			glColor3f(0.0, 1.0, 0.0);
			glTranslatef( -2, 1.5, -3);
			glutSolidCube(3.0);
		glPopMatrix();

		// SPHERE
		glPushMatrix();
			glColor3f(0.0, 0.0, 1.0);
			glTranslatef(2, 1.5, -3);
			glutSolidSphere(2.0, 30.0, 30.0);
		glPopMatrix();

		// light source
		glPushMatrix();
			glDisable(GL_LIGHTING);
			glDisable(GL_LIGHT0);
			glColor3f(1.0, 1.0, 1.0);
			glTranslatef(light_position[0], light_position[1], light_position[2]);
			glutWireSphere(0.20, 7.0, 7.0);
			glEnable(GL_LIGHTING);
			glEnable(GL_LIGHT0);
		glPopMatrix();
	glPopMatrix();

	// draw GUI
	if(!MouseDown){
		glPushMatrix();
			glDisable(GL_LIGHTING);
			glDisable(GL_LIGHT0);
			glDisable(GL_COLOR_MATERIAL);

			glOrtho(0, WindowWidth, WindowHeight, 0, -15, 15);
			glTranslatef(0.0, 0.0, 14);

			// draw lighting info
			WriteMessage("Lighting", 10, 320);
			sprintf(dispText, "ambient{%4.2f, %4.2f, %4.2f, %4.2f}", light_ambient[0], light_ambient[1], light_ambient[2], light_ambient[3]);
			WriteMessage(dispText, 10, 340);
			sprintf(dispText, "diffuse{%4.2f, %4.2f, %4.2f, %4.2f}", light_diffuse[0], light_diffuse[1], light_diffuse[2], light_diffuse[3]);
			WriteMessage(dispText, 10, 355);
			sprintf(dispText, "specular{%4.2f, %4.2f, %4.2f, %4.2f}", light_specular[0], light_specular[1], light_specular[2], light_specular[3]);
			WriteMessage(dispText, 10, 370);
			sprintf(dispText, "position{%4.2f, %4.2f, %4.2f, %4.2f}", light_position[0], light_position[1], light_position[2], light_position[3]);
			WriteMessage(dispText, 10, 385);

			// material info
			// draw lighting info
			WriteMessage("Material", 250, 320);
			sprintf(dispText, "ambient{%4.2f, %4.2f, %4.2f, %4.2f}", light_ambient[0], light_ambient[1], light_ambient[2], light_ambient[3]);
			WriteMessage("Lighting", 10, 320);
			sprintf(dispText, "ambient{%4.2f, %4.2f, %4.2f, %4.2f}", ambient[0], ambient[1], ambient[2], ambient[3]);
			WriteMessage(dispText, 250, 340);
			sprintf(dispText, "diffuse{%4.2f, %4.2f, %4.2f, %4.2f}", diffuse[0], diffuse[1], diffuse[2], diffuse[3]);
			WriteMessage(dispText, 250, 355);
			sprintf(dispText, "specular{%4.2f, %4.2f, %4.2f, %4.2f}", specular[0], specular[1], specular[2], specular[3]);
			WriteMessage(dispText, 250, 370);
			sprintf(dispText, "emission{%4.2f, %4.2f, %4.2f, %4.2f}", emission[0], emission[1], emission[2], emission[3]);
			WriteMessage(dispText, 250, 385);
			sprintf(dispText, "shiness{%4.2f}", shiness[0]);
			WriteMessage(dispText, 250, 400);

			glEnable(GL_LIGHTING);
			glEnable(GL_LIGHT0);
			glEnable(GL_COLOR_MATERIAL);
		glPopMatrix();
	}

	glutSwapBuffers();
}

/*| Called when the cursor is moving in the window. Used to control the drag
 *| scene rotation.
 *|
*/
void
DisplayMouseMotion(int x, int y){

	int dx = PrevX - x;
	int dy = y - PrevY;

	if( MouseDown )
	{
		RotateX += (dx * 180.0f) / 600.0f;
		RotateY += (dy * 180.0f) / 600.0f;
		PrevX = x;
		PrevY = y;
		glutPostRedisplay();
	}
}

/*| Called when the mouse is clicked. Used to control the click-and-drag
 *| scene rotation.
*/
void 
DisplayMouseClick(int button, int state, int x, int y){

        if( state == GLUT_DOWN ){
			if(button == GLUT_LEFT_BUTTON){
				PrevX = x;
				PrevY = y;
				MouseDown = GL_TRUE;
				DisplayMouseMotion(x,y);	// so it moves when we click down.
			}
        }
        else if( state == GLUT_UP ){
            glutPostRedisplay();
			MouseDown = GL_FALSE;
        }
}

/*| Handles all keyboard input. Used to adjust all lighting values.
 *|
 *| IN: key - the key the user pressed.
 *|		x - x coord of the mouse when the key was pressed.
 *|		y - y coord of mouse when key was pressed.
*/
void
Keyboard(unsigned char key, GLint x, GLint y){

	switch(key){
		// light ambient
		case '1':	// RED ++
			light_ambient[0] += incrmt;
			if(light_ambient[0] > 1.0) light_ambient[0] = 1.0f;
			break;
		case '!':	// RED --
			light_ambient[0] -= incrmt;
			if(light_ambient[0] < -1.0) light_ambient[0] = -1.0f;
			break;
		case '2':	// GREEN ++
			light_ambient[1] += incrmt;
			if(light_ambient[1] > 1.0) light_ambient[1] = 1.0f;
			break;
		case '@':	// GREEN --
			light_ambient[1] -= incrmt;
			if(light_ambient[1] < -1.0) light_ambient[1] = -1.0f;
			break;
		case '3':	// BLUE ++
			light_ambient[2] += incrmt;
			if(light_ambient[2] > 1.0) light_ambient[2] = 1.0f;
			break;
		case '#':	// BLUE --
			light_ambient[2] -= incrmt;
			if(light_ambient[2] < -1.0) light_ambient[2] = -1.0f;
			break;
		case '4':	// ALPHA ++
			light_ambient[3] += incrmt;
			if(light_ambient[3] > 1.0) light_ambient[3] = 1.0f;
			break;
		case '$':	// ALPHA --
			light_ambient[3] -= incrmt;
			if(light_ambient[3] < -1.0) light_ambient[3] = -1.0f;
			break;

		// light diffuse
		case 'q':	// RED ++
			light_diffuse[0] += incrmt;
			if(light_diffuse[0] > 1.0) light_diffuse[0] = 1.0f;
			break;
		case 'Q':	// RED --
			light_diffuse[0] -= incrmt;
			if(light_diffuse[0] < -1.0) light_diffuse[0] = -1.0f;
			break;
		case 'w':	// GREEN ++
			light_diffuse[1] += incrmt;
			if(light_diffuse[1] > 1.0) light_diffuse[1] = 1.0f;
			break;
		case 'W':	// GREEN --
			light_diffuse[1] -= incrmt;
			if(light_diffuse[1] < -1.0) light_diffuse[1] = -1.0f;
			break;
		case 'e':	// BLUE ++
			light_diffuse[2] += incrmt;
			if(light_diffuse[2] > 1.0) light_diffuse[2] = 1.0f;
			break;
		case 'E':	// BLUE --
			light_diffuse[2] -= incrmt;
			if(light_diffuse[2] < -1.0) light_diffuse[2] = -1.0f;
			break;
		case 'r':	// ALPHA ++
			light_diffuse[3] += incrmt;
			if(light_diffuse[3] > 1.0) light_diffuse[3] = 1.0f;
			break;
		case 'R':	// ALPHA --
			light_diffuse[3] -= incrmt;
			if(light_diffuse[3] < -1.0) light_diffuse[3] = -1.0f;
			break;

		// light specular
		case 'a':	// RED ++
			light_specular[0] += incrmt;
			if(light_specular[0] > 1.0) light_specular[0] = 1.0f;
			break;
		case 'A':	// RED --
			light_specular[0] -= incrmt;
			if(light_specular[0] < -1.0) light_specular[0] = -1.0f;
			break;
		case 's':	// GREEN ++
			light_specular[1] += incrmt;
			if(light_specular[1] > 1.0) light_specular[1] = 1.0f;
			break;
		case 'S':	// GREEN --
			light_specular[1] -= incrmt;
			if(light_specular[1] < -1.0) light_specular[1] = -1.0f;
			break;
		case 'd':	// BLUE ++
			light_specular[2] += incrmt;
			if(light_specular[2] > 1.0) light_specular[2] = 1.0f;
			break;
		case 'D':	// BLUE --
			light_specular[2] -= incrmt;
			if(light_specular[2] < -1.0) light_specular[2] = -1.0f;
			break;
		case 'f':	// ALPHA ++
			light_specular[3] += incrmt;
			if(light_specular[3] > 1.0) light_specular[3] = 1.0f;
			break;
		case 'F':	// ALPHA --
			light_specular[3] -= incrmt;
			if(light_specular[3] < -1.0) light_specular[3] = -1.0f;
			break;

		// light position
		case 'z':	// X ++
			light_position[0] += 1;
			break;
		case 'Z':	// X --
			light_position[0] -= 1;
			break;
		case 'x':	// Y ++
			light_position[1] += 1;
			break;
		case 'X':	// Y --
			light_position[1] -= 1;
			break;
		case 'c':	// Z ++
			light_position[2] += 1;
			break;
		case 'C':	// Z --
			light_position[2] -= 1;
			break;
		case 'v':	// W ++
			light_position[3] += incrmt;
			if(light_position[3] > 1.0) light_position[3] = 1.0f;
			break;
		case 'V':	// W --
			light_position[3] -= incrmt;
			if(light_position[3] < -1.0) light_position[3] = -1.0f;
			break;

		// material ambient
		case '7':	// RED ++
			ambient[0] += incrmt;
			if(ambient[0] > 1.0) ambient[0] = 1.0f;
			break;
		case '&':	// RED --
			ambient[0] -= incrmt;
			if(ambient[0] < -1.0) ambient[0] = -1.0f;
			break;
		case '8':	// GREEN ++
			ambient[1] += incrmt;
			if(ambient[1] > 1.0) ambient[1] = 1.0f;
			break;
		case '*':	// GREEN --
			ambient[1] -= incrmt;
			if(ambient[1] < -1.0) ambient[1] = -1.0f;
			break;
		case '9':	// BLUE ++
			ambient[2] += incrmt;
			if(ambient[2] > 1.0) ambient[2] = 1.0f;
			break;
		case '(':	// BLUE --
			ambient[2] -= incrmt;
			if(ambient[2] < -1.0) ambient[2] = -1.0f;
			break;
		case '0':	// ALPHA ++
			ambient[3] += incrmt;
			if(ambient[3] > 1.0) ambient[3] = 1.0f;
			break;
		case ')':	// ALPHA --
			ambient[3] -= incrmt;
			if(ambient[3] < -1.0) ambient[3] = -1.0f;
			break;

		// material diffuse
		case 'u':	// RED ++
			diffuse[0] += incrmt;
			if(diffuse[0] > 1.0) diffuse[0] = 1.0f;
			break;
		case 'U':	// RED --
			diffuse[0] -= incrmt;
			if(diffuse[0] < -1.0) diffuse[0] = -1.0f;
			break;
		case 'i':	// GREEN ++
			diffuse[1] += incrmt;
			if(diffuse[1] > 1.0) diffuse[1] = 1.0f;
			break;
		case 'I':	// GREEN --
			diffuse[1] -= incrmt;
			if(diffuse[1] < -1.0) diffuse[1] = -1.0f;
			break;
		case 'o':	// BLUE ++
			diffuse[2] += incrmt;
			if(diffuse[2] > 1.0) diffuse[2] = 1.0f;
			break;
		case 'O':	// BLUE --
			diffuse[2] -= incrmt;
			if(diffuse[2] < -1.0) diffuse[2] = -1.0f;
			break;
		case 'p':	// ALPHA ++
			diffuse[3] += incrmt;
			if(diffuse[3] > 1.0) diffuse[3] = 1.0f;
			break;
		case 'P':	// ALPHA --
			diffuse[3] -= incrmt;
			if(diffuse[3] < -1.0) diffuse[3] = -1.0f;
			break;

		// material specular
		case 'j':	// RED ++
			specular[0] += incrmt;
			if(specular[0] > 1.0) specular[0] = 1.0f;
			break;
		case 'J':	// RED --
			specular[0] -= incrmt;
			if(specular[0] < -1.0) specular[0] = -1.0f;
			break;
		case 'k':	// GREEN ++
			specular[1] += incrmt;
			if(specular[1] > 1.0) specular[1] = 1.0f;
			break;
		case 'K':	// GREEN --
			specular[1] -= incrmt;
			if(specular[1] < -1.0) specular[1] = -1.0f;
			break;
		case 'l':	// BLUE ++
			specular[2] += incrmt;
			if(specular[2] > 1.0) specular[2] = 1.0f;
			break;
		case 'L':	// BLUE --
			specular[2] -= incrmt;
			if(specular[2] < -1.0) specular[2] = -1.0f;
			break;
		case ';':	// ALPHA ++
			specular[3] += incrmt;
			if(specular[3] > 1.0) specular[3] = 1.0f;
			break;
		case ':':	// ALPHA --
			specular[3] -= incrmt;
			if(specular[3] < -1.0) specular[3] = -1.0f;
			break;

		// material emission
		case 'm':	// RED ++
			emission[0] += incrmt;
			if(emission[0] > 1.0) emission[0] = 1.0f;
			break;
		case 'M':	// RED --
			emission[0] -= incrmt;
			if(emission[0] < -1.0) emission[0] = -1.0f;
			break;
		case ',':	// GREEN ++
			emission[1] += incrmt;
			if(emission[1] > 1.0) emission[1] = 1.0f;
			break;
		case '<':	// GREEN --
			emission[1] -= incrmt;
			if(emission[1] < -1.0) emission[1] = -1.0f;
			break;
		case '.':	// BLUE ++
			emission[2] += incrmt;
			if(emission[2] > 1.0) emission[2] = 1.0f;
			break;
		case '>':	// BLUE --
			emission[2] -= incrmt;
			if(emission[2] < -1.0) emission[2] = -1.0f;
			break;
		case '/':	// ALPHA ++
			emission[3] += incrmt;
			if(emission[3] > 1.0) emission[3] = 1.0f;
			break;
		case '?':	// ALPHA --
			emission[3] -= incrmt;
			if(emission[3] < -1.0) emission[3] = -1.0f;
			break;

		// material shiness
		case '=':
			shiness[0] += 2;
			if(shiness[0] > 100.0f) shiness[0] = 100.0f;
			break;
		case '+':
			shiness[0] -= 2;
			if(shiness[0] < 0.0f) shiness[0] = 0.0f;
			break;

		case '-':
			glShadeModel(GL_SMOOTH);
			break;
		case '_':
			glShadeModel(GL_FLAT);
			break;

		case 27:
			exit(-1);
			break;

		default:
			break;
	}
	glutPostRedisplay();
}

/*| Hanldes all input from the popup menu
*/
void
HandleMenu(int choice){

	// NOT IMPLEMENTED

	switch(choice){

	default:
			// this should never happen.
			printf("Unknown menu command.\n");
			break;
		}
}

/*| Create and initialize the popup menus.
*/
void
MenuInit(void){
	int menu;

	// NOT IMPLIMENTED
}

/*| Set opengl options for lighting.
*/
void
Init(void){
	int i;

	// clear color is black, and simple block shading.
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glShadeModel(GL_SMOOTH);

	// used to make sure objects closer to the user are draw last
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
}

/*| Prints instructions and copyright notices onto the console when
 *| the application is launched.
*/
void
PrintUsage(void){
	printf("Lighting Demo v1.0\n");
	printf("Copyright(C) 1999, by Nathan Buggia.\n");
	printf("All rights reserved.\n\n");

	printf("All of the lighting values are controlled by the keyboard. In\n");
	printf("general, a key increses the value, and shift+key decreases the\n");
	printf("value. The lighting values are controlled by the left side of\n");
	printf("the keyboard and the material values are controlled by the\n");
	printf("right side of the keyboard in the order they appear on the\n");
	printf("screen. For example, the the {'1', '2', '3', '4'} keys will\n");
	printf("increase the Red, Blue, Green and Alpha values of the abient\n");
	printf("value of the light source. Also the {'m','<','>','/'} keys will\n");
	printf("increase the respective values of the material emmision.\n");
	printf("\n");
	printf("Finally, the '=' and '+' keys will adjust the shinniness and\n");
	printf("the '-' and '_' keys will toggle flat or smooth shading.\n");
	printf("\n");
	printf("Press ESC to quit\n");
}

/*| ** MAIN **
*/
int
main(int argc, char* argv[]){

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(WindowWidth, WindowHeight);
	glutInitWindowPosition(5, 5);
	glutCreateWindow("Lighting Demo");

	Init();
	MenuInit();
	PrintUsage();

	glutDisplayFunc(&Display);
	glutKeyboardFunc(&Keyboard);
	glutReshapeFunc(&Reshape);
	glutMouseFunc(&DisplayMouseClick);
	glutMotionFunc(&DisplayMouseMotion);

	glutMainLoop();

	return 0;
}
