/**
 * Show suggestions for query of an input.
 *
 * The `.remotesearchUiSuggest()` is only applicable to input elements from type text.
 *
 * This widget displays suggests for the current value of the input field while typing.
 *
 * ### Examples
 * Apply .remotesearchUiSuggest() to input field with id 'ProductSearchField'.
 *
 * JavaScript:
 *
 *     de_epages('#ProductSearchField')
 *         .remotesearchUiSuggest({
 *             suggestUrl: '?ViewAction=ViewRemoteSearchSuggests&ObjectID=12345',
 *             searchFilter: 'input[name=filter]',
 *             suggestMax: 8,
 *             showImages: false
 *         });
 *
 * HTML:
 *
 *     <form action="?ViewAction=ViewRemoteSearchResults&ObjectID=12345" method="post">
 *       <input type="hidden" name="filter"/>
 *       <input type="text" name="SearchString" id="ProductSearchField" value=""/>
 *       <button type="submit" name="SearchButton">{Search}</button>
 *     </form>
 *
 *
 * @class jQuery.ui.remotesearchUiSuggest
 * @extends jQuery.widget
 *
 * @uses jQuery.json
 * @uses jQuery.tmpl
 * @uses jQuery.ui.widget
 * @uses ep
 * @uses jQuery.dict
 * @uses ep.fn.contextOrientation
 * @uses ep.ui.validate
 * @uses de_epages
 * @since 6.14.0
 */

/**
 * @cfg {String} [searchFilter] A string containing a selector expression, a DOM element, an existing jQuery object for the filter input.
 */

/**
 * @cfg {String} suggestUrl A url to get suggestions for the query, the server needs to support JSONP via the callback argument.
 */

/**
 * @cfg {Integer} [suggestChars] An integer value of minimum required characters to start the suggest search.
 */

/**
 * @cfg {Integer} [suggestMax] An integer value of maximum displayed suggestions.
 */

/**
 * @cfg {Boolean} [showImages] A boolean indication whether to display images in the suggest list.
 */

/**
 * See `jQuery.ui.remotesearchUiSuggest` for details.
 *
 * @param {Object} [options] A map of additional options pass to the method.
 * @param {String} [searchFilter] A string containing a selector expression, a DOM element, an existing jQuery object for the filter input.
 * @param {String} suggestUrl A url to get suggestions for the query, the server needs to support JSONP via the callback argument.
 * @param {Integer} [suggestChars] An integer value of minimum required characters to start the suggest search.
 * @param {Integer} [suggestMax] An integer value of maximum displayed suggestions.
 * @param {Boolean} [showImages] A boolean indication whether to display images in the suggest list.
 *
 * @method remotesearchUiSuggest
 * @member jQuery
 *
 * @since 6.14.0
 */

/*
 * @copyright		© Copyright 2006-2011, epages GmbH, All Rights Reserved.
 *
 * @module			de_epages.remotesearch.ui.suggest
 *
 * @revision		$Revision: 1.21 $
 */

define("de_epages/remotesearch/ui/suggest", [
	"jquery",
	"ep",
	"de_epages",
	"util/string",
	"$dict!../dictionary",
	"$tmpl!./suggest",

	"jquery/ui/widget",
	"ep/fn/contextorientation",
	"ep/ui/validate"
], function ($ , ep, de_epages, str, remotesearchDict, tmplSuggest) {

/*
 * @dictionary			de_epages.remotesearch.dictionary
 *
 * @translation			{Products}
 *						{Categories}
 *						{Manufacturers}
 *						{SearchFor}
 */

 	var	tmplDict = {
			products		: remotesearchDict.translate("Products"),
			categories		: remotesearchDict.translate("Categories"),
			manufacturers	: remotesearchDict.translate("Manufacturers"),
			searchfor		: remotesearchDict.translate("SearchFor")
		};

	$.widget( "ui.remotesearchUiSuggest", $.ui.uiValidate, {

		options: {
		//	searchFilter	: "selector input",
		//	suggestUrl		: "?",
			suggestChars	: 1,
			suggestMax		: 10,
			showImages		: true
		},

		_create: function(){
			this._superApply(arguments);

			var self = this,
				o = self.options;

			// hidden input for filter options
			self.filter = o.filter ? $(o.searchFilter) : $('<input type="hidden"/>').insertAfter(self.elem);

			self.elem
				.attr("autocomplete","off")
				.on("keydown", $.proxy(self, "_keydown"))
				.on("keyup", $.proxy(self, "_keyup"))
				.on("focus", $.proxy(self, "_show"))
				.on("blur", $.proxy(self, "_hide"));

			// list container
			self.list = $('<div class="de_epages-remotesearchUiSuggest-box"/>')
				.on('mouseenter','li',function(){
					$(this).addClass('ui-hover');
				})
				.on('mouseleave','li',function(){
					$(this).removeClass('ui-hover');
				})
				.on('mousedown','li:not(.Separator)',function( event ){
					self._select( $(this).data('index') );
					self._action( event );
					// move action in other tread
					setTimeout(function(){
						self.elem.trigger('focus');
					},1);

				});
			// caching for fast suggests
			self.cache = {};
			self.dataModels = {};
			// default selected index of suggest items
			self.selectedIndex = -1;
			// setup an object like jqXHR to provide an abort method
			self.xhr = {abort: $.noop};
			// basic shr settings
			self.xhrSettings = {
				type	: "get",
				dataType: "jsonp",
				data	: {
//					datasource	: ep.config.storeName,
//					shopGUID	: ep.config.siteGuid,
//					lang		: ep.config.language

				}
			};
			if( o.suggestUrl ){
				self.xhrSettings.url = o.suggestUrl;
			}
		},

		_keydown: function( event ){
			var keyCode = event.keyCode || event.which;
			if( keyCode == 38 || keyCode == 40 ){
				event.preventDefault();
			}
			else if( keyCode == 13 ){
				this._action( event );
			}
		},

		_keyup: function( event ){
			var self	= this,
				keyCode = event.keyCode || event.which,
				query	= self.elem.val(),
				o		= self.options;

			if( keyCode == 9 || keyCode == 13 || keyCode == 37 || keyCode == 39 || keyCode == 32 ){
				return;
			}
			else if( keyCode == 38 ){
				self._select( 'prev' );
			}
			else if( keyCode == 40 ){
				self._select( 'next' );
			}
			else if( query.length < self.options.suggestChars ){
				self._clear();
			}
			else if( self.cache[ query ] ){
				self._build();
			}
			else{
				if (self.lastAjaxFail) {
					if ((new Date().getTime() - self.lastAjaxFail) < 60000) {
						// in case a request failed less than 1 minute ago
						// we will not bother the server this time.
						return;
					}
					else {
						// let us try talking to the server again now.
						self.lastAjaxFail = null;
					}
				}
				$.ajax(
					$.extend(true,{
						data:{
							q: query
						}
					}, self.xhrSettings)
				)
				.done(function( response ){
					$.extend( self.cache[ query ] || (self.cache[ query ] = {}), response );
					self._build();
				})
				.fail(function() {
					self.lastAjaxFail = new Date().getTime();
				});
			}
		},

		_hide: function(){
			this.list.hide();
		},

		_show: function(){
			if( this.list.find('li').length ){
				this.list.show();
			}
		},

		_clear: function(){
			this.list.empty();
			this._hide();
		},

		_dataModel: function( query ){
			var self = this,
				dataModel = self.dataModels[ query ],
				data;

			if (!dataModel) {
				data = self.cache[ query ];

				dataModel = {
					query	: query,
					list	: [],
					dict	: tmplDict
				};

				if (data) {
					$.each(["products", "manufacturers", "categories"], function (i, type) {
						data[type] = $.each(data[type] || [], function () {
							this.type = type;
						});

						dataModel.list = dataModel.list.concat(data[type]);
					});

					// Cache model
					self.dataModels[ query ] = dataModel;
				}
			}

			// Use current options
			dataModel.o = self.options;

			return dataModel;
		},

		_build: function(){
			var self = this,
				query = self.elem.val(),
				dataModel = self.dataModel = self._dataModel(query),
				rHighlight = new RegExp( "("+str.escExpStr(query)+")", "ig");

			self._clear();

			self.list
				.appendTo("body")
				.contextOrientation(self.elem,"bottom")
				.append(
				tmplSuggest(
						[dataModel],
						{
							highlight: function( text ){
								return text.replace(rHighlight,"<strong>$1</strong>");
							},
							separatorLast: '',
							separator: function( type ){
								return this.separatorLast == type ? false : !!(this.separatorLast = type);
							}
						}
					)
				);

				//check and correct min-width of suggestbox
				var listWidth = self.list.width(),
					boxWidth = self.elem.parent().innerWidth();
				if(boxWidth > listWidth){
					self.list.width(boxWidth);
				}
				if(self.options.navbar){
					self.list.addClass(self.options.navbar);
				}

			self._select(-1);
			self._show();

			// trigger event if suggest was built
			$(document).trigger('suggestsearch:build', {list: self.list, elem: self.elem});
		},

		_select: function( i ){
			var self = this,
				set = self.selectedIndex,
				items = self.list.find('li:not(.Separator)');

			// reset filter data
			self.filterData = {};

			if( i == 'next' ){
				set++;
			}
			else if( i == 'prev' ){
				set--;
			}
			else{
				set = i;
			}

			if( set >= items.length ){
				set = -1
			}
			else if( set < -1 ){
				set = items.length - 1;
			}

			items.removeClass('ui-select ui-active');
			if( set != -1 ){
				items.eq(set)
					.addClass('ui-select ui-active');

				self.filterData = (self.dataModel.list[ set ] || {}).filter || {};
			}

			if( $.type(self.filterData) === "object" ){
				self.filter
					.attr("name", self.filterData.fieldName || "" )
					.val(self.filterData.attributeValue || "" );
			}

			self.selectedIndex = set;
		},

		_action: function( event ){
			var self = this;

			event.preventDefault();

			// Special: go to page by object id
			if( $.type(self.filterData) === "number" ){
				location.href = ep.config.baseUrl + "?ObjectID=" + self.filterData;
			}
			// Default: submit form
			else{
				self.elem
					.closest('form')
					.trigger('submit');
			}
		}

	});

	return de_epages;

});