
Thread = function(obj) {
	if (typeof obj == 'object') return Thread.extend(obj);
	else {
		this.T_id = Thread.count++;
		Thread.all[this.T_id] = this;
		return;
	};
};
Thread.all = [];
Thread.count = 0;
Thread.prototype.toString = function() { return 'Thread.all[' + this.T_id + ']'; };

Thread.extend = function(o) {
	if (typeof o != 'object') return null;
	
	if (!o.isEventObject) EventObject.extend(o);
	
	o.setDuration = Thread.prototype.setDuration;
	o.setInterval = Thread.prototype.setInterval;
	o.setMaster = Thread.prototype.setMaster;
	o.addSlave = Thread.prototype.addSlave;
	o.removeSlave = Thread.prototype.removeSlave;
	o.start = Thread.prototype.start;
	o.T_start = Thread.prototype.T_start;
	o.T_execute = Thread.prototype.T_execute;
	o.stop = Thread.prototype.stop;
	o.getRatio = Thread.prototype.getRatio;
	
	return o;
};

EventObject.extend(Thread.prototype);

Thread.prototype.setDuration = function(t) {
	this.T_duration = t;
};
Thread.prototype.setInterval = function(t) {
	this.T_interval = t;
};

Thread.prototype.setMaster = function(master) {
	if (master) master.addSlave(this);
	else this.master.removeSlave(this);	
};
Thread.prototype.addSlave = function(slave) {
	if (slave.master) slave.master.removeSlave(slave);
	if (!this.slaves) this.slaves = [];
	this.slaves[this.slaves.length] = slave;
	slave.master = this;
};
Thread.prototype.removeSlave = function(slave) {
	for (var i = 0; i < this.slaves.length; i++) {
		if (this.slaves[i] == slave) {
			this.slaves[i] = this.slaves[this.slaves.length-1];
			this.slaves.length--;
			return;
		};
	};
};

Thread.prototype.start = function() {
	if (this.master) {
		this.T_duration = this.master.T_duration;
		this.T_interval = this.master.T_interval;
	} else {
		if (!this.T_duration) this.T_duration = 0; // thread runs forever
		if (!this.T_interval) this.T_interval = 1;
	};

	this.invokeEvent('prerun');
	if (typeof this.prerun == 'function') this.prerun();
	
	if (this.slaves) for (var i = 0; i < this.slaves.length; i++) this.slaves[i].start();
	if (!this.master) {
		if (ua.ie && ua.mac) setTimeout(this + '.T_start()',0);
		else this.T_start();
	};
};
Thread.prototype.T_start = function() {
	this.T_startTime = this.master ? this.master.T_startTime : (new Date()).getTime(); 
	this.T_elapsed = this.T_last = 0;
	this.T_active = true;
	
	if (this.slaves) for (var i = 0; i < this.slaves.length; i++) this.slaves[i].T_start();
	if (!this.master) this.T_timer = setTimeout(this + '.T_execute()', this.T_interval);
};
Thread.prototype.T_execute = function() {
	this.T_last = this.T_elapsed;
	this.T_elapsed = this.master ? this.master.T_elapsed : (new Date()).getTime() - this.T_startTime;
	this.T_diff = this.T_elapsed - this.T_last;
		
	if (typeof this.run == 'function') this.run();
	this.invokeEvent('run');
	
	if (this.slaves) for (var i = 0; i < this.slaves.length; i++) this.slaves[i].T_execute();	
	if (!this.master) {
		if (this.T_duration && this.T_duration <= this.T_elapsed) this.stop();
		else this.T_timer = setTimeout(this + '.T_execute()', this.T_interval);
	};
};
Thread.prototype.stop = function() {
	if (!this.T_active) return;
	clearTimeout(this.T_timer);

	this.T_active = false;
	
	if (typeof this.postrun == 'function') this.postrun();
	this.invokeEvent('postrun');

	if (this.slaves) for (var i = 0; i < this.slaves.length; i++) this.slaves[i].stop();
};

Thread.prototype.getElapsedTime = function() {
	return this.T_elapsed;
};

Thread.prototype.setRatioFunction = function(f) {
	this.ratioFunction = f;
};

Thread.prototype.getRatio = function() {
	if (this.T_active && this.T_duration) {
		var r = this.T_elapsed / this.T_duration;
		r = r <= 1 ? r : 1;
		return this.ratioFunction ? this.ratioFunction(r) : r;
	}
	else return 0;
};

// ------------------------------------
//        Glide (RatioFunction)
// ------------------------------------
Thread.Glide = function(a) {
	var f = new Function('r', 'return r - arguments.callee.koeff * Math.sin(2 * r * Math.PI)');
	f.setKoeff = Thread.Glide.setKoeff;
	f.setKoeff(typeof a == 'number' ? a : .7);
	
	return f;
};
Thread.Glide.setKoeff = function(a) {
	a = a < 0 ? 0 : a > 1 ? 1 : a;
	this.koeff = a / (2 * Math.PI);
};

// -------------------------------------
//              Slide
// -------------------------------------

Slide = function(elm, x, y, duration, rf) {
	this.superClass = Thread;
	this.superClass();
	
	this.setInterval(10);

	this.setElm(elm);
	this.setDestination(x,y);
	this.setDuration(duration);
	if (typeof rf == 'function') this.setRatioFunction(rf);
	
	this.slideID = Slide.count++;
	Slide.all[this.slideID] = this;
};
Slide.prototype = new Thread();

Slide.all = [];
Slide.count = 0;
Slide.prototype.toString = function() { return 'Slide.all['+this.slideID+']'; };

Slide.prototype.setElm = function(elm) {
	this.elm = elm;
};
Slide.prototype.setDestination = function(x,y) {
	this.destX = x;
	this.destY = y;
};

Slide.prototype.prerun = function() {
	if (!this.elm) {
		this.stop();
		return;
	};
	
	this.startX = dom.getX(this.elm);
	this.startY = dom.getY(this.elm);
	
	this.dx = this.destX - this.startX;
	this.dy = this.destY - this.startY;
	
};
Slide.prototype.run = function() {
	var r = this.getRatio();
	dom.setProperties(this.elm, {coords: new Point(this.startX + r * this.dx, this.startY + r * this.dy)});
};

