/* Class taken from ScrollableDataTable - unmodified */
ExtendedDataTable.Selection = Class.create({
	initialize: function() {
		this.ranges = [];
	},

	addId: function(id) {
		id = parseInt(id);
		if(this.isSelectedId(id))
			return;
		var i = 0;
		while(i < this.ranges.length && id >= this.ranges[i++].indexes[1]);
		i--;
		if(this.ranges[i-1] && id==(this.ranges[i-1].indexes[1]+1) ) {
			if(id==(this.ranges[i].indexes[0]-1)) {
				this.ranges[i-1].indexes[1] = this.ranges[i].indexes[1];
				this.removeRange(i);			
			} else {
				this.ranges[i-1].indexes[1]++;			
			}
		} else {
			if(this.ranges[i]){
				if(this.ranges[i] && id==(this.ranges[i].indexes[0]-1)) {
					this.ranges[i].indexes[0]--;			
				} else {
					if(id==(this.ranges[i].indexes[1]+1)){
						this.ranges[i].indexes[1]++;			
					} else {
						if(id<this.ranges[i].indexes[1]){
							this.addRange(i, new ExtendedDataTable.Range(id, id));					
						} else {
							this.addRange(i + 1, new ExtendedDataTable.Range(id, id));					
						}
					}
				}	
			} else {
				this.addRange(i, new ExtendedDataTable.Range(id, id));					
			}	
		} 			
	},

	addRange: function(index, range) {
		var i = this.ranges.push(range) - 2;
		if(index >= 0) {
			while(i>=index)
				this.ranges[i+1] = this.ranges[i--];
			this.ranges[i+1] = range;
		}
	},

	removeRange: function(index) {
		var i = index + 1;
		while(i!=this.ranges.length)
			this.ranges[i-1] = this.ranges[i++];
		this.ranges.pop();
	},

	isSelectedId: function(id) {
		var i = 0;
		while(i < this.ranges.length && id >= this.ranges[i].indexes[0]) {
			if(id >= this.ranges[i].indexes[0] && id <= this.ranges[i].indexes[1]) {
				return true;
			} else {
				i++;
			}
		}
		return false;
	},

	getSelectedIdsQuantity: function() {
		var number = 0;
		var l = this.ranges.length;
		for (var i = 0; i < l; i++) {
			number+= this.ranges[i].size();
		}
		return number;
	},
	
	size: function () {
		return this.getSelectedIdsQuantity();
	},
	
	removeId: function(id) {
		id = parseInt(id);
		if(!this.isSelectedId(id))
			return;
		var i = 0;
		while(i < this.ranges.length && id > this.ranges[i++].indexes[1]);
		i--;
		if(this.ranges[i]) {
			if(id==(this.ranges[i].indexes[1]) ) {
				if(id==(this.ranges[i].indexes[0])){
					this.removeRange(i);			
				} else {
					this.ranges[i].indexes[1]--;			
				}
			} else {
				if(id==(this.ranges[i].indexes[0])){
					this.ranges[i].indexes[0]++;			
				} else {
				this.addRange(i+1, new ExtendedDataTable.Range(id+1, this.ranges[i].indexes[1]));			
				this.ranges[i].indexes[1] = id-1;
				}
			}
		}		
	},

	getState: function() {
		var s = this.clone();
		return {
			size: function() {
					return s.size();
			},
			
			each: function(iterator) {
				s.each(iterator);
  			},
			
			isSelected: function(id) {
				return s.isSelectedId(id);
  			}
		};
	},

	clone: function() {
		var ret =  Object.extend(new Object(),this);
		var l = ret.ranges.length;
		ret.ranges = new Array(l);
		for (var i = 0; i < l; i++) {
			ret.ranges[i] = this.ranges[i].clone();
		}		
 		return ret;
 	},

	each: function(iterator) {
		var l = this.ranges.length;
		for (var i = 0; i < l; i++) {
			this.ranges[i].each(iterator);					
		}
 	},
  	
  	getRanges: function() {
		return this.ranges;
	},

	setRanges: function(ranges) {
		this.ranges = ranges;
	},
	
	initRanges: function(rangeStrRArray) {
		if(rangeStrRArray.length == 0) {
			this.ranges = [];
			return;
		}
		this.ranges = new Array(rangeStrRArray.length);
		var indexStrRArray;
		var l = this.ranges.length;
		for(var i = 0; i < l; i++) {
			indexStrRArray = rangeStrRArray[i].split(",");
			this.ranges[i] = new ExtendedDataTable.Range(parseInt(indexStrRArray[0]), parseInt(indexStrRArray[1]));
		}
		
	}, 

	inspectRanges: function() {
		var ranges = this.getRanges();
		var ret = "";
		ranges.each( function(r) { ret += r.inspect(); } );
		return ret;
	} 
});


/* Class taken from ScrollableDataTable - unmodified */
ExtendedDataTable.Range = Class.create({
	initialize: function(startIndex, endIndex) {
		this.indexes = [startIndex, endIndex];
	},

	inspect: function() {
		return this.indexes[0] + "," + this.indexes[1] + ";";
	},
	toString: function() {
		return this.inspect();
	},
	
	size: function() {
		return this.indexes[1] - this.indexes[0] + 1;;
	},
	
	each: function(iterator) {
		var j = this.indexes[0];
		while(j <= this.indexes[1]) {
      		iterator(j++);					
		}
  	},
	
	clone: function() {
		var ret = Object.extend(new Object(),this);
		ret.indexes = this.indexes.clone();
		return ret;
  	}
});

/* Modified class from ScrollableDataTable */
ExtendedDataTable.SelectionManager = Class.create({
	initialize: function(options, owner) {
		this.dataTable = owner;
		this.options = options;
		this.selectionFlag;
		this.firstIndex;
		this.activeRow = -1;
		var element = options.gridId;
		this.gridElement = document.getElementById(element + ":n");
		
		this.prefix = options.gridId;
		this.selection = new ExtendedDataTable.Selection();

		this.inputElement = options.selectionInput;
		this.onselectionchange = options.onselectionchange;
		this.selectedClass = options.selectedClass;
		this.activeClass = options.activeClass;
		

	},
	
	refreshEvents: function() {
		this.setListeners();
		if(this.options.selectionMode != "none") {
			this.eventKeyPress = this.processKeyDown.bindAsEventListener(this);
			Event.observe(document, "keydown", this.eventKeyPress);
		}
		A4J.AJAX.AddListener({
			onafterajax: function(req, event, data) {
				if(!$(this.prefix + ":n")) {
					Event.stopObserving(document, "keydown", this.eventKeyPress);		
				}
			}.bind(this)
		});
		if (document.selection) {
			this.eventResetSelection = this.resetSelection.bindAsEventListener(this);
			Event.observe(this.gridElement, "click", this.eventResetSelection);
		}

		this.eventLostFocus = this.processLostFocus.bindAsEventListener(this);
		Event.observe(document, "click", this.eventLostFocus);

		this.eventPreventLostFocus = this.processPreventLostFocus.bindAsEventListener(this);
		Event.observe(this.gridElement, "click", this.eventPreventLostFocus);
	},

	restoreState: function() {
		this.selectionFlag = null;
		var selStrAr = $(this.inputElement).value.split(";");
		var activeRow = NaN;
		while (selStrAr.length != 0 && selStrAr[selStrAr.length - 1].indexOf(",") == -1 &&
			isNaN(activeRow = Number(selStrAr.pop())));
		if (!isNaN(activeRow)) {
			this.setActiveRow(activeRow);
		}
		this.selection.initRanges(selStrAr);
		var i = 0;
		var j;
		while(i < this.selection.ranges.length) {
			j = this.selection.ranges[i].indexes[0];
			while(j <= this.selection.ranges[i].indexes[1]) {
				var nElement = $(this.prefix + ":n:" + j);
				Element.addClassName(nElement, "extdt-row-selected");
				Element.addClassName(nElement, "rich-sdt-row-selected");
				Element.addClassName(nElement, this.selectedClass);
				j++;
			}
			i++;
		}
		this.oldState = this.selection.getState();
	},
	
	setListeners: function() {
		//May need optimization by attaching listeners to whole rows instead of all cells
		var nrows = $(this.prefix + ":n").rows;
		this.rowCount = nrows.length;
		var rowIndex;
		var groupingExists = $(this.prefix + ":group-row:0") != null;
		if(!groupingExists) { //simple listener binding
			if(this.options.selectionMode != "none") {
				for(var i = 0; i < this.rowCount; i++) {
					rowIndex = Number(nrows[i].id.split(this.prefix)[1].split(":")[2]);
					this.addListener(nrows[i], rowIndex);
				}
			}
		} else { //extended listener binding with grouping
			var groupRow;
			var lastGroupId = 0;
			var bGroupExpanded = true;
			var bHideFirstRow = false;
			if (ClientUILib.isIE) {
				//hide first fake ie row to ensure proper rendering
				bHideFirstRow = true;
			};
			var groupId;
			var groupItems = [];
			var groupItem = 0;
			var groups = [];
			var groupRows = this.dataTable.groupRows;
			for(var i = 0; i < this.rowCount; i++) {
				tempo = nrows[i].id.split(this.prefix)[1];
				var tempArr = tempo.split(":")
				groupRow = tempArr[1] == "group-row";
				groupId = Number(tempArr[2]);
				if(groupRow) {
					groups[lastGroupId] = groupItems;
					bGroupExpanded = (groupRows[groupId].getAttribute('expanded') == 'true');
					var textSpan = groupRows[lastGroupId].lastChild.lastChild;
					var txtNode = document.createTextNode("(" + groupItems.size() + ")");
					if (textSpan.lastChild) {
						textSpan.replaceChild(txtNode, textSpan.lastChild);
					}else{
						textSpan.appendChild(txtNode);
					}
					groupItem = 0;
					groupItems = [];
					lastGroupId = groupId;
				} else {
					if(this.options.selectionMode != "none") {
						rowIndex = Number(nrows[i].id.split(this.prefix)[1].split(":")[2]);
						this.addListener(nrows[i], rowIndex);
					}
					groupItems[groupItem++] = nrows[i];
					if ( (i==0) && (bHideFirstRow) ) {
					   
					}
					if (!bGroupExpanded) {
						nrows[i].style.display = 'none';
						if ((ClientUILib.isIE) && (i!=0)){
							//prevent IE from showing borders of cells
							//which parents have been hidden :|
							var cells = nrows[i].childNodes;
							var l = cells.length;
							for (var j = 0; j < l; j++) {
								cells[j].style.borderStyle = 'none';
							}
						}						
					}
				}
			}
			groups[lastGroupId] = groupItems;
			var textSpan = groupRows[lastGroupId].lastChild.lastChild;
			var txtNode = document.createTextNode("(" + groupItems.size() + ")");
			if (textSpan.lastChild) {
				textSpan.replaceChild(txtNode, textSpan.lastChild);
			}else{
				textSpan.appendChild(txtNode);
			}			
			this.dataTable.groups = groups;
		}
	},
	/*
		Modification: instead of providing listener for each cell,
		one is provided for a whole row
	*/
	addListener: function(tr, rowIndex) {
		if (tr) {
			var listener = this.processClick.bindAsEventListener(this, rowIndex);
			Utils.DOM.Event.removeListeners(tr);
			Utils.DOM.Event.observe(tr, "click", listener);	
		}
	},
	
	removeListeners: function(){
		Event.stopObserving(document, "keydown", this.eventKeyPress);
		if (document.selection) {
			Event.stopObserving(this.gridElement, "click", this.eventResetSelection);
		}
		Event.stopObserving(document, "click", this.eventLostFocus);
		Event.stopObserving(this.gridElement, "click", this.eventPreventLostFocus);
		if(this.options.selectionMode != "none") {
			var nrows = $(this.prefix + ":n").rows;
			var rowCount = nrows.length;
			for(var i = 0; i < this.rowCount; i++) {
				Utils.DOM.Event.removeListeners(nrows[i]);	
			}
		}
	},
	
/*	getGridSelection: function() {
		return this.selection.getRanges();
	},*/

	processPreventLostFocus: function() {
		this.inFocus = true;
		this.preventLostFocus = true;
	},

	processLostFocus: function() {
		if (!this.preventLostFocus) {
			this.lostFocus();
		} else {
			this.preventLostFocus = false;
		}
	},

	lostFocus: function() {
		this.inFocus = false;
	},

	processKeyDown: function(event) {
		if ($(this.prefix + ":n").rows.length > 0) {
			if(!event.shiftKey) {
				this.shiftRow = null;
			}		
			var range, rowIndex;
			var activeRow = this.activeRow;
			var noDefault = false;
			this.firstIndex = Number($(this.prefix + ":n").rows[0].id.split(this.prefix)[1].split(":")[2]);;
			switch (event.keyCode || event.charCode) {
				case Event.KEY_UP:
					if (this.inFocus && activeRow != null) {
						if(this.firstIndex != activeRow) {
							rowIndex = (this.rowCount + activeRow - 1) % this.rowCount;		
							if (!event.ctrlKey && !event.shiftKey) {
								this.selectionFlag = "x";
								range = [rowIndex, rowIndex];
								this.setSelection(range);		
							} else if (!event.ctrlKey && event.shiftKey) {
								if(!this.shiftRow) {
									this.shiftRow = this.activeRow;
								}
								if(this.shiftRow >= this.activeRow) {
									this.addRowToSelection(rowIndex);						
								} else {
									this.removeRowFromSelection(activeRow);						
								}
							}
							noDefault = true;
							this.setActiveRow(rowIndex);
						} else {
							//this.grid.getBody().showRow("up");					
						}
					}
					break;
				case Event.KEY_DOWN:
					if (this.inFocus && activeRow != null) {
						rowIndex = (activeRow + 1) % this.rowCount;		
						if(this.firstIndex != rowIndex) {
							if (!event.ctrlKey && !event.shiftKey) {
								this.selectionFlag = "x";
								range = [rowIndex, rowIndex];
								this.setSelection(range);		
							} else if (!event.ctrlKey && event.shiftKey) {
								if(!this.shiftRow) {
									this.shiftRow = this.activeRow;
								}
								if(this.shiftRow <= this.activeRow) {
									this.addRowToSelection(rowIndex);						
								} else {
									this.removeRowFromSelection(activeRow);						
								}
							}
							noDefault = true;
							this.setActiveRow(rowIndex);
						} else {
							//this.grid.getBody().showRow("down");					
						}
					}
					break;
				case 65: case 97:								// Ctrl-A
					if (this.inFocus && event.ctrlKey) {
						this.selectionFlag = "a";
						for (var i = 0; i <  this.rowCount; i++) {
							this.addRowToSelection(i);
						}
						noDefault = true;
					}
					break;
				case Event.KEY_TAB:
					this.lostFocus();
			}
			if (noDefault) {
				this.dataTable.showRow(this.activeRow);
				this.selectionChanged(event);			
				if (event.preventBubble) event.preventBubble();
				Event.stop(event);
			}
		}
	},
	
	/*
		Modified method:
		Component supports three selection modes:
		none - no selection allowed
		single - only one row can be selected at a time (no ctrl or shift)
		multi - normal full-featured selection mode
	*/
	processClick: function(event, rowIndex) {
		if (this.options.selectionMode == "none") {
			return;
		}

		var bSingleSelection = this.options.selectionMode == "single";

		if(!event.shiftKey) {
			this.shiftRow = null;
		}		
		var range;	
		
		if ( event.shiftKey && !event.ctrlKey && !bSingleSelection && !event.altKey) {
			this.firstIndex = Number($(this.prefix + ":n").rows[0].id.split(this.prefix)[1].split(":")[2]);;
			this.selectionFlag = "x";
			if(!this.shiftRow) {
				this.shiftRow = this.activeRow;
			}
			this.startRow = this.shiftRow;
			if (((this.startRow <= rowIndex) && (this.firstIndex <= this.startRow || rowIndex < this.firstIndex))
				|| (this.startRow > rowIndex && this.firstIndex < this.startRow && rowIndex <= this.firstIndex)) {
				this.endRow = rowIndex;
			} else {
				this.endRow = this.startRow;
				this.startRow = rowIndex;
			}
			if(this.startRow > this.endRow) { //bugfix
				//without this selection of first row with shift was broken
				var t = this.startRow;
				this.startRow = this.endRow;
				this.endRow = t;
			}
			range = [this.startRow, this.endRow];
			this.setSelection(range);		
		} else if (!event.shiftKey &&  event.ctrlKey && !event.altKey) {
			if (this.selection.isSelectedId(rowIndex)) {
				this.removeRowFromSelection(rowIndex);
			} else {
				if (!bSingleSelection || this.selection.size() == 0) {
					this.addRowToSelection(rowIndex);
				}
			}
		} else  if (!event.shiftKey && !event.ctrlKey && !event.altKey) {
			this.selectionFlag = "x";
			range = [rowIndex, rowIndex];
			this.setSelection(range);		
		}
		this.setActiveRow(rowIndex);
		if (event.shiftKey) {
			if (window.getSelection) {
				window.getSelection().removeAllRanges();
			} else if (document.selection) {
				document.selection.empty();
			}
		}
		this.selectionChanged(event);
	},
	
	selectionChanged: function(event) {
		$(this.inputElement).value = this.selection.inspectRanges() + this.activeRow + ";" + (this.selectionFlag ? this.selectionFlag : "") ;
		var state = this.selection.getState();			
		event.oldSelection = this.oldState;
		event.newSelection = state;
		if(this.onselectionchange) this.onselectionchange(event);
		this.oldState = state;
	},

	setShiftRow: function(event) {
		if(event.shiftKey) {
			if(!this.shiftRow) {
				this.shiftRow = this.activeRow;
			}
		} else {
			this.shiftRow = null;		
		}	
	},
	
	setSelection: function(range) {
		//TODO test for extreemes - first rows, last rows, all rows
		var l = this.selection.ranges.length;
		for(var i = l - 1;i >= 0; i--) {
			var selection = this.selection.ranges[i].indexes;
			if(selection == range) {
				continue;
			}
			var j = selection[0];
			for(j;j <= selection[1];j++) {
				this.removeRowFromSelection(j);
			}
		}
		var i = range[0];
		range[1] = (range[1] + 1) % this.rowCount;	
		while (i != range[1]) {
			this.addRowToSelection(i);
			i = (i + 1) % this.rowCount;	
		}
	},
	
	resetSelection: function(e) {
		if(e.shiftKey) {
			document.selection.empty();
		}
	},
	
	addRowToSelection: function(rowIndex) {
		this.selection.addId(rowIndex);
		var nElement = $(this.prefix + ":n:" + rowIndex);
		Element.addClassName(nElement, "extdt-row-selected");
		Element.addClassName(nElement, "rich-sdt-row-selected");
		Element.addClassName(nElement, this.selectedClass);
	},

	removeRowFromSelection: function(rowIndex) {
		this.selection.removeId(rowIndex);
		var nElement = $(this.prefix + ":n:" + rowIndex);
		Element.removeClassName(nElement, "extdt-row-selected");
		Element.removeClassName(nElement, "rich-sdt-row-selected");
		Element.removeClassName(nElement, this.selectedClass);
	},

	setActiveRow: function(rowIndex) {
		var fElement, nElement;
		if(this.activeRow != null) {
			nElement = $(this.prefix + ":n:" + this.activeRow);
			Element.removeClassName(nElement, "extdt-row-active");
			Element.removeClassName(nElement, "rich-sdt-row-active");
			Element.removeClassName(nElement, this.activeClass);
		}
		nElement = $(this.prefix + ":n:" + rowIndex);
		Element.addClassName(nElement, "extdt-row-active");
		Element.addClassName(nElement, "rich-sdt-row-active");
		Element.addClassName(nElement, this.activeClass);
		this.activeRow = rowIndex;
	}
});
