var EventDispatcher = Class.$extend({
	
	__init__: function() {
		this.handlers = {};
	},
	
	addHandler: function(eventName, handler) {
		if (this.handlers[eventName] === undefined)
			this.handlers[eventName] = [];
		this.handlers[eventName].unshift(handler); // unshift rather than push because #dispatchEvent does a reverse-for
	},
	
	removeHandler: function(eventName, handler) {
		for (var h = this.handlers[eventName].length - 1; h >= 0; h--)
			if (arguments.length === 1 || this.handlers[eventName][h] === handler)
				delete this.handlers[eventName][h];
	},
	
	dispatchEvent: function(eventName) {
		if (this.handlers[eventName])
			for (var h = this.handlers[eventName].length - 1; h >= 0; h--)
				this.handlers[eventName][h]();
	}
	
});

var Interval = EventDispatcher.$extend({
	
	__init__: function(opts) {
		this.$super();
		this.duration = opts['duration'];
	},
	
	start: function() {
		this.interval = setInterval(this.dispatchEvent.bind(this, 'iteration'), this.duration);
	},
	
	stop: function() {
		clearInterval(this.interval);
		this.interval = null;
	}
	
});

var Looper = Interval.$extend({
	
	__init__: function(opts) {
		this.$super(opts);
		
		this.length = opts['length'];
		this.currentIndex = opts['startIndex'] || 0;
		this.previousIndices = [];
		
		this.previousIndicesMaxCount = opts['previousIndicesMaxCount'] || 42;
		
		this.addHandler('iteration', this.gotoNext.bind(this));
	},
	
	goto: function(newIndex) {
		this.previousIndices.unshift(this.currentIndex);
		
		if (this.previousIndices.length > this.previousIndicesMaxCount)
			this.previousIndices.splice(this.previousIndicesMaxCount)
		
		this.currentIndex = newIndex;
	},
	
	gotoNext: function() {
		var newIndex =
			this.currentIndex >= this.length - 1
				? 0
				: this.currentIndex + 1;
		
		this.goto(newIndex);
	},
	
	gotoPrevious: function() {
		var newIndex =
			this.currentIndex <= 0
				? this.length - 1
				: this.currentIndex - 1;
		
		this.goto(newIndex);
	}
	
});

