/**
 * Simple Emoticon filter that converts plain-text emoticons to <DIV> with css class names based on the emoticon
 *
 * @param megaChat
 * @returns {EmoticonsFilter}
 * @constructor
 */
var EmoticonsFilter = function(megaChat) {
    var self = this;

    self.emoticonsRegExp = /(:[a-zA-Z0-9\-_]+:)/gi;
    self.map = {};

    self.emoticonsLoading = megaChat.getEmojiDataSet('emojis')
        .then((emojis) => {
            self.emojis = emojis;
            $.each(emojis, function(k, meta) {
                self.map[meta.n.toLowerCase()] = meta.u;
            });
        })
        .catch(dump)
        .finally(() => {
            delete this.emoticonsLoading;
        });

    self.reservedEmotions = {};
    self.reservedEmotions["tm"] = '\u2122';

    megaChat.on("onBeforeRenderMessage", function(e, eventData) {
        self.processMessage(e, eventData);
    });
    megaChat.on("onBeforeSendMessage", function(e, messageObject) {
        self.processOutgoingMessage(e, messageObject);
    });


    return this;
};

EmoticonsFilter.prototype.processMessage = async function(e, eventData) {
    'use strict';
    var self = this;

    if (eventData.message.decrypted === false) {
        return;
    }

    if (this.emoticonsLoading) {
        await this.emoticonsLoading;
    }

    // ignore if emoticons are already processed
    if (!eventData.message.processedBy) {
        eventData.message.processedBy = {};
    }
    if (eventData.message.processedBy['emojiFltr'] === true) {
        return;
    }

    // use the HTML version of the message if such exists (the HTML version should be generated by hooks/filters on the
    // client side.
    var textContents;
    if (eventData.message.textContents) {
        textContents = eventData.message.textContents;
    } else {
        return; // not yet decrypted.
    }


    var messageContents = eventData.message.messageHtml ? eventData.message.messageHtml : textContents;

    messageContents = self.processHtmlMessage(messageContents);

    if (messageContents) {
        eventData.message.messageHtml = messageContents;
    }
    eventData.message.processedBy['emojiFltr'] = true;
};

/**
 * Simple method of converting utf8 strings containing utf emojis, to strings containing HTML code (e.g. <img /> tags)
 * with emojis as images.
 *
 * Note: any code that uses this function, should implement its own way of tracking if a message was already parsed
 * (and skip double parsing of the same message twice, since this may create weird looking html code).
 *
 * Note 2: any code that uses this function, should ALWAYS take care of eventual XSS
 *
 * @param messageContents {string}
 */
EmoticonsFilter.prototype.processHtmlMessage = function(messageContents) {
    var self = this;

    if (!messageContents) {
        return; // ignore, maybe its a system message (or composing/paused composing notification)
    }

    // convert legacy :smile: emojis to utf
    messageContents = messageContents.replace(self.emoticonsRegExp, function(match, p1) {
        var foundSlug = $.trim(p1.toLowerCase());
        // remove start/end ":"
        foundSlug = foundSlug.substr(1).substr(0, foundSlug.length - 2);

        var utf = self.map[foundSlug];
        if (self.reservedEmotions[foundSlug]) {
            return '<img class="emoji" draggable="false" alt="' + self.reservedEmotions[foundSlug] + '" src="' +
                    staticpath + 'images/mega/twemojis/2_v2/' + twemoji.size + '/' +
                    twemoji.convert.toCodePoint(self.reservedEmotions[foundSlug]) + twemoji.ext + '"/>';
        }

        if (!utf) {
            return match;
        }

        return utf;
    });

    // convert any utf emojis to images
    messageContents = twemoji.parse(messageContents, {
        size: 72,
        callback: function(icon, options, variant) {
            return staticpath + 'images/mega/twemojis/2_v2/' + options.size + '/' + icon + options.ext;
        }
    });

    // inject the awesome onerror for twemojis
    messageContents = messageContents.replace(
        'class="emoji"',
        'class="emoji"'
    );

    // if only one emoji, make it big
    if (
        messageContents.substr(0, 4) === "<img" &&
        messageContents.substr(-1) === ">" &&
        messageContents.indexOf("<img", 1) === -1
    ) {
        messageContents = messageContents.replace(
            'class="emoji"',
            'class="emoji big"'
        );
    }

    var found = false;
    messageContents = messageContents.replace(/alt="([^"]+)"/g, function(match, p1) {
        if (megaChat._emojiData.emojisUtf[p1]) {
            found = true;

            return match
                .replace(
                    match,
                    'alt="' + p1 + '" data-simpletip=":' + megaChat._emojiData.emojisUtf[p1].n + ':"'
                );
        }

        return match;
    });


    if (found) {
        messageContents = messageContents
            .replace(/class="emoji"/g, 'class="emoji simpletip"')
            .replace(/class="emoji big"/g, 'class="emoji big simpletip"');
    }

    return messageContents;
};

EmoticonsFilter.prototype.processOutgoingMessage = async function(e, messageObject) {
    'use strict';
    var self = this;

    if (this.emoticonsLoading) {
        await this.emoticonsLoading;
    }

    var contents = messageObject.textContents;

    if (!contents) {
        return; // ignore, maybe its a system message (or composing/paused composing notification)
    }

    contents = contents.replace(self.emoticonsRegExp, function(match) {
        var origSlug = $.trim(match.toLowerCase());
        var foundSlug = origSlug;


        if (foundSlug.substr(0, 1) === ":" && foundSlug.substr(-1, 1) === ":") {
            foundSlug = foundSlug.substr(1, foundSlug.length - 2);
        }

        var utf = self.map[foundSlug];

        if (utf && !self.reservedEmotions[foundSlug]) {
            return match.replace(origSlug, utf);
        } else {
            return match;
        }
    });

    messageObject.textContents = contents;
};

EmoticonsFilter.prototype.fromUtfToShort = function(s) {
    var self = this;
    var cached = {};
    return s.replace(/[^\x00-\x7F]{1,}/g, function(match) {
        if (cached[match]) {
            return ":" + cached[match] + ":";
        }
        var found = false;
        Object.keys(self.map).forEach(function(slug) {
            if (self.reservedEmotions[slug]) {
                return false;
            }
            var utf = self.map[slug];
            cached[utf] = slug;

            if (!found && utf === match) {
                found = slug;
                return false;
            }
        });

        return found ? (":" + found  + ":") : match;
    });
};
