// OpenGL Tutorial
// Light.c

/*************************************************************************
This example is a modification Polygons_List.c to use lighting.  The
three sided sphere was changed to a four-sided sphere to make
calculating the normal vectors easier.

Notice in the source code the order in which normals are defined for
GL_QUAD_STRIP.  In the GL_QUAD_STRIP section, Normal A applies to
vertices 1, 2, 3, and 4; Normal B applies to vertices 3, 4, 5, and 6;
Normal C applies to vertices 5, 6, 7, and 8; and Normal D applies to
vertices 7, 8, 9, and 10.  Due to the way that OpenGL renders objects,
the correct normal value must be set before the last vertex in the
current object is specified so that the correct normal value is
assigned to that object.
*************************************************************************/

// gcc -o Light  Light.c -lX11 -lMesaGL -lMesaGLU -lMesatk -lm

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gltk.h>

#define SPHERE 1
#define CUBE 2

void reshape(int width, int height) {

	// Set the new viewport size
	glViewport(0, 0, (GLint)width, (GLint)height);

	// Choose the projection matrix to be the matrix 
	// manipulated by the following calls
	glMatrixMode(GL_PROJECTION);

	// Set the projection matrix to be the identity matrix
	glLoadIdentity();

	// Define the dimensions of the Orthographic Viewing Volume
	glOrtho(-8.0, 8.0, -8.0, 8.0, -8.0, 8.0);

	// Choose the modelview matrix to be the matrix
	// manipulated by further calls
	glMatrixMode(GL_MODELVIEW);
}

GLenum key_down(int key, GLenum state) {

	if ((key == TK_ESCAPE) || (key == TK_q) || (key == TK_Q))
		tkQuit();
}

void draw(void) {

	// Clear the RGB buffer and the depth buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Set the modelview matrix to be the identity matrix
	glLoadIdentity();
	// Translate and rotate the object
	glTranslatef(-2.5, 0.0, 0.0);

	// Draw sphere
	glCallList(SPHERE);

	glLoadIdentity();
	glTranslatef(2.5, 0.0, 0.0);
	glRotatef(45, 1.0, 0.0, 0.0);
	glRotatef(45, 0.0, 1.0, 0.0);
	glRotatef(45, 0.0, 0.0, 1.0);

	// Draw cube
	glCallList(CUBE);

	// Flush the buffer to force drawing of all objects thus far
	glFlush();
}

void make_sphere() {

	GLint slices, stacks;
	GLUquadricObj *quadObj;

	quadObj = gluNewQuadric();
	slices = 16;
	stacks = 10;

	glNewList(SPHERE, GL_COMPILE);

		glColor3f(1.0, 0.0, 1.0);
		gluSphere(quadObj, 4.0, slices, stacks);
	
	glEndList();
}


void make_cube() {

	glNewList(CUBE, GL_COMPILE);

		glColor3f(0.0, 1.0, 0.0);
	
		// Draw the sides of the cube
		glBegin(GL_QUAD_STRIP);

                        // Normal A
			glNormal3d(0.0, 0.0, -1.0);

                        // Vertex 1
			glVertex3d(3, 3, -3);
                        // Vertex 2
			glVertex3d(3, -3, -3);

                        // Vertex 3
			glVertex3d(-3, 3, -3);
                        // Vertex 4
			glVertex3d(-3, -3, -3);

                        // Normal B
			glNormal3d(-1.0, 0.0, 0.0);

                        // Vertex 5
			glVertex3d(-3, 3, 3);
                        // Vertex 6
			glVertex3d(-3, -3, 3);

                        // Normal C
			glNormal3d(0.0, 0.0, 1.0);

                        // Vertex 7
			glVertex3d(3, 3, 3);
                        // Vertex 8
			glVertex3d(3, -3, 3);

                        // Normal D
			glNormal3d(1.0, 0.0, 0.0);

                        // Vertex 9
			glVertex3d(3, 3, -3);
                        // Vertex 10
			glVertex3d(3, -3, -3);
		glEnd();
	
		glColor3f(0.0, 0.0, 1.0);
	
		// Draw the top and bottom of the cube
		glBegin(GL_QUADS);

			glNormal3d(0.0, 1.0, 0.0);
			glVertex3d(-3, -3, -3);
			glVertex3d(3, -3, -3);
			glVertex3d(3, -3, 3);
			glVertex3d(-3, -3, 3);
	
			glNormal3d(0.0, 1.0, 0.0);
			glVertex3d(-3, 3, -3);
			glVertex3d(3, 3, -3);
			glVertex3d(3, 3, 3);
			glVertex3d(-3, 3, 3);
		glEnd();

	glEndList();
}

void main(int argc, char **argv) {

	GLfloat specular [] = { 1.0, 1.0, 1.0, 1.0 };
	GLfloat shininess [] = { 100.0 };
	GLfloat position [] = { 1.0, 1.0, 1.0, 0.0 };

	// Set top left corner of window to be at location (0, 0)
	// Set the window size to be 500x500 pixels
	tkInitPosition(0, 0, 500, 500);

	// Initialize the RGB and Depth buffers
	tkInitDisplayMode(TK_RGB | TK_DEPTH);

	// Open a window, name it "Light"
	if (tkInitWindow("Light") == GL_FALSE) {
		tkQuit();
	}

	// Set the clear color to black
	glClearColor(0.0, 0.0, 0.0, 0.0);

	// Set the shading model
	glShadeModel(GL_SMOOTH);

	// Set the polygon mode to fill
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	// Enable depth testing for hidden line removal
	glEnable(GL_DEPTH_TEST);

	// Define material properties of specular color and degree of 
	// shininess.  Since this is only done once in this particular 
	// example, it applies to all objects.  Material properties can 
	// be set for individual objects, individual faces of the objects,
	// individual vertices of the faces, etc... 
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);

	// Set the GL_AMBIENT_AND_DIFFUSE color state variable to be the
	// one referred to by all following calls to glColor
	glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);

	// Create a Directional Light Source
	glLightfv(GL_LIGHT0, GL_POSITION, position);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	// Create the display lists
	make_sphere();
	make_cube();

	// Assign reshape() to be the function called whenever
	// an expose event occurs
	tkExposeFunc(reshape);

	// Assign reshape() to be the function called whenever 
	// a reshape event occurs
	tkReshapeFunc(reshape);

	// Assign key_down() to be the function called whenever
	// a key is pressed
	tkKeyDownFunc(key_down);

	// Assign draw() to be the function called whenever a display
	// event occurs, generally after a resize or expose event
	tkDisplayFunc(draw);

	// Pass program control to tk's event handling code
	// In other words, loop forever
	tkExec();
}
