/**************************************************************************************\
*   Name:           Queue                                                              *
*   Author:         Philip Rasmussen (DK)                                              *
*   Version:        0.1.0 (beta)                                                       *
*   Description:	Defines functionality for maintaining item queues                  *
\**************************************************************************************/

function Queue(){
	
	this._pointer = 0;
	this._items = [];
	this._eventListeners = {}; // itemInserted, itemMoved, itemRemoved, itemChanged, pointerChanged
	
	this.count = function(){
	
		return this._items.length;
	}
	
	this.pointer = function(pointer){
	
		if(typeof pointer == 'number'){
		
			var oldPointer = this._pointer;
		
			if(this._pointer > this.count()) this._pointer = this.count()-1;
			else if(this._pointer < 0) this._pointer = 0;
			else this._pointer = pointer;
			
			if(oldPointer != this._pointer) this.triggerEvent('pointerChanged');
			
			return this;
		}
		
		return this._pointer;
	}
	
	this.current = function(){
	
		if(this.count()) return this._items[this.pointer()];
		else return null;
	}
	
	this.previous = function(){
	
		var pointer = this.pointer();
	
		if(pointer == 0) this.pointer(this.count()-1);
		else this.pointer(pointer-1);
		
		return this.current();
	}
	
	this.next = function(){
	
		var pointer = this.pointer();
	
		if(pointer == this.count()-1) this.pointer(0);
		else this.pointer(pointer+1);
		
		return this.current();
	}
	
	this.rewind = function(){
	
		this.pointer(0);
	
		return this.current();
	}
	
	this.end = function(){
	
		this.pointer(this.count()-1);
	
		return this.current();
	}
	
	this.endReached = function(){
	
		return this.pointer() == this.count()-1;
	}
	
	this.startReached = function(){
	
		return this.pointer() == 0;
	}
	
	this.itemAt = function(index, item){
	
		if(typeof index != 'number') throw new Exception('Queue.itemAt requires first parameter to be of type number.');
		if(index < 0 || index >= this.count()) throw new Exception('Invalid index passed to Queue.itemAt.');
		
		if(item != null){
		
			this._items[index] = item;
			this.triggerEvent('itemChanged');
			
			return this;
		}
		
		return this._items[index];
	}
	
	this.removeItemAt = function(index){
	
		var count = this.count();
	
		if(typeof index != 'number') throw new Exception('Queue.removeItemAt requires first parameter to be of type number.');
		if(index < 0 || index >= count) throw new Exception('Invalid index passed to Queue.removeItemAt.');
		
		this._items.splice(index, 1);
		
		var pointer = this.pointer();
		if(index > pointer || index == pointer && index == count-1) this.pointer(pointer-1);
			
		this.triggerEvent('itemRemoved');
		
		return this;	 
	}
	
	this.allItems = function(items){
	
		if(items != null){
		
			if((items[0].constructor.toString()).indexOf('Array') == -1) items = [items];
		
			this.removeAllItems();
			this._items = items;

			if(items.length > 0)
				this.triggerEvent('itemInserted');
			
			return this;
		}
		
		return this._items;	
	}
	
	this.removeAllItems = function(){
	
		this._items = [];
		this.pointer(0);
			
		this.triggerEvent('itemRemoved');
		
		return this;
	}
	
	this.insertItemBefore = function(item, index){
	
		if(typeof index != 'number') throw new Exception('Queue.insertItemBefore requires second parameter to be of type number.');
		if(index < 0 || index >= this.count()) throw new Exception('Invalid index passed to Queue.insertItemBefore.');
		if(item == null) throw new Exception('Queue.insertItemBefore was passed no item to insert.');
		
		if(index == 0) this._items.unshift(item);
		else this._items.splice(index-1, 0, item);
		
		var pointer = this.pointer();
		if(pointer >= index) this.pointer(pointer+1);
		
		this.triggerEvent('itemInserted');
		
		return this;
	}
	
	this.insertItemAfter = function(item, index){
	
		if(typeof index != 'number') throw new Exception('Queue.insertItemAfter requires second parameter to be of type number.');
		if(index < 0 || index >= this.count()) throw new Exception('Invalid index passed to Queue.insertItemAfter.');
		if(item == null) throw new Exception('Queue.insertItemAfter was passed no item to insert.');
		
		this._items.splice(index, 0, item);
		
		var pointer = this.pointer();
		if(pointer > index) this.pointer(pointer+1);
		
		this.triggerEvent('itemInserted');
		
		return this;
	}
	
	this.moveItemToBefore = function(index, index2){
	
		var count = this.count();
	
		if(typeof index != 'number') throw new Exception('Queue.moveItemToBefore requires first parameter to be of type number.');
		if(index < 0 || index >= count) throw new Exception('Invalid index passed to Queue.moveItemToBefore.');
		if(typeof index2 != 'number') throw new Exception('Queue.moveItemToBefore requires second parameter to be of type number.');
		if(index2 < 0 || index2 >= count) throw new Exception('Invalid second index passed to Queue.moveItemToBefore.');
		
		if(index == index2 || index == index2-1) return this;
		
		var pointer = this.pointer();
		
		var item = this._items.splice(index, 1)[0];
		if(index2 > index) --index2;
		
		if(index2 == 0) this._items.unshift(item);
		else this._items.splice(index2-1, 0, item);
		
		if(pointer == index) this.pointer(index2-1);
		else if(pointer > index && pointer < index2) this.pointer(pointer-1);
		else if(pointer < index && pointer >= index2) this.pointer(pointer+1);
		
		this.triggerEvent('itemMoved');
		
		return this;
	}
	
	this.moveItemToAfter = function(index, index2){
	
		var count = this.count();
	
		if(typeof index != 'number') throw new Exception('Queue.moveItemToAfter requires first parameter to be of type number.');
		if(index < 0 || index >= count) throw new Exception('Invalid index passed to Queue.moveItemToAfter.');
		if(typeof index2 != 'number') throw new Exception('Queue.moveItemToAfter requires second parameter to be of type number.');
		if(index2 < 0 || index2 >= count) throw new Exception('Invalid second index passed to Queue.moveItemToAfter.');
		
		if(index == index2 || index-1 == index2) return this;
		
		var pointer = this.pointer();
		
		var item = this._items.splice(index, 1)[0];
		if(index2 > index) --index2;
		
		this._items.splice(index2, 0, item);
		
		if(pointer == index) this.pointer(index2+1);
		else if(pointer > index && pointer < index2) this.pointer(pointer-1);
		else if(pointer < index && pointer > index2) this.pointer(pointer+1);
		
		this.triggerEvent('itemMoved');
		
		return this;
	}
	
	this.addEventListener = function(type, handler){
	
		if(!this._eventListeners[type]) this._eventListeners[type] = [];
		if(typeof handler == 'function') this._eventListeners[type].push(handler);
	}
	
	this.removeEventListener = function(type, handler){
	
		var handlers = this._eventListeners[type];
	
		if(handlers){
		
			var length = handlers.length;
		
			for(var i = 0; i<length; ++i) if(handlers[i] == handler){this._eventListeners[type].splice(i, 1); break;};
		}
		
		return this;
	}
	
	this.triggerEvent = function(type){
	
		var handlers = this._eventListeners[type];
	
		if(handlers){
		
			var length = handlers.length;
		
			for(var i = 0; i<length; ++i) handlers[i].apply(this, []);
		}
		
		return this;
	}
}
