#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <iostream>
using namespace std;

//angle of rotation
float xpos = 5, ypos = -15, zpos = 25, xrot = 0, yrot = 0;
float objxrot = 0, objyrot = 0;
float iinc = 0;
float quadx = 0, quady = 0, quadz = 0;
float quadR = 1, quadG = 1, quadB = 1;

bool flock = false;


const int ParticleCount = 5000;

float radius = 5.0f; // our radius distance

float lastx, lasty;


struct quadPoints {
	
	float qp1x, qp1y, qp1z;
	float qp2x, qp2y, qp2z;
	float qp3x, qp3y, qp3z;
	float qp4x, qp4y, qp4z;
	
	float quadR;
	float quadG;
	float quadB;
	
	quadPoints(float x, float y, float z){
		
		qp1x = x;
		qp1y = y;
		qp1z = z;
		
		qp2x = (float)(rand()%100)/10;
		qp2y = (float)(rand()%100)/10;
		qp2z = (float)(rand()%100)/10;
		
		qp3x = (float)(rand()%100)/10;
		qp3y = (float)(rand()%100)/10;
		qp3z = (float)(rand()%100)/10;
		
		qp4x = (float)(rand()%100)/10;
		qp4y = (float)(rand()%100)/10;
		qp4z = (float)(rand()%100)/10;
		
	
	}
	
	quadPoints(float x, float y, float z, float R, float G, float B){
		
		qp1x = x;
		qp1y = y;
		qp1z = z;
		
		qp2x = (float)(rand()%100)/10;
		qp2y = (float)(rand()%100)/10;
		qp2z = (float)(rand()%100)/10;
		
		qp3x = (float)(rand()%100)/10;
		qp3y = (float)(rand()%100)/10;
		qp3z = (float)(rand()%100)/10;
		
		qp4x = (float)(rand()%100)/10;
		qp4y = (float)(rand()%100)/10;
		qp4z = (float)(rand()%100)/10;
		
		quadR = R;
		quadG = G;
		quadB = B;
		
	}
	
	quadPoints(float x, float y, float z, float x2, float y2, float z2, float R, float G, float B){
		
		qp1x = x;
		qp1y = y;
		qp1z = z;
		
		qp2x = x2;
		qp2y = y2;
		qp2z = z2;
		
		qp3x = (float)(rand()%100)/10;
		qp3y = (float)(rand()%100)/10;
		qp3z = (float)(rand()%100)/10;
		
		qp4x = (float)(rand()%100)/10;
		qp4y = (float)(rand()%100)/10;
		qp4z = (float)(rand()%100)/10;
		
		quadR = R;
		quadG = G;
		quadB = B;
		
	}
	
};

struct xyzPoint{
	
	float x, y, z;
	
};


typedef struct
	{
		double Xpos;
		double Ypos;
		double Zpos;
		double Xmov;
		double Ymov;
		double Zmov;
		double Direction;
		double Acceleration;
		double Deceleration;
		double Scalez;
		bool Visible;
	}PARTICLES;


PARTICLES Particle[ParticleCount];
//vector<PARTICLES> Particle;
vector<quadPoints> quadPos;
xyzPoint lastQuadPoint;
xyzPoint nextLastQuadPoint;

void glCreateParticles (void) {

	for (int i = 1; i < ParticleCount; i++){
		
		Particle[i].Xpos = 0;
		Particle[i].Ypos = -5;
		Particle[i].Zpos = -5;
		Particle[i].Xmov = rand()%11;
		Particle[i].Ymov = rand()%11;
		Particle[i].Zmov = rand()%11;
		Particle[i].Scalez = 0.25;
		Particle[i].Direction = 0;
		Particle[i].Acceleration = (rand()%11)  * 0.1;
		Particle[i].Deceleration = 0.025;
	}
}

void glUpdateParticles (void) {

	for (int i = 1; i < ParticleCount; i++){
		
		Particle[i].Ypos = Particle[i].Ypos + Particle[i].Acceleration - Particle[i].Deceleration;
		Particle[i].Deceleration = Particle[i].Deceleration + 0.025;
		
		Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov;
		Particle[i].Ypos = Particle[i].Ypos + Particle[i].Ymov;
		Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov;
		
		Particle[i].Direction = Particle[i].Direction + rand()%11;
		
		if (Particle[i].Ypos < -5)
		{
			Particle[i].Xpos = 0;
			Particle[i].Ypos = -5;
			Particle[i].Zpos = -5;
			Particle[i].Direction = 0;
			Particle[i].Acceleration = rand()%11 * 0.02;
			Particle[i].Deceleration = 0.0025;
		}
		
	}
}

void quad (quadPoints qp) {

	glPushMatrix();

	//object rotation
	glRotatef(objxrot,1.0,0.0,0.0);
	glRotatef(objyrot,0.0,1.0,0.0);  

	glColor4f(qp.quadR, qp.quadG, qp.quadB, 0.5f);
	
	glBegin(GL_QUADS);
		glVertex3f(qp.qp1x, qp.qp1y, qp.qp1z);
		glVertex3f(qp.qp2x, qp.qp2y, qp.qp2z);
		glVertex3f(qp.qp3x, qp.qp3y, qp.qp3z);
		glVertex3f(qp.qp4x, qp.qp4y, qp.qp4z);
	glEnd();
	
	nextLastQuadPoint.x = qp.qp3x;
	nextLastQuadPoint.y = qp.qp3y;
	nextLastQuadPoint.z = qp.qp3z;
	
	lastQuadPoint.x = qp.qp4x;
	lastQuadPoint.y = qp.qp4y;
	lastQuadPoint.z = qp.qp4z;
	
	glPopMatrix();
	
}

void addQuad(){

	float R = (float)(rand()%100)/100;
	float G = (float)(rand()%100)/100;
	float B = (float)(rand()%100)/100;
	
	quadPoints q(lastQuadPoint.x, lastQuadPoint.y, lastQuadPoint.z, nextLastQuadPoint.x, nextLastQuadPoint.y, nextLastQuadPoint.z, R, G, B);
	quadPos.push_back(q);
	
	cout << lastQuadPoint.x << ", " << lastQuadPoint.y << ", " << lastQuadPoint.z << "\n";
	
}

void subQuad(){
	
	if(quadPos.size() > 0){
		quadPos.pop_back();
	}	
	
}

void glDrawParticles (void) {

	for (int i = 1; i < ParticleCount; i++){
		
		glPushMatrix();
		
		glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos);
		glRotatef (Particle[i].Direction - 90, 0, 0, 1);
		glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez);
		
		// draw quads
		for(int i = 0; i < quadPos.size(); i++){
			quad(quadPos[i]);
		}
		
		glPopMatrix();
		
		
		
	}
}


void enable (void) {
	glEnable (GL_DEPTH_TEST); //enable the depth testing
	glEnable (GL_COLOR_MATERIAL);
	glShadeModel (GL_SMOOTH); //set the shader to smooth shader
}

void display (void) {
	glClearColor (1.0,1.0,1.0,1.0); //clear the screen to black
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear the color buffer and the depth buffer
	glEnable (GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //set the blend 
	enable();
    
	// camera translation and rotations
	glLoadIdentity(); 
	glTranslatef(0.0f, 0.0f, -radius);
	glRotatef(xrot,1.0,0.0,0.0);
	glRotatef(yrot,0.0,1.0,0.0);  //rotate our camera on the y-axis (up and down)
	glTranslated(-xpos,0.0f,-zpos); //translate the screen to the position of our camera
	
	int addorsub = rand()%101;
	
	if (!flock){
		for(int i = 0; i < quadPos.size(); i++){
			quad(quadPos[i]);
		}
	}
	
	if (flock){
		if (addorsub > 50){
			addQuad();
		}
		else{
			subQuad();
		}
		glUpdateParticles();
		glDrawParticles();
	}
	
	glutSwapBuffers(); //swap the buffers
	
}

void reshape (int w, int h) {
	glViewport (0, 0, (GLsizei)w, (GLsizei)h); //set the viewport to the current window specifications
	glMatrixMode (GL_PROJECTION); //set the matrix to projection
	glLoadIdentity ();
	gluPerspective (60, (GLfloat)w / (GLfloat)h, 0.1, 100.0); //set the perspective (angle of sight, width, height, , depth)
	glMatrixMode (GL_MODELVIEW); //set the matrix back to model
}

void keyboard (unsigned char key, int x, int y) {
	
	if (key=='m')
	{
		//explode?
	}
	
	if (key=='r')
	{
		// add to drawing
		float R = (float)(rand()%100)/100;
		float G = (float)(rand()%100)/100;
		float B = (float)(rand()%100)/100;
		
		quadPoints q(lastQuadPoint.x, lastQuadPoint.y, lastQuadPoint.z, nextLastQuadPoint.x, nextLastQuadPoint.y, nextLastQuadPoint.z, R, G, B);
		quadPos.push_back(q);
		
		//cout << lastQuadPoint.x << ", " << lastQuadPoint.y << ", " << lastQuadPoint.z << "\n";
	}
	
	if (key=='t')
	{
		if(quadPos.size() > 0){
			quadPos.pop_back();
		}
	}
	
	if (key=='p')
	{
		sleep(10);
	}
	
	if (key=='q')
	{
		xrot += 1;
		if (xrot >360) xrot -= 360;
	}
	
	if (key=='z')
	{
		xrot -= 1;
		if (xrot < -360) xrot += 360;
	}
	
	if (key=='w')
	{
		float xrotrad, yrotrad;
		yrotrad = (yrot / 180 * 3.141592654f);
		xrotrad = (xrot / 180 * 3.141592654f); 
		xpos += float(sin(yrotrad));
		zpos -= float(cos(yrotrad));
		ypos -= float(sin(xrotrad));
	}
	
	if (key=='s')
	{
		float xrotrad, yrotrad;
		yrotrad = (yrot / 180 * 3.141592654f);
		xrotrad = (xrot / 180 * 3.141592654f); 
		xpos -= float(sin(yrotrad));
		zpos += float(cos(yrotrad));
		ypos += float(sin(xrotrad));
	}
	
	if (key=='d')
	{
		yrot += 1;
		if (yrot > 360) yrot -= 360;
	}
	
	if (key=='a')
	{
		yrot -= 1;
		if (yrot < -360) yrot += 360;
	}
	
	if (key=='i')
	{
		objxrot += 2;
		if (objxrot >360) objxrot -= 360;
	}
	if (key=='k')
	{
		objxrot -= 2;
		if (objxrot < -360) objxrot += 360;
	}
	if (key=='j')
	{
		objyrot -= 2;
		if (objyrot < -360) objyrot += 360;
	}
	if (key=='l')
	{
		objyrot += 2;
		if (objyrot > 360) objyrot -= 360;
	}
	
	if (key=='o')
	{
		if(!flock){
			flock = true;
		}
	}
	
	if (key=='m')
	{
		if(flock){
			flock = false;
		}
	}
	
	if (key==27)
	{
		exit(0);
	}
}

void mouseMovement(int x, int y) {

}

void init(){
	
	float x = (float)(rand()%100)/10;
	float y = (float)(rand()%100)/10;
	float z = (float)(rand()%100)/10;
	
	quadPoints q(x, y, z);
	quadPos.push_back(q);
	
	lastQuadPoint.x = x;
	lastQuadPoint.y = y;
	lastQuadPoint.z = z;
	
	glCreateParticles();

	addQuad();
	
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); 

	glutInitWindowSize (1000, 7100); 
	glutInitWindowPosition (100, 100);
    glutCreateWindow ("quadder"); 
	
	init();
	
    glutDisplayFunc (display); 
	glutIdleFunc (display); 
	glutReshapeFunc (reshape); 
	
	glutPassiveMotionFunc(mouseMovement); //check for mouse movement
	
	glutKeyboardFunc (keyboard); 
    glutMainLoop (); 
    return 0;
}
