/**
 * Клавиши
 */
var KEY = window.KEY = {
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,
    DEL: 8,
    TAB: 9,
    RETURN: 13,
    ESC: 27,
    PAGEUP: 33,
    PAGEDOWN: 34,
    SPACE: 32,
    COMMA: 188,
    OPERA_COMMA: 44
};

/**
 * Автокомплитер
 * @param obj объект, на котором применять автокомплитер
 * @param options настройки
 */
Autocomplete = function(obj, options) {
    this.init(obj, options);
}

Autocomplete.prototype = {
    
    ajaxTimer: null,
    ajaxInputs: new Array(),
    /**
     * Посылка запросов на сервер для получения наиболее релевантных результатов.
     * В запросе передается переменная pattern, в которой содержится значение текстового поля в данный момент времени
     */
    ajax: function() {
        var that = this;
        
        sendData = function() {
            $.getJSON(that.options['changeUrl'], {pattern: that.getInput().val()}, function(data) {
		var inputVal = that.getInput().val();
            	if (data['values'].length>0 && inputVal.length>0 && inputVal.indexOf(data['pattern'])!=-1) {
	                that.results.set(data['values']);
	                that.results.show();
	                that.results.indicate(that.getInput().val());
            	} else {
            		that.results.hide();
            	}
            });
        }
        
        if (this.ajaxTimer!=null) {
            clearTimeout(this.ajaxTimer);
        }
        this.ajaxTimer = setTimeout(sendData, 100);
    },
    
    /**
     * Остановка таймера на аджакс
     */
    stopAjax: function() {
        clearTimeout(this.ajaxTimer);
    },
    
    init: function(obj, options) {
        var that = this;
        
        this.obj = obj;
        this.options = options;
        
        var keyevent = $.browser.opera ? 'keypress' : 'keydown';
	this.obj.attr('autocomplete', 'off');
        this.obj.blur(function(){
		    that.results.hide();
		    that.stopAjax();
		})
		.keyup(function(){
                        var val = $(this).val();
                        if (val != that.prevInputVal) {
                            if (val.length>0) {
                                that.ajax();
                            }
                            if (that.results.isVisible()) {
                                that.results.indicate(that.getInput().val());
                            }
                        }
                        that.prevInputVal = val;
                })
                .bind(keyevent, function(e){
                        switch (e.keyCode) {
                            case KEY.ESC:
                                that.getResults().hide();
                                break;
			    case KEY.OPERA_COMMA:
			    case KEY.COMMA:
                            case KEY.RETURN:
				var val = $(this).val();
				var results = that.getResults();
				if (results.isVisible()) {
				    that.results.fireControl(true);
					//that.getInput().val('');
					that.stopAjax();
				} else {
					if (that.options.validate(val)) {
						that.data.add(new TokenizerItem(that.getInput().val(), that.getInput()));
						//that.getInput().val('');
						that.stopAjax();
					}
				}
                                //return false;
                                break;
                            case KEY.DOWN:
                                var results = that.getResults();
                                if (results.isVisible) {
                                    results.moveIndex(1);
                                }
                                break;
                            case KEY.UP:
                                var results = that.getResults();
                                if (results.isVisible) {
                                    results.moveIndex(-1);
                                }
                                break;
                        }
                });

        var container = (jQuery)(document.createElement('div'))
                        .addClass('autocomplete-area')
        obj.wrap(container);
	this.container = obj.parent();
	
        this.container.css('width', (obj.outerWidth()-(this.container.outerWidth()-this.container.width()))+'px');
	
    
        this.results = new AutocompleteResults(this);
    },
    
    getResults: function() {
        return this.results;
    },
    
    getInput: function() {
        return this.getObj();
    },
    
    getContainer: function() {
        return this.container;
    },
    
    getObj: function() {
        return this.obj;
    }
}

/**
 * Результаты поиска
 */
AutocompleteResults = function(ar) {
    this.init(ar);
}

AutocompleteResults.prototype = {
    ar: null,
    
    init: function(ar) {
        this.ar = ar;
        this.container = (jQuery)(document.createElement('ul'))
                        .addClass('autocomplete-results')
                        .css('display', 'none')
        this.ar.getInput().after(this.container);
        this.container.css('width', this.ar.getContainer().outerWidth() - (this.container.outerWidth()-this.container.width()));
        this.reset();
    },
    
    reset: function() {
        this.active = 0;
    },
    
    show: function () {
        this.container.show();
        this.ar.getInput().focus();
    },
    
    hide: function () {
        this.container.hide();
    },
    
    fireControl: function(needFocus) {
        val = this.val();
        this.hide();
	this.ar.getInput().val(val);
    },
    
    set: function(results) {
        this.container.html('');
        var that = this;
        var i = 0;
        for (index in results) {
            r = results[index];
            this.container.append('<li><a '+ (i++==this.active ? 'class="active"' : '') +' href="#">'+r+'</a></li>');
            this.container.find('li:last a').data('value', r);
        }
        this.container.find('li').each(function(){
            var index = that.container.find('li').index(this);
            $(this).find('a').hover(function(){
                that.setActive(index);
            }).click(function(){
                that.fireControl();
		return false;
            });
        });
        this.reset();
        this.length = results.length;
    },
 
    sort: function(valueRegex, startRegex, value) {
        var items = this.container.find('li a');
        var results = new Array(items.length);
        for (i=0; i<results.length; i++) {
            v = items.eq(i).data('value');
            r = 0;
            //if (v.indexOf(regex)!=-1) r = 1;
            //if (v.substr(0, value.length)==value) r = 2;
	    if (v.match(valueRegex)) {
		r = 1;
	    }
            if (v.match(startRegex)) {
		r = 2;
	    }
            results[i] = new Array();
            results[i]['value'] = v;
            results[i]['relevance'] = r;
        }
        results.sort(function(a, b){
            return b['relevance']-a['relevance']
        });
        for (i=0; i<results.length; i++) {
            items.eq(i).data('value', results[i].value)
                        .html(results[i].value);
        }
        if (results[0]['relevance']==2) {
            this.setActive(0);
        }
    },
 
    indicate: function(value) {
        var highlight = function(haystack, regex) {
            haystack = haystack.replace(regex, '<em>$1</em>');
            return haystack;
        }
	valueRegex = eval('/('+value+')/i');
	startRegex = eval('/^('+value+')/i')
        this.sort(valueRegex, startRegex, value);
        this.container.find('li a').each(function(i){
            val = highlight($(this).data('value'), valueRegex);
            $(this).html(val);
        });
    },
    
    setActive: function(index) {
        var that = this;
        this.active = index;
        this.container.find('li a').each(function(i) {
            $(this).removeClass('active');
            if (i==that.active) {
                $(this).addClass('active');
            }
        });
    },
    
    moveIndex: function(step) {
        var that = this;
        this.active = this.active+step;
        if (this.active<0) this.active = this.length-1;
        if (this.active==this.length) this.active = 0;
        this.container.find('li a').each(function(i){
            $(this).removeClass('active');
            if (i==that.active) {
                $(this).addClass('active');
            }
        });
    },
    
    isVisible: function() {
        return this.container.css('display') != 'none';
    },
    
    val: function() {
        return this.container.find('li').eq(this.active).find('a').data('value');
    }
}
