// ------------------------------------------------
// selectHelper component
// ------------------------------------------------

function selectHelper(data, opt) {
	var parent_this = this;
	if (opt) { 
		this.opt = $.extend(this.opt, opt);
	}
	this.input = $("<input type='text'/>");
	this.list = new listSelectable(data);
	this.data = data;

	this.input.keypress( function (event) {
		if (event.keyCode == 27)  {//ESC
			parent_this.list.selectByKey(null);
		}
		if (event.keyCode == 13)  {//Enter (first in list)
			var key = parent_this.list.walkKey;
			if (key === null) {
				for (var i in parent_this.data) {
					if (parent_this.list.isKeyVisible(i)) {
						key = i;
						break;
					}
				}
			}
			parent_this.list.selectByKey(key);
		}
	});

	this.input.keydown( function (event) {
		if (event.keyCode == 40)  {//DOWN
			parent_this.list.walk(1);
			return;
		}
		if (event.keyCode == 38)  {//UP
			parent_this.list.walk(-1);
			return;
		}
	});

	this.input.keyup( function (event) {
		parent_this.refresh();
	});
	
	this.input.blur( function() {
		setTimeout( function() {
			if (!parent_this._blurCancel) {
				parent_this.list.selectByKey(null);
			}
		}, 200);
		
	});
	
	this.list.select( function(key) { 
		if (key !== null) {
			parent_this._blurCancel = true;
		}
	});
	this.refresh();

}
	
selectHelper.prototype = {
	input: null,
	list: null,
	data: null,
	opt: {
		minInput: 0
	},

	_blurCancel: false,


	isMatch: function(str, matchStr) {
		if (this.opt.minInput > matchStr.length) {
			return false;
		}

		if (!matchStr) {                                           
			return true;
		}
		return str.toLowerCase().indexOf(matchStr.toLowerCase()) >= 0;
	},

	refresh: function() {
		var searchStr = this.input.val();
		for (var key in this.data ) {
			this.list.displayKey(key, this.isMatch(this.data[key].title, searchStr));
		}
	},

	appendTo: function(parentElement) {
		$(parentElement).append(this.input);
		this.list.appendTo(parentElement);
		return this;
	},
	
	setFocus: function() {
		this.input.focus();
		return this;
	}
}


// ------------------------------------------------
// listSelectable component
// ------------------------------------------------

function listSelectable(data) {
	this.init(data);
}

listSelectable.prototype = {
	list: null,
	data: null,
	selectedKey: null,
	_listPtr: [],
	_listKeyPtr: {},
	_onSelectFunction: [],

		walkKey: null,

	init: function(data) {
		var parent_this = this;
		this.data = data;
		this.list = $('<ul/>');
		parent_this._listPtr = [];
		parent_this._listKeyPtr = {};
		var loopFunc = function(key, value) {
			//var e = $("<li>" + value + "</li>");
			//e.click(function() {
			//	parent_this._onSelect(key);
			//}); 
			//e.appendTo(parent_this.list);
			//
			parent_this._listPtr.push( {'key':key, 'obj': null, 'visible': false, 'id':value.id} );
			parent_this._listKeyPtr[key] =  parent_this._listPtr.length - 1;
		}
		for (var key in data ) {
			loopFunc(key, data[key]);
		}

	},

	// Отлоденное создание элементов
	_createLiElement: function(ptrIndex) {
		var parent_this = this;
		var ptr = this._listPtr[ptrIndex];
		if (ptr.obj) {
			return;
		}
		var key = ptr.key;
		var value = this.data[key].title;

		var e = $("<li>" + value + "</li>");
		e.click(function() {
			parent_this._onSelect(key);
		}); 
		prevLi = null;
		for (var i=ptrIndex - 1; i>=0; --i) {
			if (this._listPtr[i].obj) {
				prevLi = this._listPtr[i].obj;
				break;
			}
		}
		if (prevLi) {
			prevLi.after(e);
		}
		else {
			e.prependTo(parent_this.list);
		}
		this._listPtr[ptrIndex].obj = e;
	},

	select: function(func) {
		this._onSelectFunction.push(func);
		return this;
	},

	appendTo: function(parentElement) {
		$(parentElement).append(this.list);
		return this;
	},

	displayKey: function(key, value) {
		var ind = this._listKeyPtr[key];
		ptr = this._listPtr[ind];

		if (ptr.visible == value) {
			return;
		}

		if(value) {
			this._createLiElement(ind);
			ptr.visible = true;
			ptr.obj.show();
		}
		else {
			ptr.visible = false;
			ptr.obj.hide();
			if (this.walkKey === key) {
				this.walkKey = null;
				ptr.obj.removeClass('selected');
			}

		}
	},

	selectByKey: function(key) {
		this._onSelect(key);
	},

	isKeyVisible: function(key) {
		var ind = this._listKeyPtr[key];
		return this._listPtr[ind].visible;
	},

	walk: function(delta) {
		var visibleList = [];
		var ind = null;
		for (var i in this._listPtr) {
			if (this._listPtr[i].visible) {
				if (this._listPtr[i].key === this.walkKey) {
					ind = visibleList.length;
				}
				visibleList.push(i);
			}
		}
		if (ind === null) {
			ind = delta < 0 ? (visibleList.length + delta ) : (delta - 1);
		}
		else {
				ind += delta;
		}
		ind = ind % visibleList.length

		// +1 for unselect
		if (ind < 0 ) {
			ind = visibleList.length + ind;
		} 

		var newKey = this._listPtr[visibleList[ind]].key;
		if (this.walkKey !== null) {
			var ind = this._listKeyPtr[this.walkKey];
			this._listPtr[ind].obj.removeClass('selected');
		}
		this.walkKey = newKey;
		if (this.walkKey !== null) {
			var ind = this._listKeyPtr[this.walkKey];
			this._listPtr[ind].obj.addClass('selected');
		}
	},                                    

	_onSelect: function(key) {
		this.selectedKey = key;
		for (var i in this._onSelectFunction) {
			this._onSelectFunction[i](this._listPtr[key].id);
		}
	}
}

