import processing.core.*;


//--------------------------- making things move
//--------------------------- example 07 _ 01
//--------------------------- zachary lieberman / zlieb@parsons.edu


class particleManager {

  int numParticles;
  int numSprings;
  
  particle   PRTS       [];
  particle   PRTS_temp  [];              // I need a temp array to run partial time steps!
  derivative DERIVS     [];              // I need to store this when I run partial time steps
  spring     SPRINGS    [];
  
  
  int ptCount;

  //---------------------
  particleManager(){
    
    ptCount = 0;
    numParticles = 5;
    numSprings = 9;
    
    PRTS          = new particle[numParticles];
    PRTS_temp     = new particle[numParticles];
    DERIVS        = new derivative[numParticles];
    SPRINGS       = new spring[numSprings];
    
    for (int i = 0; i < numParticles; i ++){
      Vector3D pos = new Vector3D((float)Math.random() * 400.0f, (float)Math.random() * 200.0f, (float)Math.random() * 200.0f);
      Vector3D vel = new Vector3D(0,0,0);
      
      PRTS[i]         = new particle();
      PRTS[i].setPropertiesAndTurnOn(pos,vel);
      PRTS_temp[i]    = new particle();
      DERIVS[i]       = new derivative();
    }
    
   // PRTS[0].bFixed   = true;
    
    // init springs (connect pts a and b)
    for (int i = 0; i < numSprings; i ++){
      SPRINGS[i] = new spring();
     
      SPRINGS[i].dist = 100.0f;
      SPRINGS[i].kspr = 1f;
    }
    
    SPRINGS[0].from  = 0; SPRINGS[0].to  = 1;
    SPRINGS[1].from  = 0; SPRINGS[1].to  = 2;
    SPRINGS[2].from  = 0; SPRINGS[2].to  = 3;
    SPRINGS[3].from  = 4; SPRINGS[3].to  = 1;
    SPRINGS[4].from  = 4; SPRINGS[4].to  = 2;
    SPRINGS[5].from  = 4; SPRINGS[5].to  = 3;
    SPRINGS[6].from  = 1; SPRINGS[6].to  = 2;
    SPRINGS[7].from  = 2; SPRINGS[7].to  = 3;
    SPRINGS[8].from  = 3; SPRINGS[8].to  = 1;

  }
  
  //---------------------
  void idle(float dt){
    // copy real into temporary:
    for (int i = 0; i < numParticles; i++){
       PRTS_temp[i].set(PRTS[i]);
       PRTS_temp[i].frc.setXYZ(0,0,0);      // clear the force every frame....
    }
    
    int simulation = 1;

    //-------------------------
    switch (simulation){
    	
        case 0:			// EULER		
        calculateForces();
      	calculateDerivatives();
      	addDerivativesToReal(dt);
      	break;
	
	case 1:			// MIDPOINT
	calculateForces();
	calculateDerivatives();
	addDerivativesToTemp(0.5f,dt);
			
			// reset the forces!
	for (int i = 0; i < numParticles; i++){
          PRTS_temp[i].frc.setXYZ(0,0,0);      // clear the force every frame....
        }
			
        calculateForces();
	calculateDerivatives();
	addDerivativesToReal(dt);
	break;
     }
     //-------------------------
     
   
  }

  //----------------------------------
  void addDerivativesToReal(float dt){
     for (int i = 0; i < numParticles; i++){
       DERIVS[i].dp.mult(dt);
       DERIVS[i].dv.mult(dt);
       PRTS[i].pos.add(DERIVS[i].dp);
       PRTS[i].vel.add(DERIVS[i].dv);
     }
   }
  
  //----------------------------------
  void addDerivativesToTemp(float scale, float dt){
    for (int i = 0; i < numParticles; i++){
      DERIVS[i].dp.mult(dt*scale);
      DERIVS[i].dv.mult(dt*scale);
      PRTS_temp[i].pos.add(DERIVS[i].dp);
      PRTS_temp[i].vel.add(DERIVS[i].dv);
    }
  }
  //----------------------------------
  void calculateDerivatives(){
    for (int i = 0; i < numParticles; i++){
      DERIVS[i].dp.set(PRTS_temp[i].vel);
      DERIVS[i].dv.set(PRTS_temp[i].frc);
    }
  }

 void calculateForces(){
     
     boolean bFixed;
     Vector3D tempVel = new Vector3D(0,0,0);
     Vector3D grav = new Vector3D(0,0,2.0f);
     
     //-------------------------------- springs ---------------------
     
     float k, d;
     boolean bAFixed;
     boolean bBFixed;
     Vector3D diff = new Vector3D(0,0,0);;
       
      for (int i = 0; i < numSprings; i++){
        
        int pta  = SPRINGS[i].from;
	int ptb  = SPRINGS[i].to;
        bAFixed	 = PRTS_temp[pta].bFixed;
        bBFixed	 = PRTS_temp[ptb].bFixed;
	diff.set(PRTS_temp[ptb].pos);
        diff.sub(PRTS_temp[pta].pos);
	float len = diff.length();
	diff.normalise();
        k = SPRINGS[i].kspr;
	d = SPRINGS[i].dist;

        // f = -k(d-dist)  hookes law
        float spring_force = (k * (d - len));
        diff.mult(spring_force);
        if (!bAFixed) PRTS_temp[pta].frc.sub(diff); 
        if (!bBFixed) PRTS_temp[ptb].frc.add(diff); 	
      }
     
     
     
     //-------------------------------- grav & drag -----------------
     for (int i=0;i<numParticles;i++) {
	bFixed = PRTS_temp[i].bFixed;
	
        // gravity!
        if (!bFixed){
          //PRTS_temp[i].frc.add(grav);
        }   	
        
        // damping / friction....
        if (!bFixed){
          tempVel.set(PRTS_temp[i].vel);
          tempVel.mult(0.2f);
          PRTS_temp[i].frc.sub(tempVel);
	}
     }
     
 }
 
 //---------------------
  void draw(PApplet mom){
    
    for (int i = 0; i < numParticles; i ++){
      PRTS[i].draw(mom);
    }
    
    // draw the springs:
    for (int i = 0; i < numSprings; i++){
        int pta  = SPRINGS[i].from;
	int ptb  = SPRINGS[i].to;
        
        mom.stroke(255);
           
         mom.line(PRTS[pta].pos.x, PRTS[pta].pos.y, PRTS[pta].pos.z, PRTS[ptb].pos.x, PRTS[ptb].pos.y, PRTS[ptb].pos.z);
    }
    
    
    
  }
   
}
