/*

This script is a quick little (not 100% accurate) javascript port of a processing script by Dave Young/henderson http://hendersonsix.com
It is released under the Creative Commons Attribution-Share Alike 3.0 United States licence

Thanks to Éamon Phelan for adding in the lovely calming fps/node-count limiting

*/

var exchangeNodes={

  // ===== options ===== //
   s:2        // speed (adjust for performance)
  ,n:50       // how many nodes
  ,w:640      // width
  ,h:150      // height
  ,bg:'#000'  // background colour
  ,lw:0.2     // line width
  ,lc:'#999'  // line colour
  ,nc:'#fff'  // node colour
  ,cpuLoad:	40// max percent of cpu time spent rendering
  ,minFps: 15  //minimum fps, below this it starts culling nodes
  // ==== end options ==== //
  
  ,ctx:null
  ,nodes:[]
  ,times:[]
  ,rtimes:[30,30,30,30,30,30,30,30,30,30]
  ,nframes: 0
  ,realFps: 30
  ,totalRtime: 300
  ,twopi:Math.PI*2
  ,random:function(lo,hi){return lo+(Math.random()*(hi-lo));}
  
  ,setup:function(el){
    var canvas=document.createElementNS('http://www.w3.org/1999/xhtml','canvas');
    el.appendChild(canvas);
    this.ctx=canvas.getContext('2d');
    canvas.width=this.w;
    canvas.height=this.h;
    for(var i=0;i<this.n;++i){this.nodes[i]=new this.Node();}
    //window.setInterval(this.draw,256/this.s);
    var cpuLoad = this.cpuLoad /= 100;
    this.slackRatio = (1 - cpuLoad)/cpuLoad; 
    window.setTimeout(this.draw, 1000);//give the browser some time to stop hogging the cpu with page rendering
  }

  ,draw:function(){
    var e=exchangeNodes;

	var before = (new Date()).getTime();
    e.times.push(before);
	
	e.ctx.fillStyle=e.bg;
    e.ctx.fillRect(0,0,e.w,e.h);
    for(var i=0;i<e.n;i++){ e.nodes[i].generate(); e.nodes[i].move(); e.nodes[i].proxy(); }
  	
	if(e.times.length > 10)
		e.times.shift();
	e.nframes++;
	
	var after = (new Date()).getTime();
	e.realFps = e.times.length/((after - e.times[0])/1000);
	var frameTime = after - before;
	e.rtimes.push(frameTime);
	e.totalRtime = e.totalRtime + frameTime - e.rtimes.shift();
	
	var avgRtime = e.totalRtime/10;
	var delay = e.slackRatio * avgRtime;

	//adjust every 10 frames
	if((e.nframes%10) ==0){
		e.nframes = 0;		
		if(e.realFps < e.minFps){
			e.nodes.splice(0, Math.floor(e.nodes.length*0.2));
			e.n = e.nodes.length;
		}
		/*if (window.console) {
			console.log("FPS: " + e.realFps);
			console.log("Percentage Rendertime: " + e.totalRtime / (after - e.times[0]));
			console.log("Computed delay: " + delay);
			console.log("Number of nodes " + e.nodes.length);
		}*/
	}
	
	if(frameTime < 100)	//if truly dire, give up
		window.setTimeout(e.draw, delay);
	
  }
  
  ,Node:function(){with(this){
    var e=exchangeNodes;
    this.xpos=0; this.ypos=0; this.vx=0; this.vy=0; this.r=0; this.minDist=0; this.gravity=0; this.id=0; this.others=null;

    this.generate=function(){
      e.ctx.fillStyle=e.nc;
      e.ctx.beginPath();
      e.ctx.arc(xpos,ypos,r/2,0,e.twopi,false);
      e.ctx.fill();
      e.ctx.closePath();
    };
    
    this.proxy=function(){
      for(var i=id+1;i<e.n;i++){
        var dx=others[i].xpos-xpos;
        var dy=others[i].ypos-ypos;
        var distance=dx*dx+dy*dy;
        if(distance<minDist){
          e.ctx.strokeStyle=e.lc;
          e.ctx.lineWidth=e.lw;
          e.ctx.beginPath();
          e.ctx.moveTo(xpos||0,ypos||0);
          e.ctx.lineTo(others[i].xpos||0,others[i].ypos||0);
          e.ctx.stroke();
          e.ctx.closePath();
        }
      }
    };

    this.move=function(){ 
      xpos=xpos+vx/e.realFps;
      ypos=ypos+vy/e.realFps;
      if(xpos>e.w||xpos<0){xpos=xpos-vx/e.realFps; vx=vx*-0.99;}
      if(ypos>e.h||ypos<0){ypos=ypos-vy/e.realFps; vy=vy*-0.99;}
    };

    xpos=e.random(0,e.w);
    ypos=e.random(0,e.h);
    vx=vy=e.random(-32,32);
    r=3;
    minDist=100;
	minDist = minDist * minDist; //no need for sqrt for distance, only a threshold
    gravity=e.random(0.05,0.2);
    others=e.nodes;
    var id=0;
  }}
};
