/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var { classes: Cc, interfaces: Ci, utils: Cu } = Components;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                  "resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");

var WebcompatReporter = {
  menuItem: null,
  menuItemEnabled: null,
  init: function() {
    Services.obs.addObserver(this, "DesktopMode:Change", false);
    Services.obs.addObserver(this, "chrome-document-global-created", false);
    Services.obs.addObserver(this, "content-document-global-created", false);

    let visible = true;
    if ("@mozilla.org/parental-controls-service;1" in Cc) {
      let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService);
      visible = !pc.parentalControlsEnabled;
    }

    this.addMenuItem(visible);
  },

  observe: function(subject, topic, data) {
    if (topic == "content-document-global-created" || topic == "chrome-document-global-created") {
      let win = subject;
      let currentURI = win.document.documentURI;

      // Ignore non top-level documents
      if (currentURI !== win.top.location.href) {
        return;
      }

      if (!this.menuItemEnabled && this.isReportableUrl(currentURI)) {
        NativeWindow.menu.update(this.menuItem, {enabled: true});
        this.menuItemEnabled = true;
      } else if (this.menuItemEnabled && !this.isReportableUrl(currentURI)) {
        NativeWindow.menu.update(this.menuItem, {enabled: false});
        this.menuItemEnabled = false;
      }
    } else if (topic === "DesktopMode:Change") {
      let args = JSON.parse(data);
      let tab = BrowserApp.getTabForId(args.tabId);
      let currentURI = tab.browser.currentURI.spec;
      if (args.desktopMode && this.isReportableUrl(currentURI)) {
        this.reportDesktopModePrompt(tab);
      }
    }
  },

  addMenuItem: function(visible) {
    this.menuItem = NativeWindow.menu.add({
      name: this.strings.GetStringFromName("webcompat.menu.name"),
      callback: () => {
        Promise.resolve(BrowserApp.selectedTab).then(this.getScreenshot)
                                               .then(this.reportIssue)
                                               .catch(Cu.reportError);
      },
      enabled: false,
      visible: visible,
    });
  },

  getScreenshot: (tab) => {
    return new Promise((resolve) => {
      try {
        let win = tab.window;
        let dpr = win.devicePixelRatio;
        let canvas = win.document.createElement("canvas");
        let ctx = canvas.getContext("2d");
        // Grab the visible viewport coordinates
        let x = win.document.documentElement.scrollLeft;
        let y = win.document.documentElement.scrollTop;
        let w = win.innerWidth;
        let h = win.innerHeight;
        // Scale according to devicePixelRatio and coordinates
        canvas.width = dpr * w;
        canvas.height = dpr * h;
        ctx.scale(dpr, dpr);
        ctx.drawWindow(win, x, y, w, h, '#ffffff');
        let screenshot = canvas.toDataURL();
        resolve({tab: tab, data: screenshot});
      } catch (e) {
        // drawWindow can fail depending on memory or surface size. Rather than reject here,
        // we resolve the URL so the user can continue to file an issue without a screenshot.
        Cu.reportError("WebCompatReporter: getting a screenshot failed: " + e);
        resolve({tab: tab});
      }
    });
  },

  isReportableUrl: function(url) {
    return url && !(url.startsWith("about") ||
                    url.startsWith("chrome") ||
                    url.startsWith("file") ||
                    url.startsWith("resource"));
  },

  reportDesktopModePrompt: function(tab) {
    let message = this.strings.GetStringFromName("webcompat.reportDesktopMode.message");
    let options = {
      action: {
        label: this.strings.GetStringFromName("webcompat.reportDesktopModeYes.label"),
        callback: () => this.reportIssue({tab: tab})
      }
    };
    Snackbars.show(message, Snackbars.LENGTH_LONG, options);
  },

  reportIssue: (tabData) => {
    return new Promise((resolve) => {
      const WEBCOMPAT_ORIGIN = "https://webcompat.com";
      let url = tabData.tab.browser.currentURI.spec
      let webcompatURL = `${WEBCOMPAT_ORIGIN}/issues/new?url=${url}`;

      if (tabData.data && typeof tabData.data === "string") {
        BrowserApp.deck.addEventListener("DOMContentLoaded", function sendDataToTab(event) {
          BrowserApp.deck.removeEventListener("DOMContentLoaded", sendDataToTab, false);

          if (event.target.defaultView.location.origin === WEBCOMPAT_ORIGIN) {
            // Waive Xray vision so event.origin is not chrome://browser on the other side.
            let win = Cu.waiveXrays(event.target.defaultView);
            win.postMessage(tabData.data, WEBCOMPAT_ORIGIN);
          }
        }, false);
      }

      let isPrivateTab = PrivateBrowsingUtils.isBrowserPrivate(tabData.tab.browser);
      BrowserApp.addTab(webcompatURL, {parentId: tabData.tab.id, isPrivate: isPrivateTab});
      resolve();
    });
  }
};

XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() {
  return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties");
});
