
var EMOJI_BAR = (function() {
    var dom = document.createElement("div")
        ,overlay = document.createElement("div")
        ,emojisDom = document.createElement("div")
        ,unicodeEmojis = document.createElement("ul")
        ,customEmojis = document.createElement("ul")
        ,searchBar = document.createElement("input")
        ,emojiCache = { unicode: {}, custom: {}}
        ,emojiDetail = document.createElement("div")
        ,emojiDetailImg = document.createElement("span")
        ,emojiDetailName = document.createElement("span")
        /** @type {function((string|null))|undefined} */
        ,emojiSelectedHandler

    /** @type {function():boolean} */
    ,isSupported = function() {
        return ("searchEmojis" in window);
    }

    ,makeHeader = function(imgSrc) {
        var img = document.createElement("img")
            ,dom = document.createElement("div");
        img.src = imgSrc;
        dom.appendChild(img);
        dom.className = R.klass.emojibar.header;
        return dom;
    }

    /** @type {function(string, Element):{dom:Element,visible:boolean}} */
    ,wrapEmojiLi = function(emojiName, emojiDom) {
        var dom = document.createElement("li");
        dom.appendChild(emojiDom);
        dom.className = R.klass.emojibar.item;
        dom.id = "emojibar-" +emojiName;
        return {
            visible: false
            ,dom: dom
        };
    }

    /** @type {function(string, *):{dom:Element,visible:boolean}} */
    ,makeUnicodeEmojiLi = function(emojiName, emoji) {
        var domEmoji = window['makeEmoji'](emoji)
            ,domParent = document.createElement("span");
        domParent.appendChild(domEmoji);
        domParent.className = R.klass.emoji.medium;
        return wrapEmojiLi(emojiName, domParent);
    }

    /** @type {function(string, string):{dom:Element,visible:boolean}} */
    ,makeCustomEmoji = function(emojiName, emojiSrc) {
        var domEmoji = document.createElement("span")
            ,domParent = document.createElement("span");
        domEmoji.className = R.klass.emoji.emoji +' ' +R.klass.emoji.custom;
        domEmoji.style.backgroundImage = 'url("' +emojiSrc +'")';
        domParent.appendChild(domEmoji);
        domParent.className = R.klass.emoji.medium;
        return wrapEmojiLi(emojiName, domParent);
    }

    /** @type {function(Object.<string,*>, Object.<string, number>):Array.<{name:string, pos:number, count:number}>} */
    ,sortEmojis = function(emojiObj, favoriteEmojis) {
        var names = []
            ,index = 0;
        for (var i in emojiObj) {
            var obj = {
                name: i,
                pos: index,
                count: 0
            };
            emojiObj[i].names.forEach(function(name) {
                obj.count += (favoriteEmojis[name] || 0);
            });
            names.push(obj);
        }
        names = names.sort(function(a, b) {
            var diff = b.count -a.count;
            if (diff) return diff;
            return a.pos -b.pos;
        });
        return names;
    }

    /** @type function(string=):number */
    ,search = function(queryString) {
        var emojiCount = 0
            ,toRemove = []
            /** @type {Array.<{name:string, pos:number, count:number}>} */
            ,sortedEmojiNames;
        queryString = queryString === undefined ? searchBar.value : queryString;

        // Service emojis
        if (isSupported()) {
            /** @type {Object.<string, *>} */
            var foundEmojis = window['searchEmojis'](queryString);

            sortedEmojiNames = sortEmojis(foundEmojis, SLACK.context.self.prefs.favoriteEmojis);
            for (var i in emojiCache.unicode) {
                if (emojiCache.unicode[i].visible) {
                    // We remove every item to reorder them (add them in order)
                    emojiCache.unicode[i].visible = false;
                    unicodeEmojis.removeChild(emojiCache.unicode[i].dom);
                }
            }
            for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
                var emojiName = sortedEmojiNames[i].name
                    ,e = emojiCache.unicode[emojiName];
                if (!e)
                    e = emojiCache.unicode[emojiName] = makeUnicodeEmojiLi(emojiName, foundEmojis[emojiName]);
                if (!e.visible) {
                    e.visible = true;
                    unicodeEmojis.appendChild(e.dom);
                }
                emojiCount++;
            }
        }

        // Custom emojis
        for (var i in emojiCache.custom) {
            if (emojiCache.custom[i].visible) {
                emojiCache.custom[i].visible = false;
                customEmojis.removeChild(emojiCache.custom[i].dom);
            }
        }
        sortedEmojiNames = sortEmojis(SLACK.context.emojis.data, SLACK.context.self.prefs.favoriteEmojis);
        for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
            var emojiName = sortedEmojiNames[i].name;
            if ((queryString === '' || emojiName.substr(0, queryString.length) === queryString) && SLACK.context.emojis.data[emojiName].substr(0, 6) !== 'alias:') {
                var e = emojiCache.custom[emojiName];
                if (!e)
                    e = emojiCache.custom[emojiName] = makeCustomEmoji(emojiName, SLACK.context.emojis.data[emojiName]);
                if (!e.visible) {
                    e.visible = true;
                    customEmojis.appendChild(e.dom);
                }
                emojiCount++;
            }
        }
        return emojiCount;
    }

    /** @type function(Element, function((string|null))=):boolean */
    ,spawn = function(domParent, handler) {
        if (isSupported()) {
            emojiSelectedHandler = handler;
            domParent.appendChild(overlay);
            domParent.appendChild(dom);
            searchBar.value = "";
            search();
            searchBar.focus();
            return true;
        }
        return false;
    }

    /** @type {function():boolean} */
    ,doClose = function() {
        if (dom.parentElement) {
            dom.parentElement.removeChild(overlay);
            dom.parentElement.removeChild(dom);
            return true;
        }
        return false;
    }

    /** @type {function():boolean} */
    ,close = function() {
        var closed = doClose();
        if (!closed)
            return false;
        if (emojiSelectedHandler)
            emojiSelectedHandler(null);
        return true;
    }

    ,onEmojiSelected = function(emojiName) {
        if (doClose() && emojiSelectedHandler)
            emojiSelectedHandler(emojiName);
    }
    ;

    overlay.addEventListener("click", function(e) {
        var bounds = dom.getBoundingClientRect();
        if (e.screenY < bounds.top || e.screenY > bounds.bottom ||
            e.screenX < bounds.left || e.screenX > bounds.right)
            close();
    });

    overlay.className = R.klass.emojibar.overlay;
    dom.className = R.klass.emojibar.container;
    emojisDom.className = R.klass.emojibar.emojis;
    emojiDetail.className = R.klass.emojibar.detail.container;
    emojiDetailImg.className = R.klass.emojibar.detail.img;
    emojiDetailName.className = R.klass.emojibar.detail.name;
    unicodeEmojis.className = customEmojis.className = R.klass.emojibar.list;
    searchBar.className = R.klass.emojibar.search;
    emojiDetail.appendChild(emojiDetailImg);
    emojiDetail.appendChild(emojiDetailName);
    emojisDom.appendChild(makeHeader(window['emojiProviderHeader']));
    emojisDom.appendChild(unicodeEmojis);
    emojisDom.appendChild(makeHeader("emojicustom.png"));
    emojisDom.appendChild(customEmojis);
    dom.appendChild(emojisDom);
    dom.appendChild(emojiDetail);
    dom.appendChild(searchBar);

    searchBar.addEventListener("keyup", function() {
        search();
    });

    var makeDelegate = function(e, cb) {
        var target = e.target;
        while (target !== dom && target && target.nodeName !== "LI")
            target = target.parentElement;
        if (target && target.nodeName === "LI" && target.id && target.id.substr(0, "emojibar-".length) === "emojibar-") {
            var emojiId = target.id.substr("emojibar-".length);
            return cb(emojiId);
        }
        cb(null);
    };

    dom.addEventListener("mousemove", function(e) {
        makeDelegate(e, function(emoji) {
            var emojiCached = emoji ? (emojiCache.unicode[emoji] || emojiCache.custom[emoji]) : null;
            if (emojiCached) {
                emojiDetailImg.innerHTML = emojiCached.dom.outerHTML;
                emojiDetailName.textContent = ':' +emoji +':';
            } else {
                emojiDetailImg.textContent = "";
                emojiDetailName.textContent = "";
            }
        });
    });

    dom.addEventListener("click", function(e) {
        makeDelegate(e, function(emoji) {
            if (emoji) onEmojiSelected(emoji);
        });
    });

    return {
        isSupported: isSupported
        ,spawn: spawn
        ,search: search
        ,close: close
    };
})();

