/*
 * jQuery 
 * version: 1.0 (2008/11/13)
 * @requires jQuery v1.2.6
 * @todo: Test it with previous jQuery versions
 * @author: sebastien rannou - http://www.aimxhaisse.com
 *
 * licensed under the MIT: http://www.opensource.org/licenses/mit-license.php
 *
 * Revision: 1
 */

jQuery.fn.autocomplete = function (url_to_call, options) {
    var that = jQuery(this),
        results_set = [],
        current_hovered_rank = 0,
        keyEvents = [
            {keycode: 38, action: function () { keyEventKeyUp(); }},
            {keycode: 40, action: function () { keyEventKeyDown(); }},
            {keycode: 13, action: function () { keyEventEnter(); }},
            {keycode: 27, action: function () { keyEventEsc(); }}
        ],
        ul_element = false,
    	o = jQuery.extend({
        onKeystroke:    function (data) {
            return data;
        },
        onSelect:       function (dom_value, json_obj) {
        	alert(dom_value);
            that.attr("value", dom_value);
        },
        keydelay:       300,
        minlength:      1,
        maxresult:      10,
        method:         "post",
        varname:        "input_content",
        params:         ""
    },  options || {});
    
    that.attr('autocomplete', 'off');
    
    /*
     * This method performs DOM initialization
     * Creates a UL with an Unique ID and push it to DOM
     * It's called only once
     */
    (function initDOM() {
        var ul_id = false;
        var ul_node = document.createElement("ul");

        // Performs generation of an unique ID
        var genUniqueId = function () {
            var result = "ul_james_" + Math.round(Math.random() * 424242);

            if (jQuery("#" + result).length > 0)
            {
                result = genUniqueId();
            }
            return result;
        };

        ul_id = genUniqueId();

        jQuery(ul_node).attr("id", ul_id).addClass("autocomplete-result-list");
        that.after(ul_node);
        // Creating a shortcut
        ul_element = jQuery("#" + ul_id);
        ul_element.hide();
        jQuery(ul_element).hover(function() {}, function() {
        	jQuery(ul_element).hide();
        });
    })();
    
    /*
	 * This method performs CSS initialization
     * It sets position's <ul> (especially for IE6)
     * And sets result's width to input's width
     * Because offset can be changed, it's called each time
     * the dom is modified
     */
    var initCSS = function initCSS() {
        var input_offset = that.offset();

        ul_element.css({
        				left: 		0,
                        top:        that.outerHeight(), //input_offset.top + that.outerHeight(),
                        width:      that.outerWidth()-2,
                        position:   "absolute",
                        'z-index':	"100"
                        });
    }
    
    /*
     * This is used to avoid form to be submit
     * when the user press Enter to make his choice
     * @TODO: When user has already made his choice, submit it
     */
    that.keydown(function (event) {
        if (event.keyCode === 13)
        {
            return false;
        }
    });
    
    /*
     * This method performs Keyboard Events
     * @TODO: Build actions for more key events (CTRL? ALT?)
     * or recognize ASCII codes?
     */
    //Timer's ID of next AJAX call
    var keyevent_current_timer = false;
    
    that.keyup(function(event) {
        var is_specific_action = false;
        // Check if a specific action is linked to the keycode
        for (var i = 0; keyEvents[i]; i++)
        {
            if (event.keyCode === keyEvents[i].keycode)
            {
            	if(ul_element.css('display')=='none') return;
                is_specific_action = true;
                keyEvents[i].action();
                break;
            }
        }
        // If it's not a specific action
        if (is_specific_action === false)
        {
            // Unset last timeout if it was defined
            if (keyevent_current_timer !== false)
            {
                window.clearTimeout(keyevent_current_timer);
                keyevent_current_timer = false;
            }
            // Set a now timeout with an AJAX call inside
            keyevent_current_timer = window.setTimeout(function () { 
                ajaxUpdate();
            }, o.keydelay);
        }
	});
    
    /*
     * This method performs AJAX calls
     */
    var ajaxUpdate = function () {
        var value_to_send = that.attr("value");
        // Check length of input's value
        if (value_to_send.length > 0 &&
            (o.minlength === false ||
            value_to_send.length >= o.minlength))
        {
        	o.params.params = $.extend({}, o.params.params, {filterKey: value_to_send});
        	that.css({
        		'background': 'url(/images/backgrounds/search-loader.gif) no-repeat center #fcfcfc',
        		'background-position': 'center right'
        	})
        	$.ajax({
                type:       o.method,
                // @TODO: Would be great if params could be an object
                data:    {data: JSONstring.make(o.params)},
                url:        url_to_call,
                dataType:   "json",
                success:    function (data) {
                    var arr = o.onKeystroke(data['1'].list);
                    results_set = [];
                    current_hovered = 0;
                    for (var i in arr)
                    {
                        if (arr[i] !== null) {
	                        results_set.push({text: arr[i].name, id: arr[i].id, json: arr[i]});
                        }
                    }
                	that.css({
                		'background': '',
                		'background-color': '#fcfcfc'
                	})
                	updateDom();
                }
            });
        }
        else
        {
            cleanResults();
        }
    }
    
    /*
     * This method performs the display of the results set
     * Basically called when an event has been made
     */
    var updateDom = function() {
    	jQuery(ul_element).empty();
    	var is_empty = true;

        initCSS();
        var itemIndex = 0;
        for (var i in results_set)
        {
            if (results_set[i] !== null)
            {
                var li_elem = document.createElement("li");

                jQuery(li_elem).addClass("autocomplete-result-list-item");
                if (i == (current_hovered_rank % results_set.length))
                {
                    jQuery(li_elem).addClass("autocomplete-result-list-item-hover");
                }
                jQuery(li_elem).append(results_set[i].text);
                jQuery(ul_element).append(li_elem);
                bind_elem_mouse_hover(li_elem, i);
                is_empty = false;
            }
            itemIndex++;
            if(itemIndex>=o.maxresult)
            	break;
        }
        if (is_empty)
        {
            jQuery(ul_element).hide();
        }
        else
        {
            jQuery(ul_element).show();
        }
    }
    
    /*
     * This method performs the ability to
     * select a result with mouse
     */
    var bind_elem_mouse_hover = function (elem, i) {
	   jQuery(elem).hover(function() {
            jQuery(ul_element)
            .find(".autocomplete-result-list-item-hover")
            .removeClass("autocomplete-result-list-item-hover");
            jQuery(elem).addClass("autocomplete-result-list-item-hover");
            current_hovered_rank = i;
	    }, function() {
            jQuery(elem).removeClass("autocomplete-result-list-item-hover");
            current_hovered_rank = 0;
	    });
        jQuery(elem).click(function() {
		  keyEventEnter();
        });
    }
    
    /*
     * This method clears results in DOM & JS
     */
    var cleanResults = function () {
        jQuery(ul_element).empty();
        jQuery(ul_element).hide();
        results_set = [];
        current_hovered_rank = 0;
    }
    
    /*
     * Key event actions
     */
    
    // Moving up into results set
    var keyEventKeyUp = function () {
        if (current_hovered_rank > 0)
        {
            current_hovered_rank--;
        }
        else if (results_set.length)
        {
                current_hovered_rank = results_set.length - 1;
        }
        updateDom();
    }
    
    // Moving down into resuls set
    var keyEventKeyDown = function () {
        if (current_hovered_rank < (results_set.length - 1))
        {
            current_hovered_rank++;
        }
        else
        {
            current_hovered_rank = 0;
        }
        updateDom();
    }
    
    // Selecting a set (onSelect function is called there)
    var keyEventEnter = function () {
        if (results_set.length > 0) {
	        o.onSelect(results_set[current_hovered_rank].text, results_set[current_hovered_rank].json);
        }
        cleanResults();
    }
    
    // Removing results set
    var keyEventEsc = function () {
        that.attr("value", "");
        cleanResults();
    }
	return o;
};
