(function (exports) {

  // Import some things we need.
  var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
  var { ExtensionSupport } = ChromeUtils.import("resource:///modules/ExtensionSupport.jsm");

  var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

  var { MsgHdrToMimeMessage } = ChromeUtils.import("resource:///modules/gloda/MimeMessage.jsm");

  var tracker;

  class Tracker {
    constructor(extension) {
      this.extension = extension;
      this.windowTracker = new Set();
      this.onClickedListener = new ExtensionCommon.EventEmitter();

      this.notifyOnClickedListener = (event) => {
        let window = event.target.ownerGlobal;
        let tabmail = window.document.getElementById("tabmail");
        // for standalone message windows "tabmail" = null
        // therefore we must then use "window" to get "nativeTab"
        let nativeTab = tabmail ? tabmail.currentTabInfo : window;

        let tabId = this.extension.tabManager.getWrapper(nativeTab).id;
        this.onClickedListener.emit("remoteContentForMsg-clicked", tabId);
      }
    }

    get instanceId() {
      return `${this.extension.uuid}_${this.extension.instanceId}`;
    }

    get windowListenerId() {
      return `windowListener_${this.instanceId}`;
    }

    addOnClickedListener(callback) {
      this.onClickedListener.on("remoteContentForMsg-clicked", callback);
    }

    removeOnClickedListener(callback) {
      this.onClickedListener.off("remoteContentForMsg-clicked", callback);
    }

    trackWindow(window) {
      this.windowTracker.add(window);
    }

    untrackWindow(window) {
      this.windowTracker.delete(window);
    }

    get trackedWindows() {
      return Array.from(this.windowTracker);
    }

  }

  class allowHtmlTemp extends ExtensionCommon.ExtensionAPI {
    // Alternative to defining a constructor here in order to init the class, is
    // to use the onStartup event. However, this causes the API to be instantiated
    // directly after the add-on has been loaded, not when the API is first used.
    constructor(extension) {
      // The only parameter is extension, but it could change in the future.
      // super() will add the extension as a member of this.
      super(extension);

      tracker = new Tracker(this.extension);
      ExtensionSupport.registerWindowListener(
        tracker.windowListenerId, {
          chromeURLs: [
            "chrome://messenger/content/messenger.xhtml",
            "chrome://messenger/content/messageWindow.xhtml"
          ],
          onLoadWindow: function (window) {
            // console.debug("AHT - onLoadWindow");
            let element = window.document.getElementById("remoteContentOptionAllowForMsg");
            element.addEventListener("click", tracker.notifyOnClickedListener);

            // let oncommand_modified = "console.debug('AHT: remoteContentOptionAllowForMsg');";
            // element.setAttribute("oncommand", oncommand_modified);
            element.removeAttribute("oncommand");

            tracker.trackWindow(window);
          },
          onUnloadWindow: function (window) {
            // console.debug("AHT - onUnloadWindow");
            let element = window.document.getElementById("remoteContentOptionAllowForMsg");
            element.removeEventListener("click", tracker.notifyOnClickedListener);

            let oncommand_original = "LoadMsgWithRemoteContent();";
            element.setAttribute("oncommand", oncommand_original);

            tracker.untrackWindow(window);
          }
        }
      );
    }

    // The API implementation.
    getAPI(context) {
      return {
        // Again, this key must have the same name.
        allowHtmlTemp: {

          // An event. Most of this is boilerplate you don't need to worry about, just copy it.
          onClick: new ExtensionCommon.EventManager({
            context,
            name: "allowHtmlTemp.onClick",
            // In this function we add listeners for any events we want to listen to, and return a
            // function that removes those listeners. To have the event fire in your extension,
            // call fire.async.
            register: (fire) => {
              function listener(event, tabId) {
                // console.debug("AHT: onClick register: tabId = " + tabId);
                fire.sync(tabId);
              }
              tracker.addOnClickedListener(listener);
              return () => {
                tracker.removeOnClickedListener(listener);
              };
            },
          }).api(),

          async checkMailForHtmlpart(messageId, optionsDebug) {
            if(optionsDebug)
              console.debug("AHT: run checkMailForHtmlpart ----------------");
            let ahtMsgHdr = context.extension.messageManager.get(messageId);
  
            // First check MsgHdr without decrypting to prevent an additional passphrase dialog in case of PGP/MIME
            let aMimeMsg = await new Promise(resolve => {
              MsgHdrToMimeMessage(
                ahtMsgHdr,
                null,
                (aMsgHdr, aMimeMsg) => resolve(aMimeMsg),
                true,
                {
                  examineEncryptedParts: false
                }
              );
            })
  
            // multipart/encrypted enables the button for encrypted PGP/MIME messages
            // in this case we don't check for HTML, because the check seems not to be possible for PGP/MIME
            if (aMimeMsg.prettyString().search("multipart/encrypted") != -1) {
              if(optionsDebug)
                console.debug("AHT: message is PGP/MIME multipart/encrypted");
              return true;
            } else {
              // search for 'Body: text/html' in MIME parts,
              // it seems this is only working if messages are downloaded for offline reading?
              let aMimeMsg = await new Promise(resolve => {
                MsgHdrToMimeMessage(
                  ahtMsgHdr,
                  null,
                  (aMsgHdr, aMimeMsg) => resolve(aMimeMsg),
                  true,
                  {
                    examineEncryptedParts: true
                  }
                );
              })
  
              if(optionsDebug) {
                console.debug("AHT: Check for html part ----------------");
                console.debug("AHT: Body: text/html " + aMimeMsg.prettyString().search("Body: text/html"));
                console.debug("AHT: text/html " + aMimeMsg.prettyString().search("text/html"));
                console.debug("AHT: Body: plain/html " + aMimeMsg.prettyString().search("Body: plain/html"));
                console.debug("AHT: plain/html " + aMimeMsg.prettyString().search("plain/html"));
                console.debug("AHT: multipart/alternative " + aMimeMsg.prettyString().search("multipart/alternative"));
                console.debug("AHT: multipart/signed " + aMimeMsg.prettyString().search("multipart/signed"));
                console.debug("AHT: multipart/encrypted " + aMimeMsg.prettyString().search("multipart/encrypted"));
              }
  
              // 'Body: text/html' is found, enable ahtButtons
              if (aMimeMsg.prettyString().search("Body: text/html") != -1) {
                if(optionsDebug)
                  console.debug("AHT: message contains HTML body part");
                return true;
              }
              // no 'Body: text/html', disable ahtButtons
              else {
                if(optionsDebug)
                  console.debug("AHT: no HTML body part");
                return false;
              }
            }
          }
  
        }
      };
    }

    onShutdown(isAppShutdown) {
      if (isAppShutdown) {
        return; // the application gets unloaded anyway
      }

      ExtensionSupport.unregisterWindowListener(
        tracker.windowListenerId,
      );

      for(let window of tracker.trackedWindows) {
        // console.debug("AHT - onShutdown");
        let element = window.document.getElementById("remoteContentOptionAllowForMsg");
        element.removeEventListener("click", tracker.notifyOnClickedListener);

        let oncommand_original = "LoadMsgWithRemoteContent();";
        element.setAttribute("oncommand", oncommand_original);
      }

      // Flush all caches
      Services.obs.notifyObservers(null, "startupcache-invalidate");
    }
  }

  exports.allowHtmlTemp = allowHtmlTemp;
})(this);