var Runge_Kutta = new Class({
	Extends: Options,
	options: {
	diffeqs: [],
	input: []
	},
	variables: [],
	initialize: function(options) {
		this.setOptions(options);
	},
	evaluate: function(i, t, x) {
		var f = this.options.diffeqs[i];
		if ($type(f) != 'function')
			throw 'invalid diffeq funcion';
		return f(t, x);
	},
	set: function(obj) {
		this.options.input = obj;
		return this;
	},

	// Calculates the values of the variables at time t+h
	// t = last time value
	// h = time increment
	// vars = array of variables
	// n = number of variables in x array
	solve: function(t, h) {
		var n = this.options.diffeqs.length;
		var i;
		var vars = this.options.input.slice(0);

		inp = [];
		k1 = [];
		k2 = [];
		k3 = [];
		k4 = [];

		for (i=0; i<n; i++)
			k1[i] = this.evaluate(i,t,vars); // evaluate at time t

		for (i=0; i<n; i++)
			inp[i] = vars[i]+k1[i]*h/2; // set up input to diffeqs

		for (i=0; i<n; i++)
			k2[i] = this.evaluate(i,t+h/2,inp); // evaluate at time t+h/2

		for (i=0; i<n; i++)
			inp[i] = vars[i]+k2[i]*h/2; // set up input to diffeqs

		for (i=0; i<n; i++)
			k3[i] = this.evaluate(i,t+h/2,inp);  // evaluate at time t+h/2

		for (i=0; i<n; i++)
			inp[i] = vars[i]+k3[i]*h; // set up input to diffeqs

		for (i=0; i<n; i++)
			k4[i] = this.evaluate(i,t+h,inp);    // evaluate at time t+h

		for (i=0; i<n; i++)
			vars[i] = vars[i]+(k1[i]+2*k2[i]+2*k3[i]+k4[i])*h/6;

		return vars;
	}
});

Fx.Spring = new Class({
	Extends: Fx,
	spring: {
		springConst: 7,
		restLengthX: 0,
		restLengthY: 0,
		X1: 0
	},
	mass: {
		mass: 0.025,
		damping: 0.2
	},
	options: {

	},
	initialize: function(options) {
		var f1 = function(t, x) {
			return x[1];
		}.bind(this);
		var fX = function(t, x) {
			var r = -this.spring.springConst*(x[0] - this.spring.X1 -this.spring.restLengthX)
				- this.mass.damping*x[1];
			return r/this.mass.mass;
		}.bind(this);
		var fY = function(t, x) {
			var r = -this.spring.springConst*(x[0] - this.spring.X1 -this.spring.restLengthY)
				- this.mass.damping*x[1];
			return r/this.mass.mass;
		}.bind(this);
	
		this.solverX = new Runge_Kutta({
			diffeqs:[
				f1,
				fX
			],
			input: [0, 0]
		});
		this.solverY = new Runge_Kutta({
			diffeqs:[
				f1,
				fY
			],
			input: [0, 0]
		});

		this.springForce = { x: 0, y: 0 };
		this.springPos = { x: 0, y: 0 };

		return this.parent(options);
	},
	setTarget: function(pos) {
		this.target = pos;
		this.spring.restLengthX = pos.x;
		this.spring.restLengthY = pos.y;
	},
	step: function() {
		var time = $time();
		var thisLastTime = $chk(this.lastTime) ? this.lastTime : this.time;
		var diff = time - thisLastTime;
		this.lastTime = time;

		var xRet = this.solverX.set([this.springPos.x,this.springForce.x]).solve(time/1000,diff/1000);
		var yRet = this.solverY.set([this.springPos.y,this.springForce.y]).solve(time/1000,diff/1000);

		this.springForce.x = xRet[1];
		this.springForce.y = yRet[1];
		this.springPos.x = xRet[0];
		this.springPos.y = yRet[0];

		if (Math.abs(xRet[1]) < 0.001 && Math.abs(yRet[1]) < 0.001)
			this.complete();

		this.set(this.springPos);
	},
	set: function(pos) {
		this.fireEvent('step', pos);
	},
	startTimer: function() {
		this.lastTime = $time();
		return this.parent();
	}
});