// OpenGL Tutorial
// Swing.c

/*************************************************************************
This program demonstrates animation.
*************************************************************************/

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

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

#define SWING 1
#define BAR 2

GLfloat 	angle;		// angle of swing
int		direction;	// direction of swing

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(-50.0, 50.0, -50.0, 50.0, -150.0, 150.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();

	// Rotate the bar and swing
	glRotatef(60.0, 0.0, 1.0, 0.0);

	// Draw the bar
	glCallList(BAR);

	// Translate and rotate the swing to make it `swing'
	glTranslatef(0.0, 30.0, 0.0);
	glRotatef(angle, 1.0, 0.0, 0.0);
	glTranslatef(0.0, -30.0, 0.0);

	// Draw the swing
	glCallList(SWING);

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

	// Swap the buffers
	tkSwapBuffers();

	// Make the it swing back or forth depending upon direction
	if (direction == 1)
		angle++;
	else
		angle--;

	// Change the direction if 40 or -40 is reached
	if (angle == -40.0) {
		direction = 1;
	}
	if (angle == 40.0) {
		direction = 0;
	}
}

void make_bar() {

	glNewList(BAR, GL_COMPILE);

		// Draw the pole
		glBegin(GL_QUADS);

			glColor3f(1.0, 1.0, 0.0);

			// Bottom
			glVertex3d(-50, 30, -1);
			glVertex3d(50, 30, -1);
			glVertex3d(50, 30, 1);
			glVertex3d(-50, 30, 1);
			// Top
			glVertex3d(-50, 33, -1);
			glVertex3d(50, 33, -1);
			glVertex3d(50, 33, 1);
			glVertex3d(-50, 33, 1);

			glColor3f(1.0, 0.65, 0.0);

			// Front
			glVertex3d(-50, 33, 1);
			glVertex3d(-50, 30, 1);
			glVertex3d(50, 30, 1);
			glVertex3d(50, 33, 1);
			// Back
			glVertex3d(50, 33, -1);
			glVertex3d(50, 30, -1);
			glVertex3d(-50, 30, -1);
			glVertex3d(-50, 33, -1);

			glColor3f(1.0, 0.0, 0.0);

			// Left
			glVertex3d(-50, 33, -1);
			glVertex3d(-50, 30, -1);
			glVertex3d(-50, 30, 1);
			glVertex3d(-50, 33, 1);
			// Right
			glVertex3d(50, 33, 1);
			glVertex3d(50, 30, 1);
			glVertex3d(50, 30, -1);
			glVertex3d(50, 33, -1);
		glEnd();

	glEndList();
}

void make_swing() {

	glNewList(SWING, GL_COMPILE);

		glColor3f(1.0, 0.0, 1.0);
		glLineWidth(5.0);
	
		// Draw the ropes
		glBegin(GL_LINES);
			glVertex3d(-20, 30, 0);
			glVertex3d(-20, -30, 0);
			glVertex3d(20, 30, 0);
			glVertex3d(20, -30, 0);
		glEnd();

		glColor3f(0.0, 1.0, 0.0);
	
		// Draw the sides of the seat
		glBegin(GL_QUAD_STRIP);
			glVertex3d(20, -30, -10);
			glVertex3d(20, -33, -10);
			glVertex3d(-20, -30, -10);
			glVertex3d(-20, -33, -10);
			glVertex3d(-20, -30, 10);
			glVertex3d(-20, -33, 10);
			glVertex3d(20, -30, 10);
			glVertex3d(20, -33, 10);
			glVertex3d(20, -30, -10);
			glVertex3d(20, -33, -10);
		glEnd();
	
		glColor3f(0.0, 0.0, 1.0);
	
		// Draw the top and bottom of the seat
		glBegin(GL_QUADS);
			glVertex3d(-20, -33, -10);
			glVertex3d(20, -33, -10);
			glVertex3d(20, -33, 10);
			glVertex3d(-20, -33, 10);
	
			glVertex3d(-20, -30, -10);
			glVertex3d(20, -30, -10);
			glVertex3d(20, -30, 10);
			glVertex3d(-20, -30, 10);
		glEnd();
	
	glEndList();
}

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

	// 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 | TK_DOUBLE);

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

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

	// Set the shading model
	glShadeModel(GL_FLAT);

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

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

	// Create the display lists
	make_bar();
	make_swing();

	angle = -39.0;
	direction = 1;

	// 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);

	// Assign idle() to be the function called whenever there 
	// are no events
	tkIdleFunc(draw);

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