var Particle = new Class({
	initialize: function(position, mass, update_callback) {
		this.position = new Vector(position.x, position.y);
		this.last_position = new Vector(position.x, position.y);
		this.force = new Vector();
		this.mass = typeof(mass)!="undefined" ? mass : 1;
		this.update_callback = function(particle) {};
		if(typeof update_callback != "undefined")
			this.update_callback = update_callback;
		this.fixed = false;
	},
	
	update: function(timestep) {
		if(!this.fixed) {
			var tmp = new Vector(this.position.x, this.position.y);
			var accel = this.force.div_ip(this.mass).mul_ip(timestep*timestep);
			this.position.mul_ip(2).sub_ip(this.last_position).add_ip(accel);
			this.last_position.copy(tmp);
			this.force.set(0, 0);
		}
		this.update_callback(this);
	},
	
	add_force: function(force) {
		if(!this.fixed)
			this.force.add_ip(force);
	},
	
	set_position: function(position) {
		this.position.copy(position);
		this.last_position.copy(position);
	},
	
	set_velocity: function(velocity) {
		if(!this.fixed)
			this.last_position = this.position.sub(velocity);
	}
});

var Joint = new Class({
	initialize: function(part1, part2) {
		this.part1 = part1;
		this.part2 = part2;
		this.restlength = part1.position.sub(part2.position).len();
	},
	
	relax: function() {
		var invmass1 = 1.0/this.part1.mass;
		var invmass2 = 1.0/this.part2.mass;
		var delta = this.part2.position.sub(this.part1.position);
		var deltalength = delta.len();
		var diff = (deltalength-this.restlength)
		           / (deltalength*(invmass1+invmass2));
		delta.mul_ip(diff);
		this.part1.position.add_ip(delta.mul(invmass1));
		this.part2.position.sub_ip(delta.mul(invmass2));
	}
});

var Space = new Class({
	initialize: function(framerate) {
		this.timestep = 1.0/framerate;
		this.particles = [];
		this.constraints = [];
		this.gravity = new Vector(0, 0);
		this.relaxations = 1;
	},
	
	run: function() {
		this.update.periodical(parseInt(this.timestep*1000), this);
	},
	
	update: function() {
		this.particles.each(function(particle, index) {
			particle.add_force(this.gravity.mul(particle.mass));
			particle.update(this.timestep);
		}, this);
		for(var i = 0; i < this.relaxations; i++) {
			this.constraints.each(function(constraint, index) {
				constraint.relax();
			}, this);
		}
	},
	
	add_particle: function(particle) {
		this.particles.push(particle);
	},
	
	add_constraint: function(constraint) {
		this.constraints.push(constraint);
	}
});
