(function ($) {
    var globalTags = [];
    var current=0;

    window.setGlobalTags = function(tags) {
        globalTags = getTags(tags);
    };
    
    function getTags(tags) {
        var tag, i, goodTags = [];
        for (i = 0; i < tags.length; i++) {
            tag = tags[i];
            if (typeof tags[i] == 'object') {
                tag = tags[i].tag;
            } 
            goodTags.push(tag.toLowerCase());
        }
        
        return goodTags;
    }
    
    $.fn.tagSuggest = function (options) {
        var defaults = { 
            'matchClass' : 'tagMatches', 
            'tagContainer' : 'div', 
            'tagWrap' : 'li', 
            'sort' : true,
            'tags' : null,
            'url' : null,
            'delay' : 250,
            'separator' : ', '
        };

        var i, tag, userTags = [], settings = $.extend({}, defaults, options);

        if (settings.tags) {
            userTags = getTags(settings.tags);
        } else {
            userTags = globalTags;
        }

        return this.each(function () {
            var tagsElm = $(this);
            var elm = this;
            var matches, fromTab = false;
            var suggestionsShow = false;
            var workingTags = [];
            var currentTag = {"position": 0, tag: ""};
            var tagMatches = document.createElement(settings.tagContainer);
            
            function showSuggestionsDelayed(el, key) {
                if (settings.delay) {
                    if (elm.timer) clearTimeout(elm.timer);
                    elm.timer = setTimeout(function () {
                        showSuggestions(el, key);
                    }, settings.delay);
                } else {
                    showSuggestions(el, key);
                }
            }

            function showSuggestions(el, key) {
                workingTags = el.value.split(settings.separator);
                matches = [];
                var i, html = '', chosenTags = {}, tagSelected = false;
                currentTag = { position: currentTags.length-1, tag: '' };
                
                for (i = 0; i < currentTags.length && i < workingTags.length; i++) {
                    if (!tagSelected && 
                        currentTags[i].toLowerCase() != workingTags[i].toLowerCase()) {
                        currentTag = { position: i, tag: workingTags[i].toLowerCase() };
                        tagSelected = true;
                    }
                    chosenTags[currentTags[i].toLowerCase()] = true;
                }

                if (currentTag.tag && (currentTag.tag.length>1)) {
                    if (settings.url) {
                        $.ajax({
                            'url' : settings.url,
                            'dataType' : 'json',
                            'data' : { 'tag' : currentTag.tag },
                            //'async' : false, // wait until this is ajax hit is complete before continue
                            'success' : function (m) {
                                matches = m;
                                if (matches.length > 0) {
                                  for (i = 0; i < matches.length; i++) {
                                      html += '<li><a id="t'+i+'" class="_tag'+(i==0?' hover':'')+'" href="#tag">' + matches[i][1] + '</a></li>';
                                  }
                                  tagMatches.html('<ul>'+html+'</ul>');
                                  current=0;
                                  suggestionsShow = !!(matches.length);
                                }
                            }
                        });
                    } 
                } else {
                    hideSuggestions();
                }
            }

            function hideSuggestions() {
                tagMatches.empty();
                matches = [];
                suggestionsShow = false;
            }

            function setSelection() {
                var v = tagsElm.val();
                if (v == tagsElm.attr('title') && tagsElm.is('.hint')) v = '';
                currentTags = v.split(settings.separator);
                hideSuggestions();
            }
            
            function selectItem() {
              $('a._tag').removeClass("hover"); 
              $('#t'+current).addClass("hover");
            }

            function chooseTag(tag) {
                var i, index;
                for (i = 0; i < currentTags.length; i++) {
                    if (currentTags[i].toLowerCase() != workingTags[i].toLowerCase()) {
                        index = i;
                        break;
                    }
                }

                if (index == workingTags.length - 1) tag = tag + settings.separator;
                workingTags[i] = tag;
                tagsElm.val(workingTags.join(settings.separator));
                tagsElm.blur().focus();
                setSelection();
            }

            function handleKeys(ev) {
                fromTab = false;
                var type = ev.type;
                var resetSelection = false;
                switch (ev.keyCode) {
                    case 38: {if(suggestionsShow && (type == 'keyup') && (current>0)){current--;selectItem();}return false;} // up
                    case 40: {if(suggestionsShow && (type == 'keyup') && (current<(matches.length-1))){current++;selectItem();}return false;} // down
                    case 37: // left ignore cases (arrow keys) 
                    case 39: {
                        hideSuggestions();
                        return true;
                    }
                    case 224:
                    case 17:
                    case 16:
                    case 18: {
                        return true;
                    }

                    case 8: {
                        // delete - hide selections if we're empty
                        if (this.value == '') {
                            hideSuggestions();
                            setSelection();
                            return true;
                        } else {
                            type = 'keyup'; // allow drop through
                            // resetSelection = true;
                            showSuggestionsDelayed(this);
                        }
                        break;
                    }

                    case 9: // return and tab
                    case 13: {
                        if (suggestionsShow) {
                            // complete
                            chooseTag(matches[current][0]);
                            
                            fromTab = true;
                            return false;
                        } else {
                            return true;
                        }
                    }
                    case 27: {
                        hideSuggestions();
                        setSelection();
                        return true;
                    }
                    case 32: {
                        setSelection();
                        return true;
                    }
                }

                if (type == 'keyup') {
                    switch (ev.charCode) {
                        case 9:
                        case 13: {
                            return true;
                        }
                    }

                    if (resetSelection) { 
                        setSelection();
                    }
                    showSuggestionsDelayed(this, ev.charCode);            
                }
            }

            // replace with jQuery version
            
           tagMatches = $(tagMatches).bind('mousedown', function (ev) {
                if ($(ev.target).hasClass('_tag')) {
                    chooseTag(matches[ev.target.id.substring(1,ev.target.id.length)][0]);return false;
                }   
            }).addClass(settings.matchClass); 
            
            tagsElm.after(tagMatches).keypress(handleKeys).keyup(handleKeys).blur(function () {
               // if (fromTab == true || suggestionsShow) { 
                if (fromTab == true) { 
                    fromTab = false;
                    tagsElm.focus();
                } 
                if (suggestionsShow) {
                  hideSuggestions();
                }
            });

            
            
            // initialise
            setSelection();
            current=0;
            selectItem();
        });
    };
})(jQuery);


