/*
 * @name  Autocomplete
 * @autor Pawel Rupiewicz ( p.rupiewicz@amg.net.pl )
 * @date  2008/2009
 */
(function($) {

	$.widget("ui.autocompleter", {

		//------------------------------------------------------------------------------------------------------------------------------------------------------------
		// Widget initialization methods
		//------------------------------------------------------------------------------------------------------------------------------------------------------------

		/** Overwrite ui-core widget methods */
		_init: function() {
			//var startTime = new Date().getTime();
			/* preparing wigdet options */
			var uid = new Date().getTime();
			$.extend( this.options, {
				uid: "ac"+uid,
				isMouseOverDropdownList: false,
				inputHiddenId: "inHidden"+uid,
				inputLabelId: "inLabel"+uid,
				inputOverlayId: "overlay"+uid
			});
			/* preparing cache */
			$.ui.autocompleter.cache.setCacheSize( this.options.cacheSize );
			if ( this.options.useCache && this.options.initCacheOnStartUp ) {
				$.ui.autocompleter.cache.add( this._getWidgetId(), "", this.element.html() );
			}

			/* creating drop download list */
			this._createDropDown();
			/* initializing events */
			this._initializeEvents();

			//var stopTime = new Date().getTime();
			//$("body").append("...timer : " + (stopTime-startTime) + "<br/>");
		},

		//------------------------------------------------------------------------------------------------------------------------------------------------------------
		// Global access functions
		//------------------------------------------------------------------------------------------------------------------------------------------------------------

		/** Getting label value */
		getLabel: function() { return $("#"+this.options.inputLabelId).val(); },
		/** Getting value */
		getValue: function() { return $("#"+this.options.inputHiddenId).val(); },
		/** Setting label value */
		setLabel: function(label) { $("#"+this.options.inputLabelId).val(label); },
		/** Setting value */
		setValue: function(value) { $("#"+this.options.inputHiddenId).val(value); },
		/** Clearing value and label */
		clear: function() { $("#"+this.options.inputHiddenId).val(""); $("#"+this.options.inputLabelId).val(""); },
		/** Clearing value and label */
		clearDropdown: function() { this.element.html(""); },
		/** Forcing searching but not open the drop down list */
		search: function( token, extraUrlParams, doClear ) {
			if ( extraUrlParams != null ) { this.options.extraUrlParams = extraUrlParams; }
			this._search(token, false, doClear );
		},
		/** Forcing searching. After search drop down list is opening */
		searchAndOpen: function( token, extraUrlParams ) {
			if ( extraUrlParams != null ) { this.options.extraUrlParams = extraUrlParams; }
			this._search(token, true );
		},
		/** Storing info about selected element */
		select: function( data ) {
			$("#"+this.options.inputHiddenId).val(data.val);
			$("#"+this.options.inputLabelId).val(data.lab);
			this.element.hide();
			// callback method
			this.options.onAfterSelect(data);
		},
		/** Helper method. Function can be executed on mouse over event */
		mouseover: function(elem) { this.options.onMouseOver(elem); },
		/** Helper method. Function can be executed on mouse out event */
		mouseout: function(elem) { this.options.onMouseOut(elem); },

		//------------------------------------------------------------------------------------------------------------------------------------------------------------
		// Widget initialization methods
		//------------------------------------------------------------------------------------------------------------------------------------------------------------

		/** preparing events */
		_initializeEvents: function() {
			// Helper variables
			var self = this;

			// Binding events
			$("#"+this.options.inputLabelId).bind( ($.browser.opera ? "keypress" : "keydown"), function(event) {
				//--
				switch( event.which ) {
					case $.ui.keyCode.ESCAPE: self.element.hide(); self.options.isMouseOverDropdownList = false; break;
					case $.ui.keyCode.DOWN: break;
					case $.ui.keyCode.UP: break;
					case $.ui.keyCode.PAGE_DOWN: break;
					case $.ui.keyCode.PAGE_UP: break;
					case $.ui.keyCode.HOME: break;
					case $.ui.keyCode.END: break;
					/* locking some keys */
					case $.ui.keyCode.CAPS_LOCK:
					case $.ui.keyCode.INSERT:
					case $.ui.keyCode.TAB: event.preventDefault(); break;
					/* when other button click we are trying to find hints */
					default: {
						clearTimeout(self.options.keysTimeout);
						self.options.keysTimeout=setTimeout( function() { self._search(self.getLabel(), true ); }, self.options.searchDelay );
						break;
					}
				}
				//--
			}).focus(function(){
				//--
			}).blur(function(){
				//$("body").append("blur<br/>");
				/* this condition is very important on FF, Opera. When we click on the layer scroll, blur event is fired, and the layer is closed. */
				if ( !self.options.isMouseOverDropdownList ) {
					self.element.hide();
				}
			}).click(function(){
				/*self.element.toggle();*/
			}).dblclick(function(){
				self.element.toggle();
			});

		},

		/** create drop down list*/
		_createDropDown: function() {
			var self = this;
			// creating autocompleter navibar
			var dropListButtonId = "dlb"+this.options.uid;
			var inputId = "input"+this.options.uid;
			var inputContainer = '';
			inputContainer += '<div class="ui-autocompleter-input-container">';
			inputContainer += '  <div class="left"></div>';
			inputContainer += '  <div class="wrapper">';
			inputContainer += '    <div id="'+this.options.inputOverlayId+'" class="overlay"></div>';
			inputContainer += '    <input id="'+this.options.inputHiddenId+'" style="display:none" type="hidden" name="'+this.options.inputName+'" value="'+this.options.inputInitVals.val+'"/>';
			inputContainer += '    <input id="'+this.options.inputLabelId+'" type="text" name="'+this.options.inputName+'_lab" value="'+this.options.inputInitVals.lab+'"/>';
			inputContainer += '  </div>';
			inputContainer += '  <div id="'+dropListButtonId+'" class="right"></div>';
			inputContainer += '</div>';
			// inserting navibar
			$(inputContainer).insertBefore(this.element);

			// positioning and styling autocomplete dropdown list
			var inputElement = $("#"+this.options.inputLabelId);
			var inputPosition = inputElement.offset();
			var inputWidth = this.options.inputWidth != null && this.options.inputWidth > 0 ? this.options.inputWidth : inputElement.width();
			var inputHeight = inputElement.outerHeight();
			var x = inputPosition.left;
			var y = inputPosition.top;
			var scrollTop = $(document).scrollTop();
			var scrollLeft = $(document).scrollLeft();
			//$("body").append( this.options.inputName + " " + x + " " + y + " " + scrollTop + " " + scrollLeft + "<br/>");

			this.element.css({
				"position": "absolute",
				"margin-top": inputHeight+'px',
				//"margin-top": "0px",
				//"top": "0px",
				"margin-left": $(".left").css("width"),
				"display": "none",
				"z-index": 10000
			});

			/*
			this.element.css({
				"position": "absolute",
				"display": "none",
				"left": x,
				"top": y + inputHeight,
				"z-index": 10000
			});
			*/
			// setting dropdown list size
			if ( this.options.dropdownListWidth != null && this.options.dropdownListWidth > 0 ) {
				this.element.css("width",this.options.dropdownListWidth+"px");
			} else {
				this.element.css("width",inputWidth+"px");
			}

			// setting input styles
			inputElement.css("width",inputWidth+"px");
			// setting read only
			if ( this.options.inputReadOnly ) {
				inputElement.attr("readonly","readonly");
			}

			// preparing overlay layer
			$("#"+this.options.inputOverlayId).css({
				"position": "absolute",
				"display": "none",
				"width": inputWidth+"px",
				"height": inputHeight+"px",
				"left": x,
				"top": y,
				"z-index": 10000
			}).addClass("ui-autocompleter-overlay");

			// adding default class
			this.element.addClass("select_options ui-autocompleter-dropdown")
						.mouseover( function() { self.options.isMouseOverDropdownList = true; } )
						.mouseout( function() { self.options.isMouseOverDropdownList = false; } );


			// adding show/hide list button
			$("#"+dropListButtonId).click( function() {

				if ( self.element.css("display") == "none" ) {
					inputElement.focus();
					self.element.show();
					self.options.isMouseOverDropdownList = false;
					//inputElement.focus();
				} else {
					self.options.isMouseOverDropdownList = false;
					inputElement.focus();
					self.element.hide();
				}

			});


		},

		/** Autocompler search logic. We are checking cache. If requested data is not in cache then we send request to server. */
		_search: function( token, openDropDown, doClear ) {
			//--
			if ( this.options.data == null ) return;
			//--
			var self = this;
			//--
			var urlParams = {
				query: token==null?"":token,
				widgetId: this.element.attr("id")
			};
			urlParams = $.extend( urlParams, this.options.extraUrlParams );
			//--
			this._toggleOverlay();
            if (doClear!="false") {
                this._resetHiddenInput();
            }
			//--
			//var loadTimeStart = new Date().getTime();
			//-- checking cache
			var data = null;
			if ( this.options.useCache ) {
				data = $.ui.autocompleter.cache.get( this._getWidgetId(), token );
			}
			if ( data != null && data != undefined ) {
				self.element.html( data );
				if ( openDropDown ) { self.element.show(); }
				self._toggleOverlay();
			} else {
				$.ajax({
					/* adress that we are getting data from*/
					url: this.options.data,
					/* setting request params */
					data: urlParams,
					/* request type */
					type: "GET",
					scriptCharset: "UTF-8",
					/* response type */
					dataType: "html",
					/* should browers cache data*/
					cache: false,
					/* error callback method */
					error: function(xmlHttpRequest, textStatus, errorThrown) {},
					/* if everything goes ok */
					success: function( data, textStatus ) {
						//var loadTimeStop = new Date().getTime();
						//$("body").append( "load data time: " + (loadTimeStop - loadTimeStart) + "ms <br/>" );
						// storing read data in cache
						if ( self.options.useCache && token != null ) {
							$.ui.autocompleter.cache.add( self._getWidgetId(), token, data );
						}
						// writing data to dropdown
						self.element.html( data );
						// closing drop down
						if ( openDropDown ) { self.element.show(); }
                        // invoking onChange method
//TOFIX                        self.options.onAfterSelect();
						// disable overlay
						self._toggleOverlay();
					}

				});
			}
		},

		/** Reseting field */
		_resetHiddenInput: function() { $("#"+this.options.inputHiddenId).val(""); },
		/** Toggle of overlay layer */
		_toggleOverlay: function() { $("#"+this.options.inputOverlayId).toggle(); },
		/** Getting widget id*/
		_getWidgetId: function() { return this.element.attr("id"); }
	});


	/** Extra operations. Setting defaults, getters and initializing extra objects */
	$.extend( $.ui.autocompleter, {
		/* setting defaults */
		defaults: {
			/* data url */
			data: null,
			extraUrlParams: {},

			/* input element and dropdown list */
			inputName: "test",
			inputInitVals: {lab:"",val:""},
			inputWidth: 300,
			inputReadOnly: false,
			dropdownListWidth: -1,
			searchDelay: 700,

			/* cache */
			useCache: true,
			initCacheOnStartUp: false, // if you want to put a lot of data to cache at startup then IE do this quite a lot of time, so use at your own risk. Other browser don't have this problem
			cacheSize: 100,

			/* callbacks */
			onAfterSelect: function(data) {},
			/* callbacks inkoved after search */
			onAfterSearch: function() {},
			//onMouseOver: function(elem) { $(elem).css("color","red"); },
			onMouseOver: function(elem) { $(elem).addClass("mark"); },
			//onMouseOut: function(elem) { $(elem).css("color","black"); }
			onMouseOut: function(elem) { $(elem).hasClass("mark") ? $(elem).removeClass("mark") : null; }
		},

		/* setting getters methods */
		getter: ["getLabel","getValue"],

		/* cache object interface */
		cache: {
			add: function( namespace, token, value ) {},
			get: function( namespace, token ) {},
			flush: function() {},
			setCacheSize: function( cacheSize ) {},
			getCachedTokens: function( namespace ) {}
		}

	});

	/** Autocomplete cache implementaion */
	$.extend( $.ui.autocompleter.cache, {
		data: {},
		cacheSize: 100,
		add: function( namespace, token, value ) {
			if ( this.data.length >= this.cacheSize ) {
				this.flush();
			}
			this.data[namespace+":"+token] = value;
		},
		get: function( namespace, token ) {
			return this.data[namespace+":"+token];
		},
		flush: function() {
			this.data = {};
		},
		setCacheSize: function( cacheSize ) {
			this.cacheSize = cacheSize;
		},
		getCachedTokens: function( namespace ) {}
	});


})(jQuery);


