import escape from 'escape-html';
import emojiRegex from 'emoji-regex';
import BUILDKITE_EMOJI from '../emojis/buildkite';
import UNICODE_EMOJI from '../emojis/apple';

const UNICODE_REGEXP = emojiRegex();
const COLON_REGEXP = new RegExp('\:[^\\s:]+\:', 'g');

class Emoji {
  parse(string, options = {}) {
    if (!string || string.length === 0) {
      return '';
    }

    // Turn off escaping if the option is explicitly set
    if (options.escape !== false) {
      string = escape(string);
    }

    // Start with replacing BK emoji (which are more likely than Unicode emoji)
    string = this._replaceColons(BUILDKITE_EMOJI, string);

    // Then do a Unicode emoji parse
    if (options.replaceUnicode !== false) {
      string = this._replaceUnicode(UNICODE_EMOJI, string);
    }

    string = this._replaceColons(UNICODE_EMOJI, string);

    return string;
  }

  // replaces unicode emoji with images
  _replaceUnicode(catalogue, string) {
    return string.replace(UNICODE_REGEXP, (match) => {
      const emojiIndex = catalogue.index[match];

      if ((typeof emojiIndex) === 'number') {
        return this._image(catalogue, catalogue.emoji[emojiIndex], match);
      } else {
        return match;
      }
    });
  }

  // replaces emoji shortcodes with images
  _replaceColons(catalogue, string) {
    // NOTE: replacements are done indirectly here, because the
    // colon regexp will not catch modifiers in a single match
    const matches = string.match(COLON_REGEXP);
    const replacements = [];

    // Bail if there aren't any emojis to replace
    if (!matches || !matches.length) {
      return string;
    }

    for (let matchIndex = 0, matchLength = matches.length; matchIndex < matchLength; matchIndex++) {
      const match = matches[matchIndex];
      const nextMatch = matches[matchIndex + 1];

      // See if this match and the next one, makes a new emoji. For example,
      // :fist::skin-tone-4:
      if (nextMatch) {
        const modifiedEmojiIndex = catalogue.index[`${match}${nextMatch}`];

        if ((typeof modifiedEmojiIndex) === 'number') {
          replacements.push(this._image(catalogue, catalogue.emoji[modifiedEmojiIndex]));
          replacements.push("");
          matchIndex += 1;

          continue;
        }
      }

      const emojiIndex = catalogue.index[match];

      if ((typeof emojiIndex) === 'number') {
        replacements.push(this._image(catalogue, catalogue.emoji[emojiIndex]));
      } else {
        replacements.push(match);
      }
    }

    return string.replace(COLON_REGEXP, () => replacements.shift());
  }

  _image({ host }, emoji, match) {
    // Emoji catalogue hosts have a normalized host that always end with a "/"
    const emojiUrl = `${host}${emoji.image}`;
    const emojiCanonicalRepresentation = match || emoji.unicode || `:${emoji.name}:`;

    return `<img class="emoji" title="${emoji.name}" alt="${emojiCanonicalRepresentation}" src="${emojiUrl}" draggable="false" />`;
  }
}

export default new Emoji();

@@ -224,39 +224,42 @@
 P = 
-new RegExp('%5C
+/
 :%5B%5E%5C
-%5C
 s:%5D+
-%5C:', 'g')
+:(?::skin-tone-%5B2-6%5D:)?/g
 ;%0A%0Ac
@@ -606,23 +606,39 @@
 _replace
-Colons(
+(string, COLON_REGEXP, 
 BUILDKIT
@@ -640,32 +640,24 @@
 LDKITE_EMOJI
-, string
 );%0A%0A    // T
@@ -761,80 +761,33 @@
 lace
-Unicode(UNICODE_EMOJI, string);%0A    %7D%0A%0A    string = this._replaceColons(
+(string, UNICODE_REGEXP, 
 UNIC
@@ -799,784 +799,148 @@
 MOJI
-, string
 );%0A
-%0A
     
-return string;%0A  %7D%0A%0A  // replaces unicode emoji with images%0A  _replaceUnicode(catalogue, string) %7B%0A    return string.replace(UNICODE_REGEXP, (match) =%3E %7B%0A      const emojiIndex = catalogue.index%5Bmatch%5D;%0A%0A      if ((typeof emojiIndex) === 'number') %7B%0A        return this._image(catalogue, catalogue.emoji%5BemojiIndex%5D, match);%0A      %7D else %7B%0A        return match;%0A      %7D%0A    %7D);%0A  %7D%0A%0A  // replaces emoji shortcodes with images%0A  _replaceColons(catalogue, string) %7B%0A    // NOTE: replacements are done indirectly here, because the%0A    // colon regexp will not catch modifiers in a single match%0A    const matches = string.match(COLON_REGEXP);%0A    const replacements = %5B%5D;%0A%0A    // Bail if there aren't any emojis to replace%0A    if (!matches %7C%7C !matches.length
+%7D%0A%0A    string = this._replace(string, COLON_REGEXP, UNICODE_EMOJI);%0A%0A    return string;%0A  %7D%0A%0A  _replace(string, regexp, catalogue
 ) %7B%0A
-  
     
@@ -956,335 +956,35 @@
 ring
-;%0A    %7D%0A%0A    for (let matchIndex = 0, matchLength = matches.length; matchIndex %3C matchLength; matchIndex++) %7B%0A      const match = matches%5BmatchIndex%5D;%0A      const nextMatch = matches%5BmatchIndex + 1%5D;%0A%0A      // See if this match and the next one, makes a new emoji. For example,%0A      // :fist::skin-tone-4:%0A      if (nextM
+.replace(regexp, (m
 atch)
+ =%3E
  %7B%0A 
@@ -992,25 +992,15 @@
     
- 
- 
 const 
-modifiedE
+e
 moji
@@ -1027,36 +1027,17 @@
 dex%5B
-%60$%7B
 match
-%7D$%7BnextMatch%7D%60
 %5D;%0A%0A
-  
     
@@ -1050,25 +1050,17 @@
 (typeof 
-modifiedE
+e
 mojiInde
@@ -1090,323 +1090,27 @@
     
-  replacements.push(this._image(catalogue, catalogue.emoji%5BmodifiedEmojiIndex%5D));%0A          replacements.push(%22%22);%0A          matchIndex += 1;%0A%0A          continue;%0A        %7D%0A      %7D%0A%0A      const emojiIndex = catalogue.index%5Bmatch%5D;%0A%0A      if ((typeof emojiIndex) === 'number') %7B%0A        replacements.push(this._image
+return this._render
 (cat
@@ -1145,17 +1145,16 @@
 iIndex%5D)
-)
 ;%0A      
@@ -1176,30 +1176,18 @@
   re
-placements.push(
+turn 
 match
-)
 ;%0A  
@@ -1201,76 +1201,8 @@
    %7D
-%0A%0A    return string.replace(COLON_REGEXP, () =%3E replacements.shift()
 );%0A 
@@ -1208,21 +1208,22 @@
   %7D%0A%0A  _
-image
+render
 (%7B host 
@@ -1234,15 +1234,8 @@
 moji
-, match
 ) %7B%0A
@@ -1358,16 +1358,74 @@
 image%7D%60;
+%0A%0A    // Prioritise unicode representation over shortcodes
 %0A    con
@@ -1461,17 +1461,8 @@
 on =
- match %7C%7C
  emo

Simplify emoji colon replacements