/**
 * jQuery Editable Select
 * Indri Muska <indrimuska@gmail.com>
 *
 * Source on GitHub @ https://github.com/indrimuska/jquery-editable-select
 */

+(function ($) {
	// jQuery Editable Select
	EditableSelect = function (select, options) {
		var that     = this;
		
		this.options = options;
		this.$select = $(select);
		this.$wrapper = $('<div class="es"></div>');
		this.$input  = $('<input type="text" autocomplete="off">');
		this.$list   = $('<ul class="es-list">');
		this.utility = new EditableSelectUtility(this);
		this.tid;
		
		if (['focus', 'manual'].indexOf(this.options.trigger) < 0) this.options.trigger = 'focus';
		if (['default', 'fade', 'slide'].indexOf(this.options.effects) < 0) this.options.effects = 'default';
		if (isNaN(this.options.duration) && ['fast', 'slow'].indexOf(this.options.duration) < 0) this.options.duration = 'fast';
		
		// create text input
		//this.$select.replaceWith(this.$input);
		//this.$list.appendTo(this.options.appendTo || this.$input.parent());
		var id = this.$select.attr('id');
		this.$wrapper.attr('id', id + '_es');
		this.$select.after(this.$wrapper);
		this.$input.appendTo(this.options.appendTo || this.$wrapper);
		this.$list.appendTo(this.options.appendTo || this.$wrapper);
		
		// initalization
		this.utility.initialize();
		this.utility.initializeList();
		this.utility.initializeInput();

		this.$select.hide();
		this.utility.trigger('created');
	}
	EditableSelect.DEFAULTS = { filter: true, effects: 'default', duration: 'fast', trigger: 'focus' };
	EditableSelect.prototype.filter = function () {
		var hiddens = 0;
		var search  = this.$input.val().toLowerCase().trim();
		var that = this;
		var $list = that.$list;

		// add 22.10.2018 for remote ajax json source
		if (this.options.source) {
			var source = this.options.source;

			if (!source || !search) {
				return false;
			}

			function checkSelectedItem(arr) {
				if (arr.length != undefined) {
					for (var i = 0; i < arr.length; i++) {
						if ($('#' + that.$select[0].id + '_input').val() == arr[i].name._text) {
							return true;
						}
					}
					return false;
				} else {
					if ($('#' + that.$select[0].id + '_input').val() == arr.name._text) {
						return true;
					} else return false;
				}
			}

			function parseResponse(response) {
				var arr = Common.xmlToJson(response);
				var $list = that.$list;
				$list.find('li').not('.other-option').remove();

				if (arr['ns2:namedlist'].item) {
					if (checkSelectedItem(arr['ns2:namedlist'].item)) {
					} else {
						if (arr['ns2:namedlist'].item.length == undefined) {

							var name = {_text: arr['ns2:namedlist'].item.name._text};
							var _attributes = {id: arr['ns2:namedlist'].item._attributes.id};
							arr['ns2:namedlist'].item = [];
							arr['ns2:namedlist'].item.push({name: name, _attributes: _attributes});
						}
						// arr['ns2:namedlist'].item.sort((a,b) => (a.name._text < b.name._text) ? 1 : ((b.name._text < a.name._text) ? -1 : 0));
						for (var i = 0; i < arr['ns2:namedlist'].item.length-1; i++) {
							for (var j = 0; j < arr['ns2:namedlist'].item.length - 1 - i; j++) {
								if (arr['ns2:namedlist'].item[j].name._text < arr['ns2:namedlist'].item[j + 1].name._text) {
									var swap = arr['ns2:namedlist'].item[j];
									arr['ns2:namedlist'].item[j] = arr['ns2:namedlist'].item[j+1];
									arr['ns2:namedlist'].item[j+1] = swap;
								}
							}
						}
						$.each(arr['ns2:namedlist'].item, function (index, item) {
							$list.prepend('<li id="project-selected-executor" v-bind:value="' + item._attributes.id + '">' + item.name._text + '</li>');
						});
						$list.find('li').addClass('es-visible').show();
						if ($list.find('li').not('.other-option').length) {
							$list.stop().fadeIn(that.options.duration);
						} else {
							$list.stop().fadeOut(that.options.duration);
						}
					}
				}
			}
			/*
			if (that.tid) clearTimeout(that.tid);
			that.tid = setTimeout(function() {
				$.ajax({
					type: 'post',
					url: source.replace('%search%', search),
					data: {search: search},
					dataType: 'json',
					success: function(response) {
						parseResponse(response);
					}
				});
			}, 750);			
			*/

			// FIXME TEST
			var response = [];
			if (typeof(that.tid) != 'undefined') clearTimeout(that.tid);
			var value = $('#' + that.$select[0].id + '_input').val();
			// if (!valueSelected(value)) {
				source = source + '?pagingCount=10&pagingFilter=' + value;
				var currentUser = JSON.parse(localStorage.getItem('currentUser'));
				that.tid = setTimeout(function() {
					setTimeout(function() {
						$.ajax({
							type: 'GET',
							url:  Application.getBasePath() + source,
							headers: { 'Authorization': 'Basic ' + currentUser.hash, 'charset': 'UTF-8'},
							dataType: "text",
							async: false,
							contentType: 'application/xml;charset=UTF-8',
							success: function(response) {
								parseResponse(response);
							}
						});
					}, 400);
				}, 450);
			// }

			// / FIXME TEST

		} else {
			var $list = that.$list;
			$list.find('li').addClass('es-visible').show();
			if (this.options.filter) {
				hiddens = this.$list.find('li').filter(function (i, li) { return $(li).text().toLowerCase().indexOf(search) < 0; }).hide().removeClass('es-visible').length;
				if (this.$list.find('li').length == hiddens) this.hide();
			}
		}
	};
	EditableSelect.prototype.show = function () {
		this.$list.css({
			top:   this.$input.position().top + this.$input.outerHeight() - 1,
			left:  this.$input.position().left,
			width: this.$input.outerWidth()
		});
		
		if (!this.$list.is(':visible') && this.$list.find('li.es-visible').length > 0) {
			var fns = { default: 'show', fade: 'fadeIn', slide: 'slideDown' };
			var fn  = fns[this.options.effects];
			
			this.utility.trigger('show');
			this.$input.addClass('open');
			this.$list[fn](this.options.duration, $.proxy(this.utility.trigger, this.utility, 'shown'));
		}
	};
	EditableSelect.prototype.hide = function () {
		var fns = { default: 'hide', fade: 'fadeOut', slide: 'slideUp' };
		var fn  = fns[this.options.effects];
		
		this.utility.trigger('hide');
		this.$input.removeClass('open');
		this.$list[fn](this.options.duration, $.proxy(this.utility.trigger, this.utility, 'hidden'));
	};
	EditableSelect.prototype.select = function ($li) {
		if (!this.$list.has($li) || !$li.is('li.es-visible:not([disabled])')) return;
		this.$input.val($li.text());
		if (this.options.filter) this.hide();
		this.filter();
		this.utility.trigger('select', $li);
	};
	EditableSelect.prototype.add = function (text, index, attrs, data) {
		var $li     = $('<li>').html(text);
		var $option = $('<option>').text(text);
		var last    = this.$list.find('li').length;
		
		if (isNaN(index)) index = last;
		else index = Math.min(Math.max(0, index), last);
		if (index == 0) {
		  this.$list.prepend($li);
		  this.$select.prepend($option);
		} else {
		  this.$list.find('li').eq(index - 1).after($li);
		  this.$select.find('option').eq(index - 1).after($option);
		}
		this.utility.setAttributes($li, attrs, data);
		this.utility.setAttributes($option, attrs, data);
		this.filter();
	};
	EditableSelect.prototype.remove = function (index) {
		var last = this.$list.find('li').length;
		
		if (isNaN(index)) index = last;
		else index = Math.min(Math.max(0, index), last - 1);
		this.$list.find('li').eq(index).remove();
		this.$select.find('option').eq(index).remove();
		this.filter();
	};
	EditableSelect.prototype.clear = function () {
		this.$list.find('li').remove();
		this.$select.find('option').remove();
		this.filter();
	};
	EditableSelect.prototype.destroy = function () {
		this.$list.off('mousemove mousedown mouseup');
		this.$input.off('focus blur input keydown');
		this.$input.replaceWith(this.$select);
		this.$list.remove();
		this.$select.removeData('editable-select');
	};
	
	// Utility
	EditableSelectUtility = function (es) {
		this.es = es;
	}
	EditableSelectUtility.prototype.initialize = function () {
		var that = this;
		that.setAttributes(that.es.$input, that.es.$select[0].attributes, that.es.$select.data());
		if (that.es.$input.attr('id')) that.es.$input.attr('id', that.es.$input.attr('id') + '_input');
		that.es.$input.addClass('es-input').data('editable-select', that.es);
		that.es.$select.find('option').each(function (i, option) {
			var $option = $(option).remove();
			that.es.add($option.text(), i, option.attributes, $option.data());
			if ($option.attr('selected')) that.es.$input.val($option.text());
		});
		that.es.filter();
	};
	EditableSelectUtility.prototype.initializeList = function () {
		var that = this;
		that.es.$list
			.on('mousemove', 'li:not([disabled])', function () {
				that.es.$list.find('.selected').removeClass('selected');
				$(this).addClass('selected');
			})
			.on('mousedown', 'li', function (e) {
				if ($(this).is('[disabled]')) e.preventDefault();
				else that.es.select($(this));
			})
			.on('mouseup', function () {
				that.es.$list.find('li.selected').removeClass('selected');
			});
	};
	EditableSelectUtility.prototype.initializeInput = function () {
		var that = this;
		switch (this.es.options.trigger) {
			default:
			case 'focus':
				that.es.$input
					.on('focus', $.proxy(that.es.show, that.es))
					.on('blur', $.proxy(that.es.hide, that.es));
				break;
			case 'manual':
				break;
		}
		that.es.$input.on('input propertychange', function (e) {
			switch (e.keyCode) {
				case 38: // Up
					var visibles = that.es.$list.find('li.es-visible:not([disabled])');
					var selectedIndex = visibles.index(visibles.filter('li.selected'));
					that.highlight(selectedIndex - 1);
					e.preventDefault();
					break;
				case 40: // Down
					var visibles = that.es.$list.find('li.es-visible:not([disabled])');
					var selectedIndex = visibles.index(visibles.filter('li.selected'));
					that.highlight(selectedIndex + 1);
					e.preventDefault();
					break;
				case 13: // Enter
					if (that.es.$list.is(':visible')) {
						that.es.select(that.es.$list.find('li.selected'));
						e.preventDefault();
					}
					break;
				case 9:  // Tab
				case 27: // Esc
					that.es.hide();
					break;
				case 16:
				case 17:
				case 18:
				case 20:
				case 33:
				case 34:
				case 35:
				case 36:
					break;
				default:
					that.es.filter();
					that.highlight(0);
					break;
			}
		});
	};
	EditableSelectUtility.prototype.highlight = function (index) {
		var that = this;
		that.es.show();
		setTimeout(function () {
			var visibles         = that.es.$list.find('li.es-visible');
			var oldSelected      = that.es.$list.find('li.selected').removeClass('selected');
			var oldSelectedIndex = visibles.index(oldSelected);
			
			if (visibles.length > 0) {
				var selectedIndex = (visibles.length + index) % visibles.length;
				var selected      = visibles.eq(selectedIndex);
				var top           = selected.position().top;
				
				selected.addClass('selected');
				if (selectedIndex < oldSelectedIndex && top < 0)
					that.es.$list.scrollTop(that.es.$list.scrollTop() + top);
				if (selectedIndex > oldSelectedIndex && top + selected.outerHeight() > that.es.$list.outerHeight())
					that.es.$list.scrollTop(that.es.$list.scrollTop() + selected.outerHeight() + 2 * (top - that.es.$list.outerHeight()));
			}
		});
	};
	EditableSelectUtility.prototype.setAttributes = function ($element, attrs, data) {
		$.each(attrs || {}, function (i, attr) { $element.attr(attr.name, attr.value); });
		$element.data(data);
	};
	EditableSelectUtility.prototype.trigger = function (event) {
		var params = Array.prototype.slice.call(arguments, 1);
		var args   = [event + '.editable-select'];
		args.push(params);
		this.es.$select.trigger.apply(this.es.$select, args);
		this.es.$input.trigger.apply(this.es.$input, args);
	};
	
	// Plugin
	Plugin = function (option) {
		var args = Array.prototype.slice.call(arguments, 1);
		return this.each(function () {
			var $this   = $(this);
			var data    = $this.data('editable-select');
			var options = $.extend({}, EditableSelect.DEFAULTS, $this.data(), typeof option == 'object' && option);
			
			if (!data) data = new EditableSelect(this, options);
			if (typeof option == 'string') data[option].apply(data, args);
		});
	}
	$.fn.editableSelect             = Plugin;
	$.fn.editableSelect.Constructor = EditableSelect;
	
})(jQuery);