(function ($) {

	Array.prototype.findValue = function(value)
	{
		for (var i in this) {
			if (this[i] == value)
				return i;
			}
	}

	$.listboxGetData = function (lbox, id) {
		var data = lbox.data('data');
		return data[id];
	}
	
	$.listboxGetIdList = function (lbox) {
		var data = lbox.data('data');
		var list = new Array();
		for (var i = 0; i < data.length; i++)
			if (typeof data[i] == 'object')
				list.push(data[i]['id']);
		return list;
	}
	
	$.listboxTransposeData = function (data) {
		var tdata = new Array();
		for (var i = 0; i < data.length; i++)
			if (typeof data[i] == 'object')
				tdata[data[i]['id']] = data[i];
		return tdata;
	}
	
	$.listboxDefaultItemContent = function (id, data) {
		return '<span class="name">' + data['name'] + '</span>';
	}
	
	$.listboxDefaultItemClass = function (id, data) {
		return '';
	}
	
	$.listboxGetItemHtml = function (lbid, data, first, last, deletable, onItemContent, onItemClass) {
		var id = data['id'];
		var iid = lbid + '-' + id;
		var cls = '';
		if (first)
		{
			cls += 'first ';
		}
		if (last)
		{
			cls += 'last ';
		}
		if (ccls = onItemClass(id, data))
		{
			cls += ccls + ' ';
		}
		cls = 'class="' + cls + '{id:' + id + '}"';
		html = '<li ' + cls + ' id="' + iid + '"><span class="item clr">';
	    if (deletable)
	    {
			html += '<span class="actions"><a href="javascript:void(0)" class="delete">Delete</a></span>';
		}
		html += onItemContent(id, data);
		html += '</span></li>';
		return html;
	}

	$.listboxResetScroll = function (lbox) {
		lbox.parent('.custom-scroll').scrollbarInit();
	}
	
	$.listboxEnsureVisible = function (lbox, li) {
	// TODO: make item visible in box
	//	lbox[0].scrollTo(li);
	}
	
	$.listboxHandleDelete = function (lbox, li, ext) {
		var id = li.metadata().id;
		var res = ext ? true : lbox.triggerHandler('listboxDelete', [ id, $.listboxGetData(lbox, id) ]);
		if (res)
		{
			if (lbox.data('selectable') == 'multi' && (idx = lbox.data('selectedItemList').findValue(id)))
			{	// remove element from selection list
				var ilist = lbox.data('selectedItemList');
				ilist.splice(idx, 1);
				lbox.data('selectedItemList', ilist);
				lbox.triggerHandler('listboxSelectionChange', [ ilist ]);
			}
			else if (lbox.data('selectable') != 'multi' && id == lbox.data('selectedItem'))
			{	// clear selection
				lbox.data('selectedItem', null);
				lbox.triggerHandler('listboxSelect');
			}
			
			// ??? do we really need cleaning of data array ???
			var data = lbox.data('data');
			data.splice(id, 1);
			lbox.data('data', $.listboxTransposeData(data));
			
			li.remove();
			lbox.children('li:first').addClass('first');
			lbox.children('li:last').addClass('last');
			
			$.listboxResetScroll(lbox);
		}
	}
	
	$.listboxHandleSelect = function (lbox, li, value) {
		var id = li.metadata().id;
		var lbid = lbox.attr('id');
		if (lbox.data('selectable') == 'multi')
		{
			var ilist = lbox.data('selectedItemList');
			var idx = ilist.findValue(id);
			var selChange = true;
			if (typeof value != 'undefined')
			{	// manual selection
				if (typeof idx != 'undefined' && !value)
				{
					ilist.splice(idx, 1);
					li.removeClass('selected');
				}
				else if (typeof idx == 'undefined' && value)
				{
					ilist.push(id);
					li.addClass('selected');
				}
				else
				{
					selChange = false;
				}
			}
			else
			{	// LI element click
				if (typeof idx != 'undefined')
				{
					ilist.splice(idx, 1);
					li.removeClass('selected');
				}
				else
				{
					ilist.push(id);
					li.addClass('selected');
				}
			}
			if (selChange)
			{
				lbox.data('selectedItemList', ilist);
				lbox.triggerHandler('listboxSelectionChange', [ ilist ]);
			}
		}
		else
		{
			oldId = lbox.data('selectedItem');
			if (typeof value != 'undefined' && !value)
			{
				if (id == oldId)
				{	// manual DE-selection
					li.removeClass('selected');
					lbox.data('selectedItem', null);
					lbox.triggerHandler('listboxSelect');
				}
			}
			else
			{
				if (id != oldId)
				{
					if (typeof oldId != 'undefined')
						lbox.children('li#'+ lbid + '-' + oldId).removeClass('selected');
					lbox.data('selectedItem', id);
					li.addClass('selected');
					$.listboxEnsureVisible(lbox, li);
					lbox.triggerHandler('listboxSelect', [ id, $.listboxGetData(lbox, id) ]);
				}
			}
		}
	}
	
    $.fn.listboxInit = function (onItemContent, onItemClass) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			var md = lbox.metadata();
			var data = (typeof md.data != 'undefined') ? md.data : [];

			if (typeof onItemContent == 'undefined' || onItemContent == null)
				onItemContent = $.listboxDefaultItemContent;
			if (typeof onItemClass == 'undefined')
				onItemClass = $.listboxDefaultItemClass;
			
			lbox.data('selectable', (typeof md.selectable != 'undefined') ? md.selectable : false);
			lbox.data('deletable', (typeof md.deletable != 'undefined') ? md.deletable : false);
			lbox.data('itemContent', onItemContent);
			lbox.data('itemClass', onItemClass);
			lbox.data('data', $.listboxTransposeData(data));
			lbox.data('selectedItem', null);
			lbox.data('selectedItemList', []);

			var html = '';
			for (var i = 0; i < data.length; i++)
			{
				html += $.listboxGetItemHtml(
						lbid, 
						data[i], 
						i == 0, 
						i == data.length-1, 
						lbox.data('deletable'), 
						onItemContent,
						onItemClass
						);
			}
			lbox.empty();
			lbox.html(html);
			
			if (lbox.data('selectable'))
			{
				lbox.children('li').click (function (e) {
					e.stopPropagation();
					$.listboxHandleSelect(lbox, $(this));
				});
			}
			
			if (lbox.data('deletable'))
			{
				lbox.find('span.actions>a.delete').click(function (e) {
					e.stopPropagation();
					$.listboxHandleDelete(lbox, $(this).parents('li'));
				});
			}
				
			$.listboxResetScroll(lbox);
		});
	}

	$.fn.listboxReset = function (data, forceSelect) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			if (typeof data == 'undefined' || !data) data = [];
			
			var onItemContent = lbox.data('itemContent');
			var onItemClass = lbox.data('itemClass');
			
			lbox.data('data', $.listboxTransposeData(data));
			
			var html = '';
			for (var i = 0; i < data.length; i++)
			{
				html += $.listboxGetItemHtml(
						lbid, 
						data[i], 
						i == 0, 
						i == data.length-1, 
						lbox.data('deletable'), 
						onItemContent,
						onItemClass
						);
			}
			lbox.empty();
			lbox.html(html);
			
			if (lbox.data('selectable'))
			{	
				lbox.children('li').click (function (e) {
					e.stopPropagation();
					$.listboxHandleSelect(lbox, $(this));
				});
			}
			
			if (lbox.data('deletable'))
			{
				lbox.find('span.actions>a.delete').click(function (e) {
					e.stopPropagation();
					$.listboxHandleDelete(lbox, $(this).parents('li'));
				});
			}
			
			$.listboxResetScroll(lbox);

			if (typeof forceSelect != 'undefined' && 
				typeof forceSelect != 'boolean' && 
				forceSelect)
			{
				lbox.data('selectedItem', null);
				lbox.data('selectedItemList', []);
				lbox.listboxSelItem(forceSelect);
			}
			else if (lbox.data('selectable') == 'multi' && 
				(lbox.data('selectedItemList').length || forceSelect))
			{
				lbox.data('selectedItemList', []);
				lbox.triggerHandler('listboxSelectionChange');
			}
			else if (lbox.data('selectable') && lbox.data('selectable') != 'multi' &&
				(lbox.data('selectedItem') || forceSelect))
			{
				lbox.data('selectedItem', null);
				lbox.triggerHandler('listboxSelect');
			}
		});
	}

	$.fn.listboxSelItem = function (id, value) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			if (id == null)
			{
				if (lbox.data('selectable') == 'multi' && (lbox.data('selectedItemList').length || typeof value != 'undefined'))
				{
					if (value)
					{
						ilist = $.listboxGetIdList(lbox);
						lbox.children('li').addClass('selected');
						lbox.data('selectedItemList', ilist);
						lbox.triggerHandler('listboxSelectionChange', [ ilist ]);
					}
					else
					{
						lbox.children('li').removeClass('selected');
						lbox.data('selectedItemList', []);
						lbox.triggerHandler('listboxSelectionChange');
					}
				}
				else if (lbox.data('selectable') != 'multi' && (lbox.data('selectedItem') || typeof value != 'undefined'))
				{
					if (lbox.data('selectedItem'))
					{
						lbox.children('li#' + lbid + '-' + lbox.data('selectedItem')).removeClass('selected');
					}
					lbox.data('selectedItem', null);
					lbox.triggerHandler('listboxSelect');
				}
			}
			else
			{
				var li = lbox.children('li#' + lbid + '-' + id);
				if (li.length)
				{
					$.listboxHandleSelect(lbox, li, value);
				}
			}
		});
	}
	
	$.fn.listboxDelItem = function (id) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			var li = lbox.children('li#' + lbid + '-' + id);
			if (li.length)
			{
				$.listboxHandleDelete(lbox, li, true);
			}
		});
	}

	$.fn.listboxShowItem = function (id, show) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			if (typeof show == 'undefined') show = true;
			if (id)
			{
				var li = lbox.children('li#' + lbid + '-' + id);
				if (li.length)
				{
					if (show)
					{
						li.show();
					}
					else
					{
						lbox.listboxSelItem(id, false);
						li.hide();
					}
					$.listboxResetScroll(lbox);
				}
			}
			else
			{
				if (show)
				{
					lbox.children('li').show();
				}
				else
				{
					lbox.listboxSelItem();
					lbox.children('li').hide();
				}
				$.listboxResetScroll(lbox);
			}
		});
	}
	
	$.fn.listboxSetItemStyle = function (style, id, remove) {
		return this.each(function () {
			var lbox = $(this);
			var lbid = lbox.attr('id');
			var path = id ? ('li#' + lbid + '-' + id) : 'li';
			if (remove)
			{
				lbox.children(path).removeClass(style);
			}
			else
			{
				lbox.children(path).addClass(style);
			}
		});
	}

//	$.fn.listboxAddItem(id, data)
//	$.fn.listboxSetItem(id, data)

	$.listboxGetSelectedId = function (lbox) {
		if (lbox.data('selectable') == 'multi')
		{
			var ilist = lbox.data('selectedItemList');
			return ilist.length ? ilist[ilist.length-1] : null;
		}
		else
		{
			return lbox.data('selectedItem');
		}
	}
	
	$.listboxGetSelectedIdList = function (lbox) {
		return lbox.data('selectable') == 'multi'
			? lbox.data('selectedItemList')
			: (lbox.data('selectedItem') ? [lbox.data('selectedItem')] : []);
	}
	
	$.listboxHasId = function (lbox, id) {
		var data = lbox.data('data');
		return typeof data[id] != 'undefined';
	}

})(jQuery);
