#include <GL/glut.h>

float r, g, b;

/* this is the depth (the steps of recursion)
 * the variable is set through the first param of the
 * command line.
 */

int i=0;

/* the init function initializes all of the tools
 * used to draw (color, the background color),
 * as well as setting the modes for the renderer 
 */

void myinit(void) {
	
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glColor3f(0.0, 0.0, 1.0);
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, 1280.0, 0.0, 1024.0);
	glMatrixMode(GL_MODELVIEW);
}

/* main display function;
 * this function will call the recursive point
 * generator for the koch snowflake, named
 * "snowflake()"  snowflake will do all of the
 * drawing, and eat up a tremendous ammount
 * of memory by saving each of its recursive steps
 */

int snowflake(int iterations, int level, GLfloat Xx, GLfloat Xy, GLfloat Yx, GLfloat Yy) {
	
	/* the snowflake function takes 6 variables; iterations and level are to keep track
	* of which recursive step we are in and lets us stop the loop.
	* the rest of the 4 varibles are GLfloat coordinates for 2 points that (previously)
	* make up the line we are performing the koch snowflake iteration on.
	*/
	
	GLfloat Ax;
	GLfloat Ay;
	GLfloat Bx;
	GLfloat By;
	GLfloat Cx;
	GLfloat Cy;
	
	double change_in_x = (Yx - Xx)/3.0;
	double change_in_y = (Yy - Xy)/3.0;
	
	/* declared 3 "points" vertex by vertex, and two double variables that
	* describe the change in x and the change in Y between the two points.
	* these values are useful in order to calculate where the two points we
	* need to create on the line are
	*/
	
	Ax = Xx + change_in_x;
	Ay = Xy + change_in_y;
	Bx = Xx + change_in_x*2;
	By = Xy + change_in_y*2;
	
	/* in my experience, rather than having the 2nd point "b" subtracted from the
	* second (higher) initial point "Y", its safer to multiply it.
	*/
	
	Cx = Ax + change_in_x/2 - 0.866025 * change_in_y;
	Cy = Ay + change_in_y/2 + 0.866025 * change_in_x;
	
	/* finding the point C off the triangle was the difficult part of the assignment
	* it involved the most mathematical thought and created the most pain.
	* basically, setting the coordinates to the value of B rotated around A 60 degrees
	* (I built in the values because the precision was enough.. 1/2 for cos(60) and .8...
	* for sqrt(3)/2 or sin(60)... having the code perform the square root of 3 1 million
	* times just seemed stupid when I could substitute 6 digit accurate numbers
	*/
	
	if(iterations == level) {
	/* if the iteration is == to the level, then the end of the 
	* line has come, and its time to draw.  I threw in a simple color
	* modification scheme with 3 global floats to make things interesting;
	* what they do is pretty self explanatory
	*/
		if(r < .99) { r+=.01; }
			else r=0.0;
		if(g < .98) { g+=.02; }
			else g=0.0;
		if(b < .97) { b+=.03; }
			else b=0.0;
		glColor3f(r, g, b);
		glBegin(GL_LINES);
			glVertex2f(Xx, Xy);
			glVertex2f(Yx, Yy);
		glEnd();
		}
	/* if we aren't done, we run snowflake on each "line segment" that we created
	* by generating the 4 points; X to A, X to C, C to B, and B to X
	*/

	else {
		snowflake(iterations, level+1, Xx, Xy, Ax, Ay);
		snowflake(iterations, level+1, Ax, Ay, Cx, Cy);
		snowflake(iterations, level+1, Cx, Cy, Bx, By);
		snowflake(iterations, level+1, Bx, By, Yx, Yy);
	}
	
	return 0;	 
}


void display () {

	// snowflake takes 2 points as 4 seperate variables
	// as arguments, and a level and depth "variable"
	// these variables make up the triangle
	GLfloat Xx = 200.0;
	GLfloat Xy = 300.0;
	GLfloat Yx = 600.0;
	GLfloat Yy = 986.8;
	GLfloat Zx = 1000.0;
	GLfloat Zy = 300.0;

	/* these coordinates were a pain to get, and the innacuracy of them caused
	* the program to seemingly "not work" for a good while
	*/

	glClear(GL_COLOR_BUFFER_BIT);

	/* run snowflake on all 3 line segments; X to Y, Y to Z, and Z back to X. */

	snowflake(i, 0, Xx, Xy, Yx, Yy);
	snowflake(i, 0, Yx, Yy, Zx, Zy);
	snowflake(i, 0, Zx, Zy, Xx, Xy);

	/* at this point our raster has been filled and we can
	* put it on the screen
	*/

	glFlush();

}

int main(int argc, char ** argv) {
	/* this simple line lets me control the number of iterations from the command line
	* it defaults to 0
	*/
	if(argc > 1) i=atoi(argv[1]);

	/* all of the glut window initializations */

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(0,0);
	glutCreateWindow("Koch's Multicolored Snowflake");
	glutDisplayFunc(display);

	myinit();
	glutMainLoop();

	return 0;
}