/**
 * @author Milovan Jovicic
 * 
 */
(function($) {
 

 
$.cmbBoxSimple = function(options) { 

	/**
	 * Initialize component for passed elements::
	 */
	var options = options || {};
	var inputBox = $(options.el);
	/**
	 * Autocomplete off turn :)
	 */
	inputBox.attr("autocomplete", "off");
	
	
	var wrapper = (inputBox.parent());

	/**
	 * Call main init function:
	 */
	
	this.init({
		  wrapper		: wrapper
		, inputBox		: inputBox
		, store			: options.store /*Could be data object or object URL strings! */
		, emptyText		: options.emptyText || 'Enter text'
		, lastListItem	: options.lastListItem //Used for : edit addresses etc. - specify in HTML format what to put there!!!
		, onSelect		: options.onSelect
		, onClearValue	: options.onClearValue
		, displayHiddenValue : options.displayHiddenValue
	})

}


/**
 * core cmbBox component code::
 */
$.cmbBoxSimple.prototype = {
	

	
	/**
	 * Return if component is Valid? 
	 */
	isValid : function() {
		/**
		 * Do validation there!! or not?
		 */
	}	
	
	/**
	 * Clears invalid state of component 
	 */
	,clearInvalid : function() {
		this.el.removeClass('addrNotFound');
	}
	
	/**
	 * Marks invalid state of component
	 */
	,markInvalid : function() {
		this.el.addClass('addrNotFound');
	}

	/**
	 * Clear valid state of component
	 */
	,clearValid : function() {
		this.el.removeClass('addrFound');
	}
	
	/**
	 * Mark valid state of component
	 */
	,markValid : function() {
		this.el.addClass('addrFound');
	}




	/**
	 * Collapses list if somewhere outside clicked...
	 * @param {Object} e
	 * @param {Object} reference to THIS object...
	 */
	,collapseIf: function(e, $this) {
		if (!$.isAncestor($this.componentWrapper, $(e.target))) {
			$this.collapse();
		};
	}

	/**
	 * Returns element of component
	 */
	,getEl: function() {
		return this.el;
	}
	
	
	/**
	 * collapses the list::
	 */
	,collapse : function(){
		if(!this.isExpanded()){
			return;
		}
		this.wrapper.hide();
		this.list.find('li.hover').removeClass('hover');
		$(document).unbind('mousedown');
	}
	
	/**
	 * return if cmbBox is expanded::
	 */
	,isExpanded: function(){
		//return (this.wrapper.css('display')!='none');
		return this.wrapper.is(':visible'); 
	}
	
	
	/**
	* returns empty text for control
	*/
	,getEmptyText: function() {
		return this.emptyText;
	}
	
	

	/**
	* Toggle expand::
	*/ 
	,toggleExpand: function() {
		if(this.isExpanded()) {
			this.collapse();
		} else {
			this.expand();
		}
	}
	
	/**
	 * Expand 
	 */
	,expand : function(){
		if(this.isExpanded() || this.getCount()<1 || !this.hasFocus){
			return;
		}
		
		this.wrapper.show();

		/**
		 * select first element in list::
		 */		
		this.select(++this.selectedIndex);
		
		
		var $this = this;
		$(document).mousedown(function(e){
			$this.collapseIf(e,$this);
			//return false;
		});
	}
	
	/**
	 * initialize component::
	 */
	,init : function(opts) {
		
		var $this = this;
		/**
		 * Init vars::
		 */
		this.componentWrapper	= opts.wrapper;
		this.largeWrapper		= opts.wrapper.parent();
		this.arrow				= opts.wrapper.find('.cmbArrow');
		this.cmbInputWrapper	= opts.wrapper.find('.cmbInputWrapper');
		this.el					= opts.inputBox;
		this.list 				= opts.wrapper.find('ul.cmbSuggestions');
		this.wrapper	 		= opts.wrapper.find('.cmbSuggestionBox');
		this.emptyText			= opts.emptyText || "Enter text";
		this.hiddenField		= $("<input name='"+this.el.attr('name')+"Value' style='display:none' type='hidden'/>").insertAfter(this.el);
		this.selectedIndex		= -1;
		this.optsStore			= opts.store;
		this.lastListItem		= opts.lastListItem || null;
		
		this.onSelect			= opts.onSelect || null;
		this.onClearValue		= opts.onClearValue || null;
		
		this.displayHiddenValue = (typeof opts.displayHiddenValue == "boolean" ? opts.displayHiddenValue : false)

		/**
		 * TimeOut for suggestion (in ms)::
		 */
		this.timeOut			= 100;
		
		/**
		 * init store::
		 */
		if(this.optsStore.mode=='remote')
		
		{
			/**
			 * Remote mode::
			 */
			this.mode='remote';
			this.store = {};
			this.storeUrlSuggest	= this.optsStore.urlSuggest.url;
		} else {
			throw('Fatal error initializing store!!!');
		}

		// if cmbBox value iz not set, apply empty text!!
		if(!this.el.val().length || this.el.val()==this.emptyText) {
			this.applyEmptyText();		
		}
		this.initEvents();
		//this.renderList();
		
	}
	
	
	/**
	 * Apply Default value!
	 */		
	,applyEmptyText:function(){
		this.el	.val(this.emptyText)
				.addClass('defaultText');
		this.clearInvalid();
		this.clearValid();
		this.hiddenField.attr('value','');
	}

	
	/**
	 * Initialize keyEvents for txtBox::
	 */
	,initEvents:function(){
		
		var $this = this;
		
		this.el
			.focus(function(){
				if ($this.getRawValue() == '') {
					$this.setValue({
						text: '',
						value: ''
					});
				}
				if($this.ajaxSuggestCall) {
					$this.ajaxSuggestCall.abort();
				}				
				$this.hasFocus = true;
				$this.loadIndicator(false);
				$(this).removeClass('defaultText')
				
			})
			.blur(function(e){
				$this.onBlur(e);
			})
			.keydown(function(e){
				switch (e.keyCode) {
					case 9: //tab
							//$this.onViewClick();
					  break;
					case 27: //ESC
						$this.collapse();
					  break;
					case 38: //keyUP
						$this.selectPrev();
						return false;
					  break;
					case 40: //keyDown
						if(!$this.isExpanded()){
							$this.onTriggerClick();
						}else{
							$this.selectNext();
						}
						return false;
					  break;
					case 13: //Enter
						$this.onViewClick();
						e.preventDefault();
						return false;
					  break;
				}
			})
			.keyup(function(e){
				switch (e.keyCode) {
					case 9:		//tab
					case 16:	//shift
					case 27:	//ESC
					case 38:	//keyUp
					case 39:	//key
					case 37:	//key
					case 40:	//keyDown
					case 13:	//Enter
					  break;
					default:
						if($this.getRawValue()!='') {
							/**
							 * this occurs when user start to change value in cmbBox:
							 */
							
							$this.clearValue();
							
							if($this.ajaxSuggestCall) {
								$this.ajaxSuggestCall.abort();
							}
							setTimeout(function(){
								$this.doQuery($this.getRawValue());
							},$this.timeOut);
						} else {
							$this.collapse();
						}
					  break;
				}
			})
		this.arrow
			.click( 
				  function(e){
					e.stopPropagation();
				    e.preventDefault();
					$this.el.focus();
					$this.onTriggerClick();
					return false;
				  }
			)
			.mousedown(
				function(){
					if($this.isExpanded()){
						$this.mouseDown = true;
					}
				}
			)
			
		/**
		 * fix for IE6 :hover bug::
		 */
		/*if($.browser.msie && $.browser.version < 7) {
			alert('aaa');
			this.arrow	
			.hover( 
				  function(){
					$(this).addClass('over');
				  }
				, function(){
					$(this).removeClass('over');
				  }
			)
		}*/
		
		
		
		
			/*.mousedown(function(){$(this).addClass('click')})
			.mouseup(function(){$(this).removeClass('click')})
			.hover( 
				  function(){
					$(this).addClass('over');
				  }
				, function(){
					$(this).removeClass('over');
				  }
			)*/
	}

	/**
	 * Initializes a list with events...
	 */
	,initListEvents : function() {
		this.listItems = this.list.find("li:not('.category')");
		var $this=this;
		this.listItems
			.hover(
				function() {
					$this.listItems.filter('.hover').removeClass('hover');
					$(this).addClass('hover');
				}
				,function(){
				}
			)
			.mousedown(function(e){
				//e.stopPropagation();
				//e.preventDefault();
				//return false;
				$this.mouseDown=true;
			})
			.click(function() {
				//Do not populate field when clicked on "edit history"::
				if(!$(this).hasClass('editHistory')) {
					$this.onViewClick();
				}
			});
	}
	
	
	/**
	 * Renders LI elements according to this.store...
	 */
	, renderList: function(params){
		if(this.getCount()>0) {
			var store = this.store;
			var li=''
			var tmp = [];
			for (var key in store) {
				/**
				 * check whether is store in remote mode, if yes then check store length
				 */
				if(key!='length' && (this.mode=='remote'?store[key].length>0:true)) {
					tmp.push("<LI class='category'>");
					tmp.push(key);
					tmp.push("</LI>");
					//li+="<LI class='category'>"+key+"</LI>";
					$.each(store[key], function(i,j){
						if(i<250) {
							tmp.push('<LI itemValue=');
							tmp.push(j.value);
							tmp.push('>');
							tmp.push(j.label);
							tmp.push('</LI>');
							//li += '<LI itemValue='+j.value+'>'+j.label+'</LI>';
						}
					})
				}
			}
			
			if(this.lastListItem ) {
				/**
				 * render last item...
				 */
				tmp.push("<LI class='lastItem'>");
				tmp.push(this.lastListItem);
				tmp.push("</LI>");
				//li += "<LI class='lastItem'>"+this.lastListItem+"</LI>";
				// <a href = '/account/edit/editAccounts.do'>Edit employees...</a>
			}
			var li = tmp.join('');
			this.list.html(li);
			this.selectedIndex=-1;
			
			
			
			this.initListEvents();
		}
	}
	
	

	
	
	/**
	 * gets total returned elements ...
	 */
	,getCount:function(){
		return this.store.length;
	}


	/**
	 * set Value of component 
	* @param {Object} params to set
	 * e.g.: params :{value:'4', text:'145 ave',} 
	 */
	,setValue:function(params) {

		this.el.removeClass('defaultText');
		
		//Whether to show hidden value or not
		if(this.displayHiddenValue) {
			this.el.val(params.value);
		} else {
			this.el.val(params.text);
		}
		if (params.text!='' && params.value!=''){
			this.hiddenField.attr('value', params.value);
			
			if(this.onSelect) {
				this.onSelect.call(this);
			} else {
				this.collapse();
				this.clearInvalid();
				this.markValid();
			}
		}
		
	}
	
	
	/**
	 * Used to clear value::
	 */
	,clearValue:function(){

		this.hiddenField.attr('value', '');
		this.clearInvalid();
		this.clearValid();
		
		if(this.onClearValue) {
			this.onClearValue.call(this);
		}
	}

	
	/**
	 * get ID from cmbBox element!
	 */
	,getValue:function() {
		var value = this.hiddenField.attr('value');
		//return ((isNaN(parseInt(value))||typeof value== 'undefined' || value === null)?'':value);
		return value;
	}
	
	/**
	 * get raw value from inputBox!
	 * @param {Object} value
	 */
	,getRawValue:function(){
		var v = this.el.val();
		if(v === this.emptyText){
			v = '';
		}
		return v;
	}
	
	/**
	 * Does querying dataset for value entered!
	 * filtering dataset and return filtered dataset!
	 */
	,doQuery : function(value){
		
		var filteredRows = 0;

		if (this.mode=='remote') {
			/**
			 * Load results from remote server::
			 */
			this.storeLoad({value:value, loadIndicator:true});
		}
	}
	
	
	/**
	 * load store - remote mode only!
	 */
	,storeLoad : function(options) {
		if (typeof options.loadIndicator == "undefined") {
			options.loadIndicator = true;
		}
		if(options.loadIndicator) {
			this.loadIndicator(true);
		}
		var $this = this;
		
		var ajaxObj = {
			type	: "GET"
		  , url		: $this.storeUrlSuggest
		  , dataType: "json"
		  , cache	: false
		  , success	: function(a) {
				$this.store = a;
				/**
				 * CLONE STORE, but ONLY ONCE!! 
				 */
				/*if(typeof $this.snapshot!='object') {
					$this.snapshot =  clone($this.store);
				}		*/			
				$this.onLoad();
			}
		  , error	: function() {
				$this.onLoadError();
			} 

		  , data	: {}
		} 			
		
		ajaxObj.data[$this.optsStore.urlSuggest.queryText] = options.value;


		this.ajaxSuggestCall=$.ajax(ajaxObj);
	}
	
	
	
	/**
	 * shows or hides loading indicator according to flag...
	 * @param {Object} flag
	 */
	,loadIndicator:function(flag){
		if(flag) {
			this.el.addClass('loading');
		} else {
			this.el.removeClass('loading');
		}
	}
	
	/**
	 * Choose value on click...
	 * @param {Object} doFocus
	 */
	,onViewClick : function(){
		var li = this.list.find("li.hover:first");
		if($(li).text()) {
			this.setValue({
				  text 	: $(li).text()
				, value	: $(li).attr('itemValue')
			})
			this.el.focus();
		} 
		
	}		
	
	/**
	 * Fires when user go out from input box
	 */
	,onBlur : function(e) {
	
		/**
		* fix for IE ordering of events:: first mouseDown, then blur 
		*/
		if(this.mouseDown) {
			this.mouseDown=false;
			return;
		}
		
		this.hasFocus = false;
		
		this.collapse();
		
		this.loadIndicator(false);
		if(this.ajaxSuggestCall) {
			this.ajaxSuggestCall.abort();
		}
		
		if (this.getRawValue() == '') {
			this.applyEmptyText();
		}
		
	}
	
	/**
	 * fires when triggers click :))
	 */
	, onTriggerClick : function() {
		/**
		 * if box is not expanded, load history data and expand it...
		 */
		 
		if (!this.isExpanded() ) {

			this.loadIndicator(true);
			var $this = this;
			var ajaxObj = {
				type: "GET",
				url: $this.storeUrlSuggest,
				dataType: "json",
				cache: false,
				success: function(a){
					$this.store = a;
					/**
					 * params:
					 * editItems:true
					 * whether to render 'edit history addresses on the end of the list!!
					 */
					$this.onLoad();
				},
				error: function(){
					$this.onLoadError();
				},
				data: {
					query: ''
				}
			}
			$.ajax(ajaxObj);
		} else {
			this.collapse();
		}
		
		
		/*
		if(this.getRawValue()=='') {
			/**
			 * if empty value, then reset store::
			 *
			if(typeof this.snapshot=='object') {
				**
				 * reset store::
				 *
				this.store = $.clone(this.snapshot);
			}
			this.onLoad();
		} else {
			this.toggleExpand();
		}*/
	}
	
	/**
	 * triggers on load event!
	 */
	,onLoad : function(){
		this.selectedIndex = -1;
		this.ajaxSuggestCall = null;
		this.collapse();
		if(this.getCount() > 0){
			this.renderList();
			this.expand();
			/*if(!this.selectByValue(this.value, true)){
				this.select(0, true);
			}*/
		}else{
			this.onEmptyResults();
		}
		this.loadIndicator(false);
	}
	
	/**
	 * triggers when error loading...
	 */
	,onLoadError : function() {
		this.loadIndicator(false);
		this.markInvalid();
	}
	
	
	/**
	 * fires when there are no results!
	 */		
	,onEmptyResults:function(){
		this.collapse();
		this.markInvalid();
	}
	
	
	
	
	/**
	 * Select an item in the dropdown list by its numeric index in the list.
	 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
	 * @param {Number} index The zero-based index of the list item to select
	 */
   , select : function(index){
		this.selectedIndex = index;
		
		this.listItems
			.removeClass('hover')
			.eq(index).addClass('hover');
			
		/**
		* Scroll To activeSuggestionItem::
		**/
		var activeSuggestionItem = this.listItems.eq(index);
		if (activeSuggestionItem.position().top < 0) {
			this.list.scrollTop(this.list.scrollTop() - activeSuggestionItem.outerHeight());
		};
		if (activeSuggestionItem.position().top + activeSuggestionItem.outerHeight() > this.wrapper.height()) {
			this.list.scrollTop(this.list.scrollTop() + activeSuggestionItem.outerHeight());
		}
		
			
			
	 }

	// private
	,selectNext : function(){
		var ct = this.getCount();
		if(ct > 0){
			if(this.selectedIndex == -1){
				this.select(0);
			}else if(this.selectedIndex < ct-1){

				//this.selectedIndex = this.listItems.index($('.hover')[0]);
				/*this.selectedIndex = this.listItems.index(this.listItems.filter('.hover'));*/

				this.select(++this.selectedIndex);
			}
		}
	}

	// private
	,selectPrev : function(){
		var ct = this.getCount();
		if(ct > 0){
			if(this.selectedIndex == -1){
				this.select(0);
			}else if(this.selectedIndex != 0){
				//this.selectedIndex = this.listItems.index($('.hover')[0]);
				/*this.selectedIndex = this.listItems.index(this.listItems.filter('.hover'));*/
				this.select(--this.selectedIndex);
			}
		}
	}
	

}; //cmbBoxsimple(



})(jQuery);
