/**
 * DadaSuggest jQuery Plugin - Creates a Google style auto-suggestion control for an input text field search box.
 *
 * As the user types text into the input text field, a list of suggestions can appear underneath it, from which the user can select a suggestion by clicking on it.
 *
 * @author Joel J. Hernandez <joel.hernandez@dada-ent.com>.
 * @version 1.0.
 * @license Free
 * @var obj The DOM Element representing the input text field corresponding to the search box.
 * @var defaults Default values to use.
 * @var options Values to use after any user defined overrides have been made to the defaults.
 * @var query Value of the input search field after the current 'keyup' event.
 * @var options.startSearchUrlQueryString Beginning of the querystring for options.searchSelectURL.
 * @var options.suggestURL URL to which the Ajax request is made in order to get the list of suggestions.
 * @var options.searchSelectURL URL which displays the list of results for the search string.
 * @var options.searchEnterURL URL which displays the list of results for the search string after the user hits "Enter" in the input text field search box.
 * @var options.suggestURLParams JSON Object used by the Ajax call to send the arguments. For example, for a PHP script, this data will be available in the $_POST or $_GET superglobals, depending on the type of request (POST or GET).
 * @var options.goSearchOnSelect If set to true, upon selecting a suggestion a redirect to options.searchSelectURL takes place, otherwise there is no redirect and just the value of the input text field changes to take the value of the selected suggestion.
 * @var options.autoHideSuggestions If true, hides the suggestions when clicking anywhere on the page.
 * @var options.loopArrowKeys If true, suggestions list becomes "circular" when traversing it, meaning that the first and last elements are neighboors.
 * @var options.autocompleteArrowKeys If true, automatically updates the value of the search query with the current suggestion when using the arrow keys to traverse the list of suggestions. Otherwise the value of the search query stays the same.
 * @var options.autocompletePointerHover If true, automatically updates the value of the search query with the current suggestion when using the mouse or pointer device to traverse the list of suggestions. Otherwise the value of the search query stays the same.
 * @var options.autocompleteRevert Sets the valus of the input text field search box to its original value before browsing the suggestions list with either the arrow keys or the mouse pointer.
 * @var options.minLength Minimum length for the user typed search string for which suggestions can be given.
 * @var options.maxLength Don't do any more ajax requests if the query string is longer than this value.
 * @var options.ajaxInterval Minumum time period in miliseconds between ajax requests. Use this value to avoid doing expensive ajax calls unless a certain period of time defined by options.ajaxInterval has elapsed since the previous request.
 * @var options.useAjaxInterval It true it uses the options.ajaxInterval constraint, otherwise no.
 * @var options.responseType Type of response returned by the server (json, xml, html, text, script).
 * @var options.requestType Type of the request (GET or POST).
 * @var options.suggestionsContainer ID of the DOM element (usually a <div>) that wraps the list (<ul>) of suggestions.
 * @var options.ulID ID of the <ul> DOM element containing the list of suggestions.
 * @var options.liClass Class name for the <li> DOM elements each corresponding to a suggestion.
 * @var options.liHoverClass Class name for the suggestion over which the mouse pointer is hovering.
 * @var options.buttonID DOM element ID for the button that sends the query string to the server search resource (equivalent to pressing "Enter").
 * @var options.liIdSuffixUnique Unique suffix of each li DOM element id, used to allow multiple autosuggestion controls in the same page without any name conflicts. Note that if this value is set by the user, then the last url parameter in the call to options.searchSelectURL or options.searchEnterURL will have this value attached at the end.
 *
 * Notes:
 * - Only JSON is supported as the responseType. If you require another response type, you must implement your own in the corresponding case of the switch statement.
 * - All the css styles used by default can be set in your own stylesheet. Just make sure that if you use the default ID and Class names you use these names in your styles declaration, otherwise if you override the ID and Class names then equally write your styles accordingly.
 * - Check the included css file to see the default styles used.
 * - The recomended layout of the elements in your home page for the autosugestion control is the following:
 *       <div>                 main wrapper
 *           <div>             input text field search box wrapper
 *                <input />    input text field search box
 *           </div>
 *           <div />           suggestions wrapper
 *       </div>
 */
(function($) {
    $.fn.dadasuggest = function(options) {
        var defaults = {
                "suggestURL"                : "suggest.php",
                "searchSelectURL"           : "search.php",
                "searchEnterURL"            : "search.php",
                "suggestURLParams"          : {},
                "startSearchUrlQueryString" : "?q=",
                "goSearchOnSelect"          : true,
                "autoHideSuggestions"       : true,
                "loopArrowKeys"             : true,
                "autocompleteArrowKeys"     : true,
                "autocompletePointerHover"  : false,
                "autocompleteRevert"        : false,
                "minLength"                 : 3,
                "maxLength"                 : 30,
                "ajaxInterval"              : 1000,
                "useAjaxInterval"           : true,
                "responseType"              : "json",
                "requestType"               : "POST",
                "suggestionsContainer"      : "suggestionsWrapper",
                "ulID"                      : "suggestionsULDefaultDadaSuggest",
                "liClass"                   : "suggestionLIDefaultDadaSuggest",
                "liClassGroup"              : "suggestionLIDefaultDadaSuggestGroup",
                "liHoverClass"              : "suggestionLIDefaultDadaSuggestHover",
                "buttonID"                  : "searchGO",
                "liIdSuffixUnique"          : ""
            };
        var options = $.extend(defaults, options);
        var keys            = new Array();  // Cache keys.
        var vals            = new Array();  // Cache values.
        var liText          = new Array();  // Cache list.
        var tmp             = new Array();
        var start           = "<ul id='" + options.ulID + "'>";
        var end             = "</ul>";
        var middle          = "";
        var aux             = "";
        var currli          = -1;
        var prevli          = 0;
        var lisize          = 0;
        var saved           = "";
        var firstAjax       = false;
        var firstAjaxLength = 0;
        var previousAjax    = (new Date().getTime()) - 1000;
        var currentAjax;
        var searchBox;
        $("#" + options.suggestionsContainer).html("");

        function addToCache(key, val) {
            var pos = $.inArray(key, keys);
            if (pos == -1) {
                keys.push(key);
                vals.push(val);
            }else {
                vals[pos] = val;
            }
        }

        function getFromCache(key) {
            var pos = $.inArray(key, keys);
            return ((pos != -1) ? vals[pos] : "");
        }

        function isOnTrack(item) {
            var ret = false;
            var str = "";
			if (options.useAjaxInterval == false) {
				return false;
			}
            if (liText.length != 0) {
				var grp = '';
                $.each(liText[liText.length - 1], function(key, val) {
                    if (val.toLowerCase().indexOf('class="' + options.liClass + ' ' + options.liClassGroup + '"') != -1) {
						// Gruppe
						ret = true;
						grp = val;
					} else if (val.toLowerCase().indexOf(item.toLowerCase()) != -1) {
                        ret = true;
						str += grp + val;
						grp = '';
                    }
                });
                if (str != "") {
                    $("#" + options.suggestionsContainer).html(start + str + end);
                }
                bindEvents(searchBox);
            }
            return ret;
        }

        function bindEvents(elem) {
            $("." + options.liClass).bind("click", function(me) {
                if (!$(this).hasClass(options.liClassGroup)) {
					elem.val($(this).html());
					if (options.goSearchOnSelect) {
						location.href = options.searchSelectURL + $(this).attr("id");  // Go to the search page.
					}
				}
            }).hover(
                function() {
                    if (options.autocompletePointerHover) {
                        elem.val($(this).html());
                    }
					if (!$(this).hasClass(options.liClassGroup)) {
						if (currli != -1) {
							$($("#" + options.ulID).children().get(currli)).addClass(options.liClass);
							$($("#" + options.ulID).children().get(currli)).removeClass(options.liHoverClass);
						}
						currli = $("#" + options.ulID).children().index(this);
						$(this).addClass(options.liHoverClass);
						$(this).removeClass(options.liClass);
					}
                },
                function() {
                    $(this).addClass(options.liClass);
                    $(this).removeClass(options.liHoverClass);
                }
            );
        }

        return this.each(function() {
            var obj      = $(this);
            searchBox    = obj;
            var query    = "";

            if ($("#" + options.buttonID)) {
                $("#" + options.buttonID).bind("click", function (t) {
					return true;
                    var qry = $.trim(obj.val());
                    if (qry) {
                        var selectExistsBtn = false;
                        var paramsBtn       = "";
                        var locSelectBtn    = options.searchSelectURL;
                        var locEnterBtn     = options.searchEnterURL + options.startSearchUrlQueryString + qry;
                        $.each($("#" + options.ulID).children(), function(i, li) {
                            if ($(li).html().toLowerCase() == qry.toLowerCase()) {
                                if (!selectExistsBtn) {
                                    locSelectBtn += $(li).attr("id");
                                }
                                selectExistsBtn = true;
                            }
                        });
                        location.href = selectExistsBtn ? locSelectBtn : locEnterBtn;
                    }
                });
            }

            if (options.autoHideSuggestions) {
                /**
                 * Make the suggestions go away when clicking anywhere on the page.
                 */
                $("*").bind("click", function(t) {
                    if ($(this).attr("id") != options.buttonID) {
                        $("#" + options.suggestionsContainer).html("");
                    }
                });
            }

            obj.bind("keyup", function(e) {
                query  = $.trim(obj.val());
                lisize = $("#" + options.ulID).children().length;

                if (query.length >= options.minLength) {
                    if ((e.which >= 37) && (e.which <= 40)) {
                        if (lisize > 0) {
                            prevli = (currli == -1) ? 0 : currli;
                            /*if ((e.which == 39) || (e.which == 40)) {  // "Down" and "Right" keys, moving forward in the suggestions list.
                                if (options.loopArrowKeys) {
                                    currli = (currli == (lisize - 1)) ? 0 : (currli + 1);
                                } else {
                                    currli = (currli == (lisize - 1)) ? (lisize - 1) : (currli + 1);
                                }
								while ($($("#" + options.ulID).children().get(currli)).hasClass(options.liClassGroup)) {
									if (options.loopArrowKeys) {
										currli = (currli == (lisize - 1)) ? 0 : (currli + 1);
									}else {
										currli = (currli == (lisize - 1)) ? (lisize - 1) : (currli + 1);
									}
								}
                            } else {                                    // "Up" and "Left" keys, moving backwards in the suggestions list.
                                if (options.loopArrowKeys) {
                                    currli = (currli <= 0) ? (lisize - 1) : (currli - 1);
                                }else {
                                    currli = (currli <= 0) ? 0 : (currli - 1);
                                }
								while ($($("#" + options.ulID).children().get(currli)).hasClass(options.liClassGroup)) {
									if (options.loopArrowKeys) {
										currli = (currli <= 0) ? (lisize - 1) : (currli - 1);
									}else {
										currli = (currli <= 0) ? 0 : (currli - 1);
									}
								}
                            }*/
                            $($("#" + options.ulID).children().get(prevli)).addClass(options.liClass);
                            $($("#" + options.ulID).children().get(prevli)).removeClass(options.liHoverClass);
                            $($("#" + options.ulID).children().get(currli)).addClass(options.liHoverClass);
                            $($("#" + options.ulID).children().get(currli)).removeClass(options.liClass);
                            if (options.autocompleteArrowKeys) {
                                obj.val($($("#" + options.ulID).children().get(currli)).html());
                            }
                        }
                    }else {
                        saved = query;
                        if (e.which != 13) {  // Any key except "Enter".
							if (options.useAjaxInterval || getFromCache(query) != "") {
                                $("#" + options.suggestionsContainer).html(start + getFromCache(query) + end);
                                bindEvents(obj);  // Bind event handlers to different events for the suggestions.
                            } else if (!isOnTrack(query)) {
                                currentAjax = new Date().getTime();
                                if ((query.length <= options.maxLength) && (!options.useAjaxInterval || (options.useAjaxInterval && (currentAjax - previousAjax >= options.ajaxInterval)))) {
                                    if (!firstAjax && (e.which != 8)) {
                                        previousAjax                       = currentAjax;
                                        options.suggestURLParams.q         = query;
                                        options.suggestURLParams.timestamp = new Date().getTime().toString();
                                        $.ajax({
                                            url      : options.suggestURL,
                                            dataType : options.responseType.toLowerCase(),
                                            type     : options.requestType.toUpperCase(),
                                            data     : options.suggestURLParams,
                                            error    : function(response) {
                                                           $("#" + options.suggestionsContainer).html("");
                                                       },
                                            success  : function(response) {
                                                           middle     = "";
                                                           aux        = "";
                                                           tmp.length = 0;
                                                           switch (options.responseType.toLowerCase()) {
                                                               case 'json':
                                                                   $.each(response, function(n, record) {
                                                                       cl = options.liClass;
																	   if (record.is_group == '1') {
                                                                           cl += " " + options.liClassGroup;
																	   }
                                                                       aux = "<li class='" + cl + "' id='" + record.urlParams + options.liIdSuffixUnique + "'>" + record.text + "</li>";
                                                                       middle += aux;
                                                                       tmp.push(aux);
                                                                   });
                                                                   break;
                                                               case 'xml':
                                                                   // To do.
                                                                   break;
                                                               case 'html':
                                                                   // To do.
                                                                   break;
                                                               case 'text':
                                                                   // To do.
                                                                   break;
                                                               case 'script':
                                                                   // To do.
                                                                   break;
                                                               default:
                                                                   // Unsoported response type.
                                                                   break;
                                                           }
                                                           liText.push(tmp);
                                                           addToCache(query, middle);
														   if (!options.useAjaxInterval) {
															   firstAjax = false;
														   }
                                                           if (middle != "") {
                                                               $("#" + options.suggestionsContainer).html(start + middle + end);
                                                               bindEvents(obj);
                                                           }else if ($("#" + options.suggestionsContainer).html() != "") {
                                                               $("#" + options.suggestionsContainer).html("");
                                                           }
                                                       }
                                        });
                                        firstAjax       = true;
                                        firstAjaxLength = query.length;
                                    }
                                }
                            }
                        }else {  // "Enter" key pressed.
                            /*obj.val($.trim(obj.val()));
                            if (obj.val()) {
                                var selectExists = false;
                                var params       = "";
                                var locSelect    = options.searchSelectURL;
                                var locEnter     = options.searchEnterURL + options.startSearchUrlQueryString + obj.val();
                                $.each($("#" + options.ulID).children(), function(i, li) {
                                    if ($(li).html().toLowerCase() == obj.val().toLowerCase()) {
                                        if (!selectExists) {
                                            locSelect += $(li).attr("id");
                                        }
                                        selectExists = true;
                                    }
                                });
                                location.href = selectExists ? locSelect : locEnter;
                            }*/
                        }
                    }
                }else {
                    firstAjax = false;
                    $("#" + options.suggestionsContainer).html("");
                }
            }).bind("click", function(e) {
                if (options.autocompleteRevert && (obj.val().length >= options.minLength)) {
                    obj.val(saved);
                }
            });
        });
    };
})(jQuery);
