var ListElement = function (el) {
	this.next = false;
	this.prev = false;
	this.el = el;
	
	this.__toJson = function() {
		var str = '{'
				+ '"prev" : '+this.prev+','
				+ '"next" : '+this.next+','
				+ '"element" : '+this.el.__toJson()+'}';
		
		return str;
	}
	
	this.__fromArray = function(node, createElementCallback) {
		this.prev = node.prev;
		this.next = node.next;
		this.el = createElementCallback(node.element);
	}
	
	this.getElement = function() {
		return this.el;
	}
}

var List = function () {
	this.elements = {};
	this.nextIndex = 1;
	this.counter = 0;
	this.startIndex = false;
	this.endIndex = false;
	
	this.__toJson = function() {
		var str = '{'
				+ '"nextIndex" : '+this.nextIndex+','
				+ '"counter" : '+this.counter+','
				+ '"startIndex" : '+this.startIndex+','
				+ '"endIndex" : '+this.endIndex+','
				+ '"nodes" : {';
				
		var elements = [];
		for (index in this.elements) {
			elements.push('"'+index+'" : '+this.elements[index].__toJson());
		}
		str += elements.join(',') + '}}';
				
		return str;
	}
	
	this.__fromArray = function(list, createElementCallback) {
		this.nextIndex = list.nextIndex;
		this.counter = list.counter;
		this.startIndex = list.startIndex;
		this.endIndex = list.endIndex;
		
		this.elements = {};
		
		for (index in list.nodes) {
			this.elements[index] = new ListElement(null);
			this.elements[index].__fromArray(list.nodes[index], createElementCallback);
		}
	}
	
	this.add = function(el) {
		return this.addBefore(el, this.endIndex);
	}
	
	this.addAfter = function(el, prev) {
		var el = new ListElement(el);
		if (prev) {
			if (this.elements[prev]) {
				el.prev = prev;
				el.next = false;
				this.elements[this.nextIndex] = el;
				if (this.elements[prev].next) {
					this.elements[this.nextIndex].next = this.elements[prev].next;
					this.elements[ this.elements[prev].next ].prev = this.nextIndex;
				}
				this.elements[prev].next = this.nextIndex;
				
				if (!this.elements[this.nextIndex].next) this.endIndex = this.nextIndex;
				this.counter++;
				return this.nextIndex++;
			}
		} else {
			el.next = false;
			if (this.startIndex) {
				el.next = this.startIndex;
				this.elements[el.next].prev = this.nextIndex;
			}
			this.startIndex = this.nextIndex;
			el.prev = false;
			this.elements[this.nextIndex] = el;
			
			if (!this.elements[this.nextIndex].next) this.endIndex = this.nextIndex;
			this.counter++;
			return this.nextIndex++; 
		}
		return false;
	}
	
	this.addBefore = function(el, next) {
		var el = new ListElement(el);
		if (next) {
			if (this.elements[next]) {
				el.prev = false;
				el.next = next;
				this.elements[this.nextIndex] = el;
				if (this.elements[next].prev) {
					this.elements[this.nextIndex].prev = this.elements[next].prev;
					this.elements[ this.elements[next].prev ].next = this.nextIndex;
				}
				this.elements[next].prev = this.nextIndex;
				
				if (!this.elements[this.nextIndex].prev) this.startIndex = this.nextIndex;
				this.counter++;
				return this.nextIndex++;
			}
		} else {
			if (this.endIndex) {
				el.prev = this.endIndex;
				this.elements[el.prev].next = this.nextIndex;
			}
			this.endIndex = this.nextIndex;
			el.next = false;
			this.elements[this.nextIndex] = el;
			
			if (!this.elements[this.nextIndex].prev) this.startIndex = this.nextIndex;
			this.counter++;
			return this.nextIndex++;
		}
		return false;
	}
	
	this.removeElement = function(index) {
		if (this.elements[index]) {
			if (this.elements[index].prev) this.elements[ this.elements[index].prev ].next = this.elements[index].next;
			if (this.elements[index].next) this.elements[ this.elements[index].next ].prev = this.elements[index].prev;
			if (this.startIndex == index) this.startIndex = this.elements[index].next;
			if (this.endIndex == index) this.endIndex = this.elements[index].prev;

			delete this.elements[index];
			this.counter--;
			return true;
		}
		return null;
	}
	
	this.getStart = function() {
		if (this.startIndex) return this.elements[this.startIndex].el;
		return null;
	}
	
	this.getEnd = function() {
		if (this.endIndex) return this.elements[this.endIndex].el;
		return null;
	}
	
	this.getElementsArray = function() {
		var arr = [];
		var next = this.startIndex;
		while (next) {
			arr[arr.length] = this.elements[next].el;
			next = this.elements[next].next;
		}
		return arr;
	}
	
	this.getElement = function(index) {
		return this.elements[index] ? this.elements[index].el : null;
	}
	
	this.setElement = function(index, el) {
		if (this.elements[index]) {
			this.elements[index].el = el;
		}
	}
	
	this.getFirstNode = function() {
		return this.startIndex ? this.getNode(this.startIndex) : null;
	}
	
	this.getNode = function(index) {
		return this.elements[index] ? this.elements[index] : null; 
	}
}
