#include "testApp.h"


//--------------------------------------------------------------
void testApp::setup(){	
	
	ofSetVerticalSync(true);
	ofSetFrameRate(60);
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f,0.0f,0.0f,0.5f);
	glClearDepth(1.0f);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	fogFilter = 0;
	
	setBoxSize(2000);
	
	screenCenter.set(ofGetWidth()/2, ofGetHeight()/2, 0);
	center.set(0,0,0);
	//center.set(1000, 1000, 1000);
	//screenCenter.set(center.x - ofGetWidth()/2,center.y - ofGetHeight()/2, 0);
	
	xAxisMin.set(-100,0,0);
	xAxisMax.set(100,0,0);
	yAxisMin.set(0,-100,0);
	yAxisMax.set(0,100,0);
	zAxisMin.set(0,0,-100);
	zAxisMax.set(0,0,100);
	
	xAxisMin += center;
	xAxisMax += center;
	yAxisMin += center;
	yAxisMax += center;
	zAxisMin += center;
	zAxisMax += center;

	mode = 1;
	
	rotateAmount.set(-10,-10,0);
	rotateSpeed.set(.3,.3,.3);
	
	for (int i = 0; i < 750; i++){
		particle myParticle;
		myParticle.setInitialCondition(ofRandom(xMin.x,xMax.x),ofRandom(yMin.y,yMax.y),ofRandom(zMin.z,zMax.z),0,0,0);
		particles.push_back(myParticle);
	}
	
	for(int i = 0; i < 5; i++){
		particle myForce;
		myForce.setInitialCondition(ofRandom(xMin.x, xMax.x),ofRandom(yMin.y, yMax.y),ofRandom(zMin.z, zMax.z),0,0,0);
		myForce.damping = .99;
		forces.push_back(myForce);
	}
	
	bShowAxis = true;
	bShowBounds = true;
	bBinning = true;
	bRotateX = false;
	bRotateY = false;
	bRotateZ = false;
	
	counter = 0;
	
	starImg1.loadImage("star.png");
	starTex1.allocate(256,256,GL_RGBA);
	starTex1.loadData(starImg1.getPixels(),256,256,GL_RGBA);
	
	z_angle = 0;
	zoom = 0;
	
}

//--------------------------------------------------------------
void testApp::update(){

	// on every frame 
	// we reset the forces
	// add in any forces on the particle
	// perfom damping and
	// then update
	
	if(bBinning){
		for (int i = 0; i < particles.size(); i++){
			computeBinPosition(particles[i].pos.x,particles[i].pos.y,particles[i].pos.z,&(particles[i].bitFlagX),&(particles[i].bitFlagY),&(particles[i].bitFlagZ));
		}
	}
	
	for(int i = 0; i < forces.size();i++){
		forces[i].resetForce();
	}
	
	for(int i = 0; i < forces.size();i++){
		forces[i].addAttractionForce(0,0,0,2000,.15);
		forces[i].addRepulsionForce(0,0,0,500,.4);
		for (int j = 0; j < i; j++){
			forces[i].addRepulsionForce(forces[j], 100, 1.5);
		}
	}
	
	for (int i = 0; i < particles.size(); i++){
		particles[i].resetForce();
	}
	
	int count = 0;
	
	for (int i = 0; i < particles.size(); i++){
		
		unsigned int bitFlagX_pta = particles[i].bitFlagX;
		unsigned int bitFlagY_pta = particles[i].bitFlagY;
		unsigned int bitFlagZ_pta = particles[i].bitFlagZ;
		
		switch(mode){
			case 0:
				
				
				
				for (int j = 0; j < i; j++){
					
					unsigned int bitFlagX_ptb = particles[j].bitFlagX;
					unsigned int bitFlagY_ptb = particles[j].bitFlagY;
					unsigned int bitFlagZ_ptb = particles[j].bitFlagZ;
					
					if(!bBinning || (bitFlagX_pta & bitFlagX_ptb) && (bitFlagY_pta && bitFlagY_ptb) && (bitFlagZ_pta & bitFlagZ_ptb)){
						count++;
						particles[i].addRepulsionForce(particles[j], 100, 0.50);
					}
				}
				
				
				break;
				
			case 1:
				//particles[i].addAttractionForce(center.x,center.y,center.z,900,0.35);
				//particles[i].addCounterClockwiseForceOnAxis(center.x, center.y, center.z, 100, 0, 0, 1);
				//particles[i].addRepulsionForceOnAxis(center.x,center.y,center.z,25,0,60,0);
				ofSetColor(255,255,255,255);
				particles[i].distanceToNearestConnection = 99999;
				for(int e = 0; e < forces.size(); e++){
					ofxVec3f diff	= particles[i].pos - forces[e].pos;
					float length	= diff.length();
					if(length <= particles[i].distanceToNearestConnection){
						particles[i].distanceToNearestConnection = length;
						particles[i].connectedTo = forces[e].pos;
					}    
					particles[i].addAttractionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,1000,.35f);
					//particles[i].addAttractionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,325,ofRandom(.5f,5.0f));
					//particles[i].addAttractionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,100,4.0f);
					particles[i].addAttractionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,30,12.0f);
					particles[i].addAttractionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,10,20.0f);
					particles[i].addRepulsionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,400,ofRandom(.5f,4.5f));
					//particles[i].addRepulsionForce(forces[e].pos.x,forces[e].pos.y,forces[e].pos.z,50,2.5f);
				}
				
				for (int j = 0; j < i; j++){
					
					unsigned int bitFlagX_ptb = particles[j].bitFlagX;
					unsigned int bitFlagY_ptb = particles[j].bitFlagY;
					unsigned int bitFlagZ_ptb = particles[j].bitFlagZ;
					
					if(!bBinning || (bitFlagX_pta & bitFlagX_ptb) && (bitFlagY_pta && bitFlagY_ptb) && (bitFlagZ_pta & bitFlagZ_ptb)){
						count++;
						particles[i].addRepulsionForce(particles[j], 30,10.0f);
						particles[i].addRepulsionForce(particles[j], ofRandom(10,200),log(ofRandom(0.05f,5.5f)));
						//particles[i].addAttractionForce(particles[j], ofRandom(50,100),log(ofRandom(0.05f,5.5f)));
					}					
				}
				break;
		}
	}
	cout << count << endl;
	
	for(int i = 0; i < forces.size();i++){
		forces[i].bounceOff3dBounds(xMin, xMax, yMin, yMax, zMin, zMax);
		forces[i].update();
		forces[i].addDampingForce();
	}
	
	for (int i = 0; i < particles.size(); i++){
		switch(mode){
			case 0:
				particles[i].bounceOff3dBounds(xMin, xMax, yMin, yMax, zMin, zMax);
				particles[i].update();
				break;
			case 1:
				particles[i].damping = 0.01f;
				particles[i].addDampingForce();
				particles[i].bounceOff3dBounds(xMin, xMax, yMin, yMax, zMin, zMax);
				particles[i].update();
				break;
		}
	}
	
	
	//deal with scene rotation
	if(bRotateX){
		rotateAmount.x += rotateSpeed.x;
	}
	if(bRotateY){
		rotateAmount.y += rotateSpeed.y;
	}
	if(bRotateZ){
		rotateAmount.z += rotateSpeed.z;
	}
	
	
	//then zoom
	z_angle += .005;
	zoom = 1000 * sin(z_angle);
	
	counter++;
}
//--------------------------------------------------------------
void testApp::setBoxSize(int width){
	xMax.set(width/2,0,0);
	xMin.set(-width/2,0,0);
	yMax.set(0,width/2,0);
	yMin.set(0,-width/2,0);
	zMax.set(0,0,width/2);
	zMin.set(0,0,-width/2);
	
	setupBins(width,width,width,15,15,15);
	
	xMax += center;
	xMin += center;
	yMax += center;
	yMin += center;
	zMax += center;
	zMin += center;
}


//--------------------------------------------------------------
void testApp::draw(){
	
	ofSetColor(0x000000);
	glClearColor(0,0,0,1.0f);
	glClear(GL_COLOR_BUFFER_BIT);
	/*
	glFogi(GL_FOG_MODE, GL_LINEAR);
	GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 0.3f};
	glFogfv(GL_FOG_COLOR, fogColor);
	glFogf(GL_FOG_DENSITY, 0.001f);
	glHint(GL_FOG_HINT, GL_NICEST);
	glFogf(GL_FOG_START,1.0f);
	glFogf(GL_FOG_END,30.0f);
	glEnable(GL_FOG);
	 */
	
	

	
	glPushMatrix();
	
	glTranslatef(0,0,zoom); //zoom out?
	
	glPushMatrix();
	glTranslatef(screenCenter.x,screenCenter.y,0);
	glRotatef(rotateAmount.x,1,0,0);
	glRotatef(rotateAmount.y,0,1,0);
	glRotatef(rotateAmount.z,0,0,1);
	//glTranslatef(-screenCenter.x,-screenCenter.y,0);
	
	for (int i = 0; i < particles.size(); i++){
		particles[i].draw();
		//particles[i].drawWithTexture(starTex1);
		particles[i].drawConnection();
	}
	
	ofSetColor(255,255,255,30);
	for(int i = 0;i < forces.size();i++){
		glPushMatrix();
		glTranslatef(forces[i].pos.x,forces[i].pos.y,forces[i].pos.z);
		ofCircle(0,0,10);
		glPopMatrix();
	}
	
	
	if(bShowAxis){
		ofSetColor(50,50,255);
		glBegin(GL_LINES);
			glVertex3f(xAxisMin.x, xAxisMin.y, xAxisMin.z);
			glVertex3f(xAxisMax.x, xAxisMax.y, xAxisMax.z);
		
			glVertex3f(yAxisMin.x, yAxisMin.y, yAxisMin.z);
			glVertex3f(yAxisMax.x, yAxisMax.y, yAxisMax.z);
		
			glVertex3f(zAxisMin.x, zAxisMin.y, zAxisMin.z);
			glVertex3f(zAxisMax.x, zAxisMax.y, zAxisMax.z);
		
		glEnd();
		
		glPushMatrix();
		glTranslatef(xAxisMax.x,xAxisMax.y - 5,xAxisMax.z);
		ofDrawBitmapString("X",0,0);
		glPopMatrix();
		
		glPushMatrix();
		glTranslatef(yAxisMax.x + 5,yAxisMax.y,yAxisMax.z);
		ofDrawBitmapString("Y",0,0);
		glPopMatrix();
		
		glPushMatrix();
		glTranslatef(zAxisMax.x,zAxisMax.y - 5,zAxisMax.z);
		ofDrawBitmapString("Z",0,0);
		glPopMatrix();
	}
	
	if(bShowBounds){
		ofSetColor(255,50,50);
		glBegin(GL_LINES);
		//
			glVertex3f(xMax.x,yMax.y,zMax.z);
			glVertex3f(xMin.x,yMax.y,zMax.z);
			
			glVertex3f(xMax.x,yMax.y,zMax.z);
			glVertex3f(xMax.x,yMin.y,zMax.z);
			
			glVertex3f(xMax.x,yMax.y,zMax.z);
			glVertex3f(xMax.x,yMax.y,zMin.z);
		//
		
			glVertex3f(xMin.x,yMin.y,zMin.z);
			glVertex3f(xMax.x,yMin.y,zMin.z);
			
			glVertex3f(xMin.x,yMin.y,zMin.z);
			glVertex3f(xMin.x,yMax.y,zMin.z);

			glVertex3f(xMin.x,yMin.y,zMin.z);
			glVertex3f(xMin.x,yMin.y,zMax.z);
		//
		
			glVertex3f(xMin.x,yMin.y,zMax.z);
			glVertex3f(xMax.x,yMin.y,zMax.z);
		
			glVertex3f(xMin.x,yMin.y,zMax.z);
			glVertex3f(xMin.x,yMax.y,zMax.z);
		
			glVertex3f(xMax.x,yMin.y,zMax.z);
			glVertex3f(xMax.x,yMin.y,zMin.z);
		
			glVertex3f(xMax.x,yMin.y,zMin.z);
			glVertex3f(xMax.x,yMax.y,zMin.z);
		
			glVertex3f(xMax.x,yMax.y,zMin.z);
			glVertex3f(xMin.x,yMax.y,zMin.z);
		
			glVertex3f(xMin.x,yMax.y,zMin.z);
			glVertex3f(xMin.x,yMax.y,zMax.z);
		glEnd();
	}
	
	glPopMatrix();
	glPopMatrix();

	ofSetColor(255,255,255);
	ofDrawBitmapString(ofToString(ofGetFrameRate()),15,15);
}

//--------------------------------------------------------------
void testApp::keyPressed  (int key){ 
	
	switch (key){
			
		case ' ':
			// reposition everything: 
			for (int i = 0; i < particles.size(); i++){
				particles[i].setInitialCondition(ofRandom(xMin.x,xMax.x),ofRandom(yMin.y,yMax.y),ofRandom(zMin.z,zMax.z),0,0,0);
			}
			break;
		case 'a':
			if(bShowAxis == true){
				bShowAxis = false;
			} else {
				bShowAxis = true;
			}
			break;
		case 'b':
			if(bShowBounds == true){
				bShowBounds = false;
			} else {
				bShowBounds = true;
			}
			break;
		case 'x':
			if(bRotateX == true){
				bRotateX = false;
			} else {
				bRotateX = true;
			}
			break;
		case 'y':
			if(bRotateY == true){
				bRotateY = false;
			} else {
				bRotateY = true;
			}
			break;
		case 'z':
			if(bRotateZ == true){
				bRotateZ = false;
			} else {
				bRotateZ = true;
			}
			break;
		case 'r':
			rotateAmount.set(0,0,0);
			break;
		case 'm':
			if(mode >= 1){
				setBoxSize(1000);
				mode = 0;
			} else {
				setBoxSize(2000);
				mode = 1;
			}
			break;
		case 'v':
			if(bBinning == true){
				bBinning = false;
			} else {
				bBinning = true;
			}
			break;
		case 'f':
			ofToggleFullscreen();
			screenCenter.set(ofGetWidth()/2, ofGetHeight()/2, 0);
			break;
	}
}

//--------------------------------------------------------------
void testApp::keyReleased  (int key){ 
}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y){
}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
}

//--------------------------------------------------------------
void testApp::mouseReleased(){
}
