/**
 * Rm_Suggest MUST be single for each "suggested" input-element
 * HELP
 * 1) Common use:
 * 		$('#somefield').rmeasysuggest();
 * 		$('#somefield').init(<array of parametrs>);
 *
 * 2) Parametrs:
 * 	Default params
 * 		minSearchLength			Minimal number of symbols to send ajax-request, default is 3.
 * 		switchNames'			Old suggest plugin switches names of visible input and hidden one. This option control status of the same action. Defaul true.
 * 		url						Ajax-action url.
 * 		urlVar					Ajax var name to send. Default is name.
 * 		value					Visible input value.
 * 		data					Hidden input value.
 * 		fieldName				Hidden input name. If NULL, than visible name plus "_suggest" is used.
 * 		fieldId					Hidden input id. If NULL, than visible id plus "_suggest" is used.
 * 		
 * 	PuntoSwitcher
 * 		usePuntoSwitcher		Additional ability - transliterate any english symbol to same-button russian. Default false.
 * 
 * New ajax notify
 * 		useNewAjaxNotify		If true, exact img element will be used to indicated ajax-reuqest status. Default false.
 * 		newAjaxNotifyId			Exact img element ID.
 * 		notifyImgSrc			Src for request-progress indication. Default value - '/img/indicator.gif'.
 *
 * Validation
 * 		fieldCheck				If true, than will validate visible and hidden fields value and set state of suggest.
 * 		useStatusIcon			Use or not icon to indicate validation state.
 * 		statusIconId			Image element ID.
 * 		validStatusImg			Src of valid state image.
 * 		invalidStatusImg		Src of invalid state image.
 *
 * 		useStatusIconBG			Use or not icon bg to indicate validation state.
 * 		statusIconBGId			Image element ID.
 * 		validStatusImgBG		Src of valid state image.
 * 		invalidStatusImgBG		Src of invalid state image.
 *
 * Hinting
 * 		inFieldHint				If true, than will show in field hint-string. If user clicks in, string will disapear. Recomended to use with fieldColorSwitch and FieldCheck
 * 		defaultHintClass		Css-class for hint.
 * 		activeHintClass			Css-class for value.
 * Selection
 * 		selectOnFocus			If true, selects field value on focus
 * 
 * Notice (optional)
 * 		noticeText				Сообщение об ошибке при отсутствии результатов
 * 		noticeCheckUrl			Урл для запроса-проверки результатов в соседнем suggest'е
 * 
 * Event handler
 * 		fireOnSelect			Функция, запускющаяся при выборе значения из выпадающего списка
 */

/**
 * @param {Object} input HTML element
 */
function RM_Plugin_EasySuggest(input)
{
	this.input = $(input);
	this.debug = false;
	this.input.get(0).easysuggest = this;
	this.hidden = null;
	this.cache = null;
	this.ajaxProgress = null;
	this.oldImgSrc = null;
	this.validState = false;
	this.settings = {
		'oldValue'			: '',
		'minSearchLength'	: 2,
		'useNewAjaxNotify'	: false,
		'notifyImgSrc'		: '/img/indicator.gif',
		'switchNames'		: true,
		'url'				: '',
		'urlVar'			: 'name',
		'value'				: '',
		'fieldColorSwitch'	: false,
		'useValidation'		: false,
		'inFieldHint'		: false,
		'fieldCheck'		: false,
		'useStatusIcon'		: false,
		'useStatusIconBG'	: false,
		'selectOnFocus'		: false,
		'selectFirst'		: true
	};
	this.selectionFlag = false;
	this.suggestState = false;
	this.suggestStateEnter = false;
	this.oldValue = null;
	this.selectFlag = false;
	this.requestId = '';
	this.fireOnSelect = function () {};
	
	/**
	 * @var	Значение видимого поля перед нажатием любой клавиши
	 */
	this.keypressOldValue = '';
};

/**
 * Базовая инициализация всех возможностей suggest -установка значений, 
 * добавление обработчиков событий, подключение punto-switcher и собственно autocomplete
 * @param param
 * @returns
 */
RM_Plugin_EasySuggest.prototype.init = function(param)
{
	jQuery.extend(this.settings, param);
	var obSelf = this;
	this.input.addClass('rm-suggest-input');
	if(!this.hidden)
	{
		this.hidden = $(this.input.after('<input type="hidden" />').next().get());
		if (param.switchNames)
		{
			// rename inputs
			this.hidden.attr('name', this.input.attr('name'));
			var inputName = param.fieldName ? param.fieldName :  this.input.attr('name')+'_suggest';
			if(param.fieldId)
				this.hidden.attr('id', param.fieldId);
			this.input.attr('name', inputName);
		}
		else
		{
			var hiddenName = param.fieldName ? param.fieldName : this.input.attr('name')+'_suggest';
			var hiddenId = param.fieldId ? param.fieldId : this.input.attr('id')+'_suggest';
			this.hidden.attr('name', hiddenName).attr('id', hiddenId);
		}
	}
	if (this.settings.noticeText)
		this.obPopup = new RM_Overform_Popup();
	
	if(param.data)
		this.hidden.val(param.data);
	if ( param.value )
		this.input.val(param.value);
	if(!this.cache)
		this.cache = new RM_Plugin_EasySuggest_Cache(); 
	this.cache.clear();
	this.oldValue = this.input.val();
	if (this.settings.inFieldHint)
		this.input.inputTitle({styleMethod: 'class', cssClass: this.settings.defaultHintClass});
	
	//Инициализация всех событий поля
	this.input.focus(function (event)
	{
		return obSelf.inputFocusHandler(event);
	}).blur(function(event)
	{
		return obSelf.inputBlurHandler(event);
	}).keypress(function(key)
	{
		return obSelf.inputKeyPressHandler(key);
	}).keyup(function(key)
	{
		return obSelf.inputKeyUpHandler(key);
	});
	this.validate();
	if (this.settings.useNewAjaxNotify)
		this.oldImgSrc = $('#'+this.settings.newAjaxNotifyId).attr('src');
	if (param.fireOnSelect)
		this.fireOnSelect = param.fireOnSelect;
	this.initAutocomplete();
};

RM_Plugin_EasySuggest.prototype.inputFocusHandler = function(event)
{
	this.suggestState = false;
	this.suggestStateEnter = false;
	this.oldValue = this.input.val();
	if (this.settings.selectOnFocus)
	{
		this.selectionFlag = false;
		this.selectOnFocus();
	}
	if (this.settings.fieldCheck)
		this.validate();
}

RM_Plugin_EasySuggest.prototype.inputBlurHandler = function(event)
{
	if (this.suggestState) {
		this.selectFirstByBlur();
		this.input.autocomplete('close');
	}
	else {
		if (this.settings.fieldCheck)
			this.validate();
	};
	/*if (this.settings.selectOnFocus) {
		this.dropSelection(event);
	}*/
	return true;
};

RM_Plugin_EasySuggest.prototype.inputKeyUpHandler = function(key)
{
	var oldValue = this.keypressOldValue != '' ? this.keypressOldValue : this.oldValue;
	if (oldValue != this.input.val() && key.keyCode != 13 && key.keyCode != 9)
	{
		this.hidden.val('');
		this.validate();
	}
	this.keypressOldValue = this.input.val();
};

RM_Plugin_EasySuggest.prototype.inputKeyPressHandler = function(key)
{
	if (this.settings.selectFirst)
		return this.selectFirstByKeyCode(key);
};

RM_Plugin_EasySuggest.prototype.isChrome = function()
{
	return (navigator.userAgent.search(/chrome/gi) === -1 ? false : true);
};

RM_Plugin_EasySuggest.prototype.selectOnFocus = function(event)
{
	if (this.isChrome())
	{
		setTimeout("$('#"+this.input.attr('id')+"').selectRange(0,0).selectRange(0,$('#"+this.input.attr('id')+"').val().length);",70);
		return false;
	}
	if (event)
		event.stopImmediatePropagation();
	this.input.selectRange(0,this.input.val().length);
	return false;
};

RM_Plugin_EasySuggest.prototype.dropSelection = function(event)
{
	if(!$.support.opacity)
		return;
	if (this.isChrome())
	{
		this.input.val(this.input.val());
		return true;
	}
	if (event)
		event.stopImmediatePropagation();
	this.input.selectRange(0,0);
};

RM_Plugin_EasySuggest.prototype.initAutocomplete = function()
{
	var obSelf = this;
	this.input.autocomplete({
		source:		function(request, response)
					{
						obSelf.getSource(request, response);
					},
		minLength:	this.settings.minSearchLength,
		open:		function(event, ui)
					{
						obSelf.selectFlag = false;
						return obSelf.openHandler(event, ui);
					},
		select:		function(event, ui)
					{
						return obSelf.selectHandler(event, ui);
					},
		focus:		function(event, ui)
					{
						return obSelf.focusHandler(event, ui);
					},
		close:		function(event, ui)
					{
						obSelf.suggestStateEnter = false;
						return obSelf.suggestState = false;
					}
	});
};

RM_Plugin_EasySuggest.prototype.validate = function()
{
	if (!this.settings.fieldCheck)
		return;
	if (this.input.val() == '' || this.input.val() == this.input.attr('default'))
		this.hidden.val('');
	if (this.input.val() != '' && this.input.val() !== this.input.attr('default') && this.hidden.val() != '')
		this.validState = true;
	else
		this.validState = false;
	if (this.settings.useStatusIcon) {
		var flagImg = this.validState ? this.settings.validStatusImg : this.settings.invalidStatusImg;
		
		if (document.getElementById(this.settings.statusIconId) != undefined)
			document.getElementById(this.settings.statusIconId).src = flagImg;
		else
		{
			var obSelf = this;
			$(function(){
				document.getElementById(obSelf.settings.statusIconId).src = flagImg;
			});
			delete(obSelf);
		}
		delete(flagImg);
	}
	if (this.settings.useStatusIconBG) {
		var flagImgBG = this.validState ? this.settings.validStatusImgBG : this.settings.invalidStatusImgBG;
		
		if (document.getElementById(this.settings.statusIconBGId) != undefined)
			document.getElementById(this.settings.statusIconBGId).style.background = flagImgBG;
		else
		{
			var obSelf = this;
			$(function(){
				document.getElementById(obSelf.settings.statusIconBGId).style.background = flagImgBG;
			});
			delete(obSelf);
		}
		delete(flagImgBG);
	}
	if (this.validState)
		this.hideNotice();
};

RM_Plugin_EasySuggest.prototype.selectFirstByKeyCode = function(key)
{
	var keyCode = key.keyCode;
	if ((keyCode == 13 || keyCode == 9) && this.suggestStateEnter)
	{
		this.suggestState = this.suggestStateEnter = false;
		if (this.pastFirstSuggestValue())
			key.stopImmediatePropagation();
		if (keyCode == 9) {
			return true;
		}
		return false;
	}
	else
	{
		/**
		 * Хак специально для opera.
		 */
		if (this.selectFlag) {
			this.selectFlag = false;
			key.stopImmediatePropagation();
			key.returnValue = false;
			return false;
		}
		return true;
	}
};

RM_Plugin_EasySuggest.prototype.selectFirstByBlur = function()
{
	this.suggestState = this.suggestStateEnter = false;
	var obj = this.input;
	if (obj.val() && this.cache.get(obj.val()) && this.cache.get(obj.val())[0]) {
		if (this.cache.get(obj.val())[0].label == obj.val() || this.cache.get(obj.val()).length == 1)
			return this.pastFirstSuggestValue();
	}
	this.validate();
};

RM_Plugin_EasySuggest.prototype.pastFirstSuggestValue = function()
{
	var obList = this.cache.get(this.input.val());
	if (!obList)
		return false;
	this.pasteValue(obList[0].label, obList[0].value);
	this.input.autocomplete('close');
	this.selectionFlag = true;
	return true;
};

RM_Plugin_EasySuggest.prototype.valid = function()
{
	return this.settings.fieldCheck? this.validState : true;
};

RM_Plugin_EasySuggest.prototype.pasteValue = function(visibleValue, hiddenValue)
{
	this.input.val(visibleValue);
	if (this.hidden)
		this.hidden.val(hiddenValue);
	this.validate();
	this.input.trigger('title_check');
};


/**
 * Проверить, делать ли кроссдоменный запрос.
 */
RM_Plugin_EasySuggest.prototype.useCrossDomainRequest = function( requestUrl )
{
	var useCrossDomainRequest = false;
	var currentDomain = window.location.hostname;
	
	if ( this.settings.url.substr( 0, 7 ) == 'http://' )
	{
		var requestDomain = this.settings.url.substr( 7 );
		requestDomain = requestDomain.substr( 0, requestDomain.indexOf( '/' ) );

		if ( requestDomain != currentDomain && requestDomain != 'www.' + currentDomain )
			useCrossDomainRequest = true;
	}
	
	return useCrossDomainRequest;
};


RM_Plugin_EasySuggest.prototype.getSource = function(request, sendData)
{
	if(!this.settings.url)
		return;
	var val = this.input.val();
	var cacheData = this.cache.get(val);
	if (!cacheData)
	{
		this.requestId = request.term;
		var obSelf = this;
		this.ajaxStart();
		$.ajax({
			timeout: 7000,
			url: obSelf.settings.url,
			dataType: "json",
			data: obSelf.settings.urlVar+"="+encodeURIComponent(request.term) + (this.useCrossDomainRequest( obSelf.settings.url ) ? '&jsoncallback=?' : ''),
			success: function(data) {
				var suggestData = null;
				if (data['suggest'])
				{
					if (!data['name'] || data['name'] != obSelf.requestId)
					{
						if (data['name'])
							obSelf.cache.put(data['name'], data['suggest']);
						return false;
					}
					suggestData = data['suggest'];
				}
				else
					suggestData = data;
				obSelf.cache.put(val, suggestData);
				if (suggestData.length > 0)
				{
					obSelf.hideNotice();
					return sendData(suggestData);
				}
				
				obSelf.showNotice(request.term);
				/*if (suggestData.length == 1)
					obSelf.pasteValue(suggestData[0].label, suggestData[0].value);*/
				obSelf.input.autocomplete('close');
				return false;
			},
			complete: function(){
				obSelf.ajaxStop();
			}
		});
	}
	else
	{
		this.ajaxStart();
		if (cacheData.length > 0)
		{
			this.hideNotice();
			sendData(cacheData);
		}
		else
			this.showNotice(val);
		this.ajaxStop();
	}
};

RM_Plugin_EasySuggest.prototype.showNotice = function(pattern)
{
	if (this.settings.noticeText === false || this.settings.noticeText === '')
		return false;
	this.requestNoticeCheck(pattern);
}

RM_Plugin_EasySuggest.prototype.hideNotice = function()
{
	if (this.settings.noticeText === false || this.settings.noticeText === '')
		return false;
	this.obPopup.destroy();
}

RM_Plugin_EasySuggest.prototype.requestNoticeCheck = function(pattern)
{
	if (this.settings.noticeCheckUrl !== false && this.settings.noticeCheckUrl !== '' && pattern && !this.cache.get('notice_'+pattern))
	{
		var obSelf = this;
		$.ajax({
			timeout: 1000,
			url: obSelf.settings.noticeCheckUrl,
			dataType: "json",
			data: "pattern="+encodeURIComponent(pattern),
			success: function(data) {
				obSelf.cache.put('notice_'+pattern, data.state);
				if (data.state)
				{
					var callback = function(){return [obSelf.input.offset().left+85, obSelf.input.offset().top-35, 180, 10, 0];};
					obSelf.obPopup.calcPositionAndShowMessage(obSelf.settings.noticeText, callback);
				}
				else
					obSelf.obPopup.destroy();
			}
		});
	}
	else
		this.obPopup.showMessage(this.settings.noticeText, this.input.offset().left+105, this.input.offset().top-35, 180, 10, 0);
}

RM_Plugin_EasySuggest.prototype.selectHandler = function(event, ui)
{
	this.suggestState = false;
	this.input.val(ui.item.label);
	this.hidden.val(ui.item.value);
	this.validate();
	this.selectFlag = true;
	this.fireOnSelect(ui.item);
	return false;
};

RM_Plugin_EasySuggest.prototype.focusHandler = function(event, ui)
{
	this.suggestStateEnter = this.suggestState = false;
	return false;
};

RM_Plugin_EasySuggest.prototype.openHandler = function(event, ui)
{
	this.suggestState = this.suggestStateEnter = true;
	if (this.oldValue != this.input.val())
		this.hidden.val('');
	return false;
};

RM_Plugin_EasySuggest.prototype.ajaxStart = function()
{
	if(this.ajaxProgress)
		return;
	if (this.settings.useNewAjaxNotify)
	{
		if (this.settings.useNewAjaxNotify)
			this.oldImgSrc = $('#'+this.settings.newAjaxNotifyId).attr('src');
		$('#'+this.settings.newAjaxNotifyId).attr('src', this.settings.notifyImgSrc);
		this.ajaxProgress = true;
	}
	else
	{
		this.ajaxProgress = $('<img class="rm-suggest-ajax-indicator" src="'+this.settings.notifyImgSrc+'" style="position:absolute;" />').insertBefore(this.input).get(0);
		this.input.addClass('rm-suggest-busy');
	}
	
};

RM_Plugin_EasySuggest.prototype.ajaxStop = function()
{
	if (this.settings.useNewAjaxNotify)
		$('#'+this.settings.newAjaxNotifyId).attr('src', this.oldImgSrc);
	else
		this.input.removeClass('rm-suggest-busy');
		this.input.prev('img.rm-suggest-ajax-indicator').remove();
	this.ajaxProgress = false;
};

/***********************
 * 
 * ajax query results cache
 * 
 **********************/

function RM_Plugin_EasySuggest_Cache()
{
	this.storageForKey = {};
	this.storageForEmptyKey = undefined;
};

RM_Plugin_EasySuggest_Cache.prototype.storageForKey = {};
RM_Plugin_EasySuggest_Cache.prototype.storageForEmptyKey = undefined;

RM_Plugin_EasySuggest_Cache.prototype.clear = function()
{
	this.storageForEmptyKey = undefined;
	this.storageForKey = {};
	return true;
};

RM_Plugin_EasySuggest_Cache.prototype.get = function(key)
{
	key = key.toString();
	if ('' == key) 
		return this.storageForEmptyKey;
	else
		return this.storageForKey[key];
		
};

RM_Plugin_EasySuggest_Cache.prototype.put = function(key, val)
{
	key = key.toString();
	if('' == key)
		this.storageForEmptyKey = val;
	else
		this.storageForKey[key] = val;
};

/*************************************
 * 
 * Bind jQuery plugin
 * 
 ************************************/

jQuery.fn.rmeasysuggest = function()
/**
 * There can be only one (c) instance of RM_Plugin_EasySuggest for each html input element.
 * So we will cache instances in jQuery.fn.rmsuggest_cache one per input's ID 
 */
{
	var obInput = this.filter('input').eq(0); /* only input elements can be "suggested"*/
	if(obInput.length != 1)
	{
		throw new Error('RM_Plugin_EasySuggest: INPUT element not found');
		return false;
	};
	var id = obInput.attr('id');
	if(!id)
	{
		throw new Error('RM_Plugin_EasySuggest: INPUT#ID not found');
		return false;
	};
	if(!jQuery.fn.rmsuggest_cache[id])
		jQuery.fn.rmsuggest_cache[id] = new RM_Plugin_EasySuggest(obInput.get(0));
	return jQuery.fn.rmsuggest_cache[id];
};

jQuery.fn.rmsuggest_cache = {}; //cache itself
	
$.fn.selectRange = function(start, end) {
	return this.each(function() {
		if(this.setSelectionRange) {
			//this.focus();
			return this.setSelectionRange(start, end);
		} else if(this.createTextRange) {
			var range = this.createTextRange();
			range.collapse(true);
			range.moveEnd('character', end);
			range.moveStart('character', start);
			range.select();
		}
	});
};

