#include "particle.h"
#include "ofMain.h"

//------------------------------------------------------------
particle::particle(){
	setInitialCondition(0,0,0,0);
	damping	= 0.01f;
	
	seperation.distance		= 20;
	alignment.distance		= 80;
	cohesion.distance		= 40;
	
	seperation.strength		= .03;
    alignment.strength		= .015;
	cohesion.strength		= .015;
	
	
	leafWidth = ofRandom(40, 60);
	leafHeight = ofRandom(40, 60);
}
//------------------------------------------------------------
void particle::reset()
{
	
	
	setInitialCondition(-10,-10,0,0);
	damping	= 0.01f;
	
	seperation.distance		= 20;
	alignment.distance		= 80;
	cohesion.distance		= 40;
	
	seperation.strength		= .03;
    alignment.strength		= .015;
	cohesion.strength		= .015;
	
	
	leafWidth = ofRandom(40, 60);
	leafHeight = ofRandom(40, 60);
	
	
	
}
void particle::reset2()
{
	
	
	setInitialCondition(-10,-10,0,0);
	damping	= 0.01f;
	
	seperation.distance		= 20;
	alignment.distance		= 80;
	cohesion.distance		= 40;
	
	seperation.strength		= .03;
    alignment.strength		= .015;
	cohesion.strength		= .015;
	
	
	leafWidth = ofRandom(40, 60);
	leafHeight = ofRandom(40, 60);
	
	
	
}

//------------------------------------------------------------
void particle::resetForce(){
    // we reset the forces every frame
    frc.set(0,0);
	
	// reset the flock info: 
	cohesion.count 	    = 0;
	seperation.count	= 0;
	alignment.count		= 0;
	
	cohesion.sum.set(0,0); 	
	seperation.sum.set(0,0); 	
	alignment.sum.set(0,0); 	

}

//------------------------------------------------------------
void particle::addForce(float x, float y){
    // add in a force in X and Y for this frame.
    frc.x = frc.x + x;
    frc.y = frc.y + y;
}

//------------------------------------------------------------
void particle::addRepulsionForce(float x, float y, float radius, float scale){
    
	// ----------- (1) make a vector of where this position is: 
	
	ofxVec2f posOfForce;
	posOfForce.set(x,y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
        diff.normalize();
		frc.x = frc.x + diff.x * scale * pct;
        frc.y = frc.y + diff.y * scale * pct;
    }
}

//------------------------------------------------------------
void particle::addAttractionForce(float x, float y, float radius, float scale){
    
	// ----------- (1) make a vector of where this position is: 
	
	ofxVec2f posOfForce;
	posOfForce.set(x,y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
		diff.normalize();
		frc.x = frc.x - diff.x * scale * pct;
        frc.y = frc.y - diff.y * scale * pct;
    }
}

//------------------------------------------------------------
void particle::addRepulsionForce(particle &p, float radius, float scale){
	
	// ----------- (1) make a vector of where this particle p is: 
	ofxVec2f posOfForce;
	posOfForce.set(p.pos.x,p.pos.y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
		diff.normalize();
		frc.x = frc.x + diff.x * scale * pct;
        frc.y = frc.y + diff.y * scale * pct;
		p.frc.x = p.frc.x - diff.x * scale * pct;
        p.frc.y = p.frc.y - diff.y * scale * pct;
    }
}

//------------------------------------------------------------
void particle::addAttractionForce(particle & p, float radius, float scale){
	
	// ----------- (1) make a vector of where this particle p is: 
	ofxVec2f posOfForce;
	posOfForce.set(p.pos.x,p.pos.y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
		diff.normalize();
		frc.x = frc.x - diff.x * scale * pct;
        frc.y = frc.y - diff.y * scale * pct;
		p.frc.x = p.frc.x + diff.x * scale * pct;
        p.frc.y = p.frc.y + diff.y * scale * pct;
    }
	
}

//------------------------------------------------------------
void particle::addClockwiseForce(particle &p, float radius, float scale){
	
	// ----------- (1) make a vector of where this particle p is: 
	ofxVec2f posOfForce;
	posOfForce.set(p.pos.x,p.pos.y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
		diff.normalize();
		frc.x = frc.x - diff.y * scale * pct;
        frc.y = frc.y + diff.x * scale * pct;
		p.frc.x = p.frc.x + diff.y * scale * pct;
        p.frc.y = p.frc.y - diff.x * scale * pct;
    }
}

//------------------------------------------------------------
void particle::addCounterClockwiseForce(particle &p, float radius, float scale){
	
	// ----------- (1) make a vector of where this particle p is: 
	ofxVec2f posOfForce;
	posOfForce.set(p.pos.x,p.pos.y);
	
	// ----------- (2) calculate the difference & length 
	
	ofxVec2f diff	= pos - posOfForce;
	float length	= diff.length();
	
	// ----------- (3) check close enough
	
	bool bAmCloseEnough = true;
    if (radius > 0){
        if (length > radius){
            bAmCloseEnough = false;
        }
    }
	
	// ----------- (4) if so, update force
    
	if (bAmCloseEnough == true){
		float pct = 1 - (length / radius);  // stronger on the inside
		diff.normalize();
		frc.x = frc.x + diff.y * scale * pct;
        frc.y = frc.y - diff.x * scale * pct;
		p.frc.x = p.frc.x - diff.y * scale * pct;
        p.frc.y = p.frc.y + diff.x * scale * pct;
		
    }
}


//------------------------------------------------------------
void particle::addForFlocking(particle &p){
	
	ofxVec3f diff, diffNormalized;
	float distance;
	
	diff			= p.pos - pos;
	distance		= diff.length();
	diffNormalized	= diff;
	diffNormalized.normalize();

	if( distance > 0 && distance < seperation.distance ){
		seperation.sum += diffNormalized;
		seperation.count++;
	}
	
	if( distance > 0 && distance < alignment.distance ){
		alignment.sum += p.vel.getNormalized();
		alignment.count++;
	}
	
	if( distance > 0 && distance < cohesion.distance ){
		cohesion.sum += p.pos;
		cohesion.count++;
	}
}

//------------------------------------------------------------
void particle::addFlockingForce(){
	
	
	
	// seperation
	if(seperation.count > 0){							// let's add seperation :)
		seperation.sum /= (float)seperation.count;
		float sepFrc 	= seperation.strength;
		frc -= (seperation.sum.normalized()		    * sepFrc);
	}
	
	// alignment
	if(alignment.count > 0){
		alignment.sum /= (float)alignment.count;
		float alignFrc 	= alignment.strength;
		frc += (alignment.sum		* alignFrc);		// don't normalize the allignment, just use the average
	}
	
	// cohesion
	if(cohesion.count > 0){
		cohesion.sum /= (float)cohesion.count;
		cohesion.sum -= pos;
		float cohFrc 	= cohesion.strength;
		frc += (cohesion.sum.normalized()			* cohFrc);
	}
	
}


//------------------------------------------------------------
void particle::addDampingForce(){
	
	// the usual way to write this is  vel *= 0.99
	// basically, subtract some part of the velocity 
	// damping is a force operating in the oposite direction of the 
	// velocity vector
	
    frc.x = frc.x - vel.x * damping;
    frc.y = frc.y - vel.y * damping;
}

//------------------------------------------------------------
void particle::setInitialCondition(float px, float py, float vx, float vy){
    pos.set(px,py);
	vel.set(vx,vy);
}

//------------------------------------------------------------
void particle::update(){	
	lastPos = pos;
	//printf("vel frc %f %f, %f %f \n", vel.x, vel.y, frc.x, frc.y);
	
	vel = vel + frc;
	pos = pos + vel;
	
	
	//trail
    float x = pos.x;
    float y = pos.y;
    
    ofPoint temp;
    temp.x = x;
    temp.y = y;
    points.push_back(temp);
    
    
    if (points.size() > 8){
        points.erase(points.begin());
    }
    
    //lastPos = pos;
	
}

//------------------------------------------------------------
void particle::draw(){
    
	rad=3;

	ofFill();
	//ofCircle(pos.x, pos.y, rad);
	
	ofxVec2f velNormal = vel;
	velNormal.normalize();
	
	ofxVec2f velPerp;
	velPerp.x = -velNormal.y;
	velPerp.y = velNormal.x;
	
	ofLine(pos.x, pos.y, pos.x + velNormal.x*10, pos.y + velNormal.y*10);
	   
}

//------------------------------------------------------------
void particle::draw2(ofImage * picture){
	ofPushMatrix();
	//ofSetColor(255, 255, 255,255);
	
	rad=30;
	//atan 
	ofxVec2f diff = pos - lastPos;
//	float distance = diff.length();
	float angle = atan2(diff.y, diff.x);
	
	ofTranslate(pos.x+20, pos.y, 0);
	ofRotateZ(angle * RAD_TO_DEG);	
	//picture->draw(0, 0,vel.x*5,vel.x*5);
	picture->draw(0, 0,40,40);
	//ofCircle(0, 0, rad);
	//picture->draw(0, 0,30,30);
	ofPopMatrix();
	
}
//------------------------------------------------------------

void particle::draw3(ofImage * picture){
	rad=25;
	//ofRectangle bounds = picture->getPixels(toDraw, 0, 0);
	
	picture->draw(pos.x, pos.y,leafWidth*0.5,leafHeight*0.5);
	//ofCircle(pos.x, pos.y, rad);
	// ofCircle(pos.x, pos.y, 3);
}

//------------------------------------------------------------
void particle::draw4(ofImage * picture){
    
    
    for (int i = 0; i < points.size(); i++){
        ofPushMatrix();
        ofTranslate(points[i].x, points[i].y, 0);
        //    ofRotateZ(angle * RAD_TO_DEG);
		    ofCircle(0, 0,i*0.5);
        ofPopMatrix();
	}
}

//------------------------------------------------------------

void particle::drawBoat(ofImage * picture){
	rad=30;
	//ofRectangle bounds = picture->getPixels(toDraw, 0, 0);
	
	picture->draw(pos.x, pos.y,100*0.5,100*0.5);
	// ofCircle(pos.x, pos.y, rad);
}



//------------------------------------------------------------
void particle::bounceOffWalls(){
	
	// sometimes it makes sense to damped, when we hit
	bool bDampedOnCollision = true;
	bool bDidICollide = false;
	
	// what are the walls
	float minx = 0;
	float miny = 20;
	float maxx = ofGetWidth();
	float maxy = ofGetHeight()-20;
	
	if (pos.x > maxx){
		pos.x = maxx; // move to the edge, (important!)
		vel.x *= -1;
		bDidICollide = true;
	} else if (pos.x < minx){
		pos.x = minx; // move to the edge, (important!)
		vel.x *= -1;
		bDidICollide = true;
	}
	
	if (pos.y > maxy){
		pos.y = maxy; // move to the edge, (important!)
		vel.y *= -1;
		bDidICollide = true;
	} else if (pos.y < miny){
		pos.y = miny; // move to the edge, (important!)
		vel.y *= -1;
		bDidICollide = true;
	}
	
	if (bDidICollide == true && bDampedOnCollision == true){
		vel *= 0.3;
	}
	
}

//------------------------------------------------------------
void particle::bounceOffWallsSharks(){
	
	// sometimes it makes sense to damped, when we hit
	bool bDampedOnCollision = true;
	bool bDidICollide = false;
	
	// what are the walls
	float minx = 0-30;
	float miny = -30;
	float maxx = ofGetWidth()+30;
	float maxy = ofGetHeight()+30;
	
	if (pos.x > maxx){
		pos.x = maxx; // move to the edge, (important!)
		vel.x *= -1;
		bDidICollide = true;
	} else if (pos.x < minx){
		pos.x = minx; // move to the edge, (important!)
		vel.x *= -1;
		bDidICollide = true;
	}
	
	if (pos.y > maxy){
		pos.y = maxy; // move to the edge, (important!)
		vel.y *= -1;
		bDidICollide = true;
	} else if (pos.y < miny){
		pos.y = miny; // move to the edge, (important!)
		vel.y *= -1;
		bDidICollide = true;
	}
	
	if (bDidICollide == true && bDampedOnCollision == true){
		vel *= 0.3;
	}
	
}



//------------------------------------------
void particle::invertWalls(){
	
	// sometimes it makes sense to damped, when we hit
	bool bDampedOnCollision = true;
	bool bDidICollide = false;
	
	// what are the walls
	float minx = 0;
	float miny = 0;
	float maxx = ofGetWidth();
	float maxy = ofGetHeight();
	
	if (pos.x > maxx){
		pos.x = minx; // move to the edge, (important!)
		//vel.x *= -1;
		bDidICollide = true;
	} else if (pos.x < minx){
		pos.x = maxx; // move to the edge, (important!)
		//vel.x *= -1;
		bDidICollide = true;
	}
	
	if (pos.y > maxy){
		pos.y = miny; // move to the edge, (important!)
		//vel.y *= -1;
		bDidICollide = true;
	} else if (pos.y < miny){
		pos.y = maxy; // move to the edge, (important!)
		//vel.y *= -1;
		bDidICollide = true;
	}
	
	if (bDidICollide == true && bDampedOnCollision == true){
		//	vel *= 0.3;
	}
}

//------------------------------------------

bool particle::calDist(particle &p){
	
	bool bHit=FALSE;
	ofxVec2f diff=p.pos -pos;
	float distance=diff.length();
	distancePrint=distance;
	radiusSum=p.rad/2+rad/2;
	if(distance<=p.rad/2+rad/2)
	{
		bHit=TRUE;
	}
	
	return(bHit);
	
	
}

