#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

struct Vertex {
	float x;
	float y;
	float z;
};

float xRotate = 0;
float yRotate = 0;
float zRotate = 0;

int choice;
float radius;
struct Vertex p1, p2, p3, p4;

void myInit () {
	
	glClearColor (1.0, 1.0, 1.0, 1.0);
	
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity();
	glOrtho (-50.0, 50.0, -50.0, 50.0, -50.0, 50.0);
	glMatrixMode (GL_MODELVIEW);
}

void drawQuad (struct Vertex p1, struct Vertex p2, struct Vertex p3, struct Vertex p4) {

	/* It is assumed that user inputs only a 2D point. We extrude along the z-axis.
	So the argument points here must have their z-coordinate set to 0. */
	
	/* first plot the quads */
	
	glBegin (GL_LINE_LOOP);
		glVertex3f (p1.x, p1.y, p1.z);
		glVertex3f (p2.x, p2.y, p2.z);
		glVertex3f (p3.x, p3.y, p3.z);
		glVertex3f (p4.x, p4.y, p4.z);
		
		glVertex3f (p1.x, p1.y, p1.z+10);
		glVertex3f (p2.x, p2.y, p2.z+10);
		glVertex3f (p3.x, p3.y, p3.z+10);
		glVertex3f (p4.x, p4.y, p4.z+10);
	glEnd();
	
	/* Now we need to show the extruding of the quad. We can do so
	by drawing the polygons corresponding to the side surface. */
	glBegin (GL_QUADS);
		glVertex3f (p1.x, p1.y, p1.z);
		glVertex3f (p2.x, p2.y, p2.z);
		glVertex3f (p2.x, p2.y, p2.z+10);
		glVertex3f (p1.x, p1.y, p1.z+10);
		
		
		glVertex3f (p2.x, p2.y, p2.z);
		glVertex3f (p3.x, p3.y, p3.z);
		glVertex3f (p3.x, p3.y, p3.z+10);
		glVertex3f (p2.x, p2.y, p2.z+10);
		
		glVertex3f (p3.x, p3.y, p3.z);
		glVertex3f (p4.x, p4.y, p4.z);
		glVertex3f (p4.x, p4.y, p4.z+10);
		glVertex3f (p3.x, p3.y, p3.z+10);
		
		glVertex3f (p4.x, p4.y, p4.z);
		glVertex3f (p1.x, p1.y, p1.z);
		glVertex3f (p1.x, p1.y, p1.z+10);
		glVertex3f (p4.x, p4.y, p4.z+10);
	glEnd();
}

void drawCircle (float r, float xCenter, float yCenter) {
	
	float x, y;
	int i;
	int noOfPoints;
	
	struct Vertex *originalSet, *extrudedSet;
	
	glTranslatef (xCenter, yCenter, 0.0);
	
	/* generate the set of initial points */
	noOfPoints = (r*800);
	noOfPoints += 10; /* little extra malloced memory */
	originalSet = (struct Vertex *) malloc (sizeof (struct Vertex)*noOfPoints);
	extrudedSet = (struct Vertex *) malloc (sizeof (struct Vertex)*noOfPoints);
	
	/* upper semicircle */
	for (x = -r, i = 0; x <= r; x += (r/200.0), i++) {
		y = sqrt ((r*r)-(x*x));
		if (y < 0) {
			perror ("sqrt()");
			abort();
		}
		originalSet[i].x = x;
		originalSet[i].y = y;
		originalSet[i].z = 0;
	}
	/* lower semicircle */
	for (x = r; x >= -r; x -= (r/200.0), i++) {
		y = sqrt ((r*r)-(x*x));
		if (y < 0) {
			perror ("sqrt()");
			abort();
		}
		y = -y;
		originalSet[i].x = x;
		originalSet[i].y = y;
		originalSet[i].z = 0;
	}
	noOfPoints = i;
	
	/* now calculate the extrudedSet. Lets extrude
	along the z-axis, for 10 units. */
	for (i = 0; i < noOfPoints; i++) {
		extrudedSet[i].x = originalSet[i].x;
		extrudedSet[i].y = originalSet[i].y;
		extrudedSet[i].z = originalSet[i].z + 10.0;
	}
	
	/* lets draw lines between the corresponding points */
	glBegin (GL_LINES);
	for (i = 0; i < noOfPoints; i++) {
		glVertex3f (extrudedSet[i].x, extrudedSet[i].y, extrudedSet[i].z);
		glVertex3f (originalSet[i].x, originalSet[i].y, originalSet[i].z);
	}
	glEnd();
	/* lets draw the two circles too */
	glBegin (GL_LINE_STRIP);
	for (i = 0; i < noOfPoints; i++)
		glVertex3f (extrudedSet[i].x, extrudedSet[i].y, extrudedSet[i].z);
	for (i = 0; i < noOfPoints; i++)
		glVertex3f (originalSet[i].x, originalSet[i].y, originalSet[i].z);
	glEnd();
	
	glTranslatef (-xCenter, -yCenter, 0.0);
}

void display () {
	
	glClear (GL_COLOR_BUFFER_BIT);
	glColor3f (0.0, 0.0, 0.0);

	glPushMatrix();
	glRotatef (xRotate, 1.0, 0.0, 0.0);
	glRotatef (yRotate, 0.0, 1.0, 0.0);
	glRotatef (zRotate, 0.0, 0.0, 1.0);
	
	
	/* draw circle */
	if (choice == 1)
		drawCircle (radius, p1.x, p1.y);
	else
		/* draw quad */
		drawQuad (p1, p2, p3, p4);
	glPopMatrix();
	glFlush();
}

void keyReact (unsigned char key, int x, int y) {
	
	switch (key) {
		
		case 'x':
			xRotate++; break;
		case 'y':
			yRotate++; break;
		case 'z':
			zRotate++; break;
		case 'X':
			xRotate--; break;
		case 'Y':
			yRotate--; break;
		case 'Z':
			zRotate--; break;
		case 'q':
		case 'Q':
			exit (0);
	}
	glutPostRedisplay();
	
	return;
}

int main (int argc, char *argv[]) {
	
	printf ("1. Circle\n2. Quad\n");
	scanf ("%d", &choice);
	
	if (choice == 1) {
		printf ("Enter radius (0-40): ");
		scanf ("%f", &radius);
		printf ("Enter the center (x,y): ");
		scanf ("%f %f", &p1.x, &p1.y);
		p1.z = 0;
	}
	else {
		printf ("Enter p1 (x,y): ");
		scanf ("%f %f", &p1.x, &p1.y);
		printf ("Enter p2 (x,y): ");
		scanf ("%f %f", &p2.x, &p2.y);
		printf ("Enter p3 (x,y): ");
		scanf ("%f %f", &p3.x, &p3.y);
		printf ("Enter p4 (x,y): ");
		scanf ("%f %f", &p4.x, &p4.y);
		p1.z = p2.z = p3.z = p4.z = 0;
	}
	
	glutInit (&argc, argv);
	glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
	glutCreateWindow ("Extrude:");
	glutInitWindowSize (400, 400);
	glutInitWindowPosition (0, 0);
	myInit();
	glutKeyboardFunc (keyReact);
	glutDisplayFunc (display);
	glutMainLoop();
	
	return 0;
}

