#include <ri.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2

#define M_PI 3.14159265

bool shadow = false;

void printColor( float r, float g, float b ) {
	printf( "\tColor [%f %f %f]\n", r, g, b );
}

void printSurface( char* surface ) {
	printf( "\tSurface %s\n", surface );
}

void printOpacity( float opacity ) {
	printf( "\tOpacity [%f %f %f]\n", opacity, opacity, opacity );
}

void printTranslate( float x, float y, float z ) {
	printf( "\tTranslate %f %f %f\n", x, y, z );
}

void printScale( float scale ) {
	printf( "\tScale %f %f %f\n", scale, scale, scale );
}

void printRotate( int axis, int degrees ) {
	if ( axis == X_AXIS ) {
		printf( "\tRotate %d 1 0 0\n", degrees );
	} else if ( axis == Y_AXIS ) {
		printf( "\tRotate %d 0 1 0\n", degrees );
	} else if ( axis == Z_AXIS ) {
		printf( "\tRotate %d 0 0 1\n", degrees );
	}
}

void printRandomRotate( int degrees ) {
	if ( rand() % 2 == 0 ) {
		degrees = -degrees;
	}
	int random = rand() % 3;
	if ( random == 0 ) {
		printf( "\tRotate %d 1 0 0\n", degrees );
	} else if ( random == 1 ) {
		printf( "\tRotate %d 0 1 0\n", degrees );
	} else {
		printf( "\tRotate %d 0 0 1\n", degrees );
	}
}

void printRootRotate( int degrees ) {
	if ( rand() % 2 == 0 ) {
		degrees = -degrees;
	}
	int random = rand() % 3;
	if ( random == 0 ) {
		printf( "\tRotate %d 1 0 0\n", degrees );
	} else if ( random == 1 ) {
		printf( "\tRotate %d 0 1 0\n", degrees );
	} else {
		printf( "\tRotate %d 0 0 1\n", degrees );
	}
}

void printAttribBegin() {
	printf( "AttributeBegin\n" );
}

void printAttribEnd() {
	printf( "AttributeEnd\n\n" );
}

void sphere( float r, float g, float b, float x, float y, float z, char* surface, float radius, float opacity, float scale, float zmin, float zmax ) {
	printAttribBegin();
		printOpacity( opacity );
		printTranslate( x, y, z );
		printScale( scale );
		printColor( r, g, b );
		printSurface( surface );
		// always draw the whole sphere
		printf( "\tSphere %f %f %f 360\n", radius, zmin, zmax );
	printAttribEnd();
}

void cylinder( float radius, float zmin, float zmax, float sweep ) {
	printf( "\tCylinder %f %f %f %f\n", radius, zmin, zmax, sweep );
}

void limb( float radius, float length ) {
	cylinder( radius, 0, length, 360 );
}

void cone( float height, float radius, float sweep ) {
	printf( "\tCone %f %f %f\n", height, radius, sweep );
}

void leaf() {
	if ( rand() % 2 != 0 ) {
		float random = ( float ) rand() / RAND_MAX;
		if ( random < 0.5 ) {
			random += 0.5;
		}
		float radius = 0.05f;
		printAttribBegin();
		printf( "Displacement \"dented\" \"Km\" 5 \"frequency\" 0.1" );
		printf( "ShadingRate 20" );
		sphere( 0.2f, 0.9f * random, 0.1f, 0.0f, 0.0f, 0.0f, "\"plastic\"", radius, 0.9f, 1.0f, -radius, radius );
		printAttribEnd();
	}
}

void polygon( float r, float g, float b, float x, float y, float z, int* coords, int numCoords, char* surface, float opacity ) {
	printAttribBegin();
		printOpacity( opacity );
		printTranslate( x, y, z );
		printColor( r, g, b );
		printSurface( surface );
		printf( "\tPolygon \"P\" [ " );
		for ( int i = 0; i < numCoords; i++ ) {
			printf( "%d ", *( coords + i ) );
		}
		printf( "]\n" );
	printAttribEnd();
}

void init( void ) {
	if ( !shadow ) {
		printf( "Option \"searchpath\" \"shader\" [\".:../shaders:&\"]\n" );
		printf( "Display \"part3.tif\" \"windows\" \"rgb\" \"compression\" \"none\"\n" );
		printf( "Format 800 600 -1\n" );
		printf( "PixelSamples 4 4\n" );
		printf( "Projection \"perspective\" \"fov\" 45\n" );
		printf( "Translate 0 -4 16\n" );
		printf( "Rotate -120 1 0 0\n\n" );
		//printf( "DepthOfField  2.3 0.100 5.5\n" );
		printf( "WorldBegin\n\n" );
		printf( "LightSource \"ambientlight\" 1 \"intensity\" 0.5\n\n" );
		//printf( "LightSource \"spotlight\" 2 \"from\" [5 4 10] \"to\" [0 0 0] \"intensity\" 100\n" );
		printf( "LightSource \"distantlight\" 2 \"from\" [0 1 4] \"to\" [0 0 0] \"intensity\" 0.8\n" );

	} else {
		printf( "Option \"searchpath\" \"shader\" [\".:../shaders:&\"]\n" );
		printf( "Display \"part3z.tiff\" \"file\" \"z\"\n" );
		printf( "Format 512 512 1.0\n" );
		printf( "Projection \"perspective\" \"fov\" 50\n" );
		printf( "Translate 5 4 10\n" );
		int rotateX = ( int ) ( atan( 4.0f / 10.0f ) * 180.0f / M_PI );
		int rotateY = ( int ) ( atan( 5.0f / 10.0f ) * 180.0f / M_PI );
		int rotateZ = ( int ) ( atan( 5.0f / 4.0f ) * 180.0f / M_PI );
		printRotate( X_AXIS, rotateX );
		printRotate( Y_AXIS, rotateY );
		printRotate( Z_AXIS, -rotateZ );
		printf( "WorldBegin\n\n" );
	}
}

void end( void ) {
	printf( "WorldEnd\n" );

	if ( shadow ) {
		printf( "MakeShadow \"part3z.tiff\" \"map.shad\"" );
	}
}

void drawTree( float radius, float length, float oldLength, int depth ) {
	printf( "TransformBegin\n" );

	// Translate to end of old one
	printTranslate( 0, 0, oldLength );

	// Rotate some random amount
	float random = ( float ) rand() / RAND_MAX;
	int degrees = ( int ) ( random * 45 );
	printRandomRotate( degrees );

	// Draw this limb
	limb(radius, length );

	// Check if it's the last time
	if ( depth > 0 ) {
		int newLimbs = ( rand() % 3 ) + 1;
		for ( int i = 0; i < newLimbs; i++ ) {
			// Get our new limb radius
			float random = ( float ) rand() / RAND_MAX;
			if ( random < 0.5 ) {
				random += 0.5;
			}
			float newRadius = radius * random;

			// Get our new limb length
			random = ( float ) rand() / RAND_MAX;
			if ( random < 0.5 ) {
				random += 0.5;
			}

			float newLength = length * random;

			if ( newRadius > 0.0 ) {
				drawTree( newRadius, newLength, length, depth - 1 );
			}
		}
	} else {
		// we're done.  draw a "leaf"
		printTranslate( 0, 0, oldLength );
		leaf();
	}
	printf( "TransformEnd\n" );
}

void drawRoot( float radius, float length, float oldLength, int depth ) {
	printf( "TransformBegin\n" );

	// Translate to end of old one
	printTranslate( 0, 0, oldLength );

	// Rotate some random amount
	float random = ( float ) rand() / RAND_MAX;
	int degrees = ( int ) ( random * 15 );
	printRootRotate( degrees );

	// Draw this limb
	limb(radius, length );

	// Check if it's the last time
	if ( depth > 0 ) {
		int newLimbs = ( rand() % 2 ) + 1;
		for ( int i = 0; i < newLimbs; i++ ) {
			// Get our new limb radius
			float random = ( float ) rand() / RAND_MAX;
			if ( random < 0.2 ) {
				random += 0.1f;
			}
			float newRadius = radius * random;

			// Get our new limb length
			random = ( float ) rand() / RAND_MAX;
			if ( random < 0.2 ) {
				random += 0.5;
			}

			float newLength = length * random;

			if ( newRadius > 0.0f ) {
				drawRoot( newRadius, newLength, length, depth - 1 );
			}
		}
	}
	printf( "TransformEnd\n" );
}

int main( int argc, char** argv )
{
	// Seed out random number generators so we get controlled randomness
	srand( 10000 );
	init();

	// Draw our bg
	float radius = 30.0f;
	sphere( 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, "\"KMPuffyclouds\"", radius, 1.0f, 1.0f, -radius, radius );

	// Draw our world
	radius = 2.0f;
	printf( "Displacement \"KMWindyWave\"" );
	sphere( 0.5f, 0.25f, 0.0f, 0.0f, 0.0f, 0.0f, "\"plastic\"", radius, 1.0f, 1.0f, -radius, radius );
	
	// Start our recursive tree call
	printf( "TransformBegin\n" );
	printAttribBegin();
	printTranslate( 0, 0, radius ); // start our tree at the top of the ball
	printf( "Displacement \"dented\"" );
	printSurface( "\"wood2\"" );
	drawTree( 0.15f, 1.75f, 0.0f, 14 );
	// 14
	printAttribEnd();
	printf( "TransformEnd\n" );

	// Add our base
	printAttribBegin();
	printTranslate( 0, 0, radius ); // add it at the base of the tree
	printf( "Displacement \"dented\"" );
	printSurface( "\"wood2\"" );
	cone( 0.8f, 0.25, 360 );
	printAttribEnd();

	// Add our roots
	printAttribBegin();
	int degrees = ( int ) ( ( float ) rand() / RAND_MAX * 360 );
	for ( int i = 0; i < 7; i++ ) {
		printf( "TransformBegin\n" );
		printTranslate( 0, 0, radius ); // add it at the base of the tree
		// get random number to rotate around
		degrees += ( int ) ( ( float ) rand() / RAND_MAX * 360 );
		printRotate( Z_AXIS, degrees );
		printRotate( X_AXIS, -90 );// Rotate to point out
		printf( "Displacement \"dented\"" );
		printSurface( "\"wood2\"" );
		drawRoot( 0.125f, 0.3f, 0.0f, 10 );
		printf( "TransformEnd\n" );
	}
	printAttribEnd();

	// Add our grass
	// Make our curve
	printColor( 0.2f, 0.85f, 0.1f );
	char* coords1 = "0 0 0 -0.05 0 0.15 0 0 0.3 0.05 0 0.4";
	char* coords2 = "0 0 0 0.05 0 0.05 0.1 0 0.1 0.2 0 0.1";
	char* coords3 = "0 0 0 0.1 0 0.2 0.15 0 0.25 0.2 0 0.2";
	for ( int i = 0; i < 4000; i++ ) {
		printf( "TransformBegin\n" );
		int degrees = ( int ) ( ( float ) rand() / RAND_MAX * 360 );
		printRandomRotate( degrees );// Rotate to point out
		printRandomRotate( degrees );
		printRandomRotate( degrees );
		printRandomRotate( degrees );
		printTranslate( 0.0f, 0.0f, radius );
		float randomX = ( float ) rand() / RAND_MAX;
		float randomY = ( float ) rand() / RAND_MAX;
		float randomZ = ( float ) rand() / RAND_MAX;
		printf( "Scale %f %f %f", randomX, randomY, randomZ );
		printf( "Basis \"bezier\" 3 \"bezier\" 3" );
		int random = rand() % 3;
		if ( random == 0 ) {
			printf( "Curves \"cubic\" [4] \"nonperiodic\" \"P\" [ %s ] \"constantwidth\" [0.03]", coords1 );
		} else if ( random == 1 ) {
			printf( "Curves \"cubic\" [4] \"nonperiodic\" \"P\" [ %s ] \"constantwidth\" [0.03]", coords2 );
		} else {
			printf( "Curves \"cubic\" [4] \"nonperiodic\" \"P\" [ %s ] \"constantwidth\" [0.03]", coords3 );
		}
		printf( "TransformEnd\n" );
	}

	// Goodbye cruel world!
	end();
}
