function autocomplete_cancelform(e)
{
    e.preventDefault();
    return false;
}

var Autocompleter = function (input, f_search, minlength, on_confirm, on_typing, on_nonconfirm)
{
    if (!input.attr("value_element"))
    {
        throw 'Autocompleter: no value element specified';
    }
    else
    {
        this.on_confirm = on_confirm || function () {};
        this.on_typing = on_typing || function () {};
        this.on_nonconfirm = on_nonconfirm || function () {};
        this.minlength = minlength;
        this.e_input = input;
        this.e_value = $('#'+input.attr("value_element"));
        this.f_search = f_search;
        this.e_list = $('#autocomplete-list');
        if (!this.e_list.size())
        {
            $('body').append('<ul id="autocomplete-list"></ul>');
            this.e_list = $('#autocomplete-list');
        }
        this.e_input
            .attr('autocomplete', 'off')
            .bind("keyup", this.keyup.bind(this))
            .bind('keypress', this.keypress.bind(this))
            .bind("blur", this.blur.bind(this));
        this._b_update_position = this.update_position.bind(this);

        this.is_searching = false;
        this.results = [];

        this.form = this.e_input.parents('form');
        this.confirmed = false;
    }
};

Autocompleter.prototype =
{

    update_position: function ()
    {
        this.e_list.width(this.e_input.outerWidth()-2);
        var offset = this.e_input.offset({margin: true, padding: true, border: true});
        var top = (offset.top+this.e_input.outerHeight()+this.e_input.scrollTop());
        var left = (offset.left+this.e_input.scrollLeft());
        this.e_list.css({
            top: top+'px',
            left: left+'px'
        });
    },

    show: function ()
    {
        this.update_position();
        this.bind();
    },

    keyup: function (e)
    {
        switch(e.keyCode)
		{
			case 38:
				this.up();
				break;
			case 40:
				this.down();
				break;
			case 13:
                this.e_input.blur();
                //this.e_input.parents('form').submit();
                break;
			case 9:
                this.blur();
                break;
			case 27:
				this.hide();
				break;
            case 8:
                this.e_value.val('');
                this.confirmed = false;
                var last_val = this.e_input.val();
                if (last_val.indexOf(',') != -1)
                {
                    var first = last_val.split(',')[0].replace(/^\s*|\s*$/g, '');
                    this.e_input.val(first);
                    this.search();
                }
                break;
			default:
				this.search();
		}
    },

    keypress: function (e)
    {
        this.on_typing();
        if (!this.confirmed)
        {
            this.style_typing();
        }
    },

    blur: function (e)
    {
        if (!this.confirmed)
        {
            this.confirm();
            this.style_blur();
        }
    },

    up: function ()
	{
		if (this.e_list.is(':visible'))
		{
			var index = this.selected_index-1;
			if (index < 0)
			{
				index = this.results.length-1;
			}
			this.select(index);
		}
	},

	down: function ()
	{
		if (this.e_list.is(':visible'))
		{
			var index;
			if (this.selected_index == null)
			{
				index = 0;
			}
			else
			{
				index = (this.selected_index+1)%this.results.length;
			}
			this.select(index);
		}
	},

    search: function ()
	{
		if (!this.is_searching)
		{
			this.selected_index = null;
			var val = this.e_input.val();
            this.last_search = val;
			val = val.replace(/^\s+|(,)?\s+$|,$/g, "");
			if (val.length >= this.minlength)
			{
				this.is_searching = true;
				this.f_search(val, this.set_results.bind(this));
                this.form.bind('submit', autocomplete_cancelform);
			}
			else
			{
				this.hide();
			}
		}
	},

    set_results: function (results)
    {
        this.is_searching = false;
        if (results && results.length > 0)
        {
            this.results = results;
            this.e_list.html('');
            for (var i=0; i<results.length; i++)
            {
                var html = '<li index="'+i+'" ';
                html += 'value="'+results[i].value+'">';
                html += results[i].name+'</li>';
                this.e_list.append(html);
            }
            this.show();
            var select = this.select.bind(this);
			var confirm = this.confirm.bind(this);
			this.e_list
				.find('li')
					.click(function (e)
					{
						e.preventDefault();
						confirm();
					})
					.mouseover(function (e)
					{
						e.preventDefault();
						select($(this).attr('index'));
					})
				.end();
            this.select(0);
        }
        else
        {
            this.hide();
        }
        if (this.e_input.val() != this.last_search)
        {
            this.search();
        }
    },

    select: function (index)
	{
		this.e_list
			.find('li')
				.removeClass('selected')
			.end().find('li[@index='+index+']')
				.addClass('selected')
			.end();
		this.selected_index = index;
	},

    confirm: function ()
    {
        if (this.e_list.is(':visible') && this.selected_index != null)
		{
			this.e_value.val(this.e_list
                .find('li[@index='+this.selected_index+']')
                .attr('value'));
			this.e_input
                .val(this.results[this.selected_index].name);
            this.on_confirm(this.results[this.selected_index]);
            this.style_confirmed();
            this.confirmed = true;
		}
        else
        {
            this.on_nonconfirm();
        }
        this.hide();
        this.form.unbind('submit', autocomplete_cancelform);
    },

    hide: function()
    {
        if (this.e_list.is(":visible"))
        {
            this.e_list.hide();
            this.unbind_position();
        }
    },

    show: function ()
    {
        if (this.e_list.is(":hidden"))
        {
            this.update_position();
            this.e_list.slideDown("fast");
            this.bind_position();
        }
    },

    bind_position: function ()
    {
        $(window)
            .bind('scroll', this._b_update_position)
            .bind('resize', this._b_update_position)
        ;
    },

    unbind_position: function()
    {
        $(window)
            .unbind('scroll', this._b_update_position)
            .unbind('resize', this._b_update_position)
        ;
    },

    style_typing: function ()
    {
        //this.e_input.css('border-color', 'red');
    },

    style_confirmed: function ()
    {
        this.e_input.css({
            borderColor: '#aaa',
            backgroundColor: '#b7cbff'
        });
    },

    style_blur: function ()
    {
        this.e_input.animate({backgroundColor: 'white'}, 500);
    }
};

$.fn.autocomplete = function (f_search, minlength, on_confirm, on_typing, on_nonconfirm)
{
    return $(this).each(function ()
    {
        this._autocompleter = new Autocompleter($(this), f_search, minlength, on_confirm, on_typing, on_nonconfirm);
    });
};

$.autocomplete_make_find = function (url)
{
    return function (value, callback)
    {
        $.getJSON(url+'/'+value, function (data)
        {
            callback(data);
        });
    };
}
