"use strict";

const { Subprocess } = ChromeUtils.importESModule(
  "resource://gre/modules/Subprocess.sys.mjs"
);

const TEMP_DIR = Services.dirsvc.get("TmpD", Ci.nsIFile).path;

const screenshotPath = PathUtils.join(TEMP_DIR, "headless_test_screenshot.png");

async function runFirefox(args) {
  const XRE_EXECUTABLE_FILE = "XREExeF";
  const firefoxExe = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).path;
  const NS_APP_PREFS_50_FILE = "PrefF";
  const mochiPrefsFile = Services.dirsvc.get(NS_APP_PREFS_50_FILE, Ci.nsIFile);
  const mochiPrefsPath = mochiPrefsFile.path;
  const mochiPrefsName = mochiPrefsFile.leafName;
  const profilePath = PathUtils.join(
    TEMP_DIR,
    "headless_test_screenshot_profile"
  );
  const prefsPath = PathUtils.join(profilePath, mochiPrefsName);
  const firefoxArgs = ["-profile", profilePath, "-no-remote"];

  await IOUtils.makeDirectory(profilePath);
  await IOUtils.copy(mochiPrefsPath, prefsPath);
  let proc = await Subprocess.call({
    command: firefoxExe,
    arguments: firefoxArgs.concat(args),
    // Disable leak detection to avoid intermittent failure bug 1331152.
    environmentAppend: true,
    environment: {
      ASAN_OPTIONS:
        "detect_leaks=0:quarantine_size=50331648:malloc_context_size=5",
      // Don't enable Marionette.
      MOZ_MARIONETTE: null,
    },
  });
  let stdout;
  while ((stdout = await proc.stdout.readString())) {
    dump(`>>> ${stdout}\n`);
  }
  let { exitCode } = await proc.wait();
  is(exitCode, 0, "Firefox process should exit with code 0");
  await IOUtils.remove(profilePath, { recursive: true });
}

async function testFileCreationPositive(args, path) {
  await runFirefox(args);

  let saved = IOUtils.exists(path);
  ok(saved, "A screenshot should be saved as " + path);
  if (!saved) {
    return;
  }

  let info = await IOUtils.stat(path);
  ok(info.size > 0, "Screenshot should not be an empty file");
  await IOUtils.remove(path);
}

async function testFileCreationNegative(args, path) {
  await runFirefox(args);

  let saved = await IOUtils.exists(path);
  ok(!saved, "A screenshot should not be saved");
  await IOUtils.remove(path);
}

async function testWindowSizePositive(width, height) {
  let size = String(width);
  if (height) {
    size += "," + height;
  }

  await runFirefox([
    "-url",
    "http://mochi.test:8888/browser/browser/components/shell/test/headless.html",
    "-screenshot",
    screenshotPath,
    "-window-size",
    size,
  ]);

  let saved = await IOUtils.exists(screenshotPath);
  ok(saved, "A screenshot should be saved in the tmp directory");
  if (!saved) {
    return;
  }

  let data = await IOUtils.read(screenshotPath);
  await new Promise((resolve, reject) => {
    let blob = new Blob([data], { type: "image/png" });
    let reader = new FileReader();
    reader.onloadend = function () {
      let screenshot = new Image();
      screenshot.onload = function () {
        is(
          screenshot.width,
          width,
          "Screenshot should be " + width + " pixels wide"
        );
        if (height) {
          is(
            screenshot.height,
            height,
            "Screenshot should be " + height + " pixels tall"
          );
        }
        resolve();
      };
      screenshot.src = reader.result;
    };
    reader.readAsDataURL(blob);
  });
  await IOUtils.remove(screenshotPath);
}

async function testGreen(url, path) {
  await runFirefox(["-url", url, `--screenshot=${path}`]);

  let saved = await IOUtils.exists(path);
  ok(saved, "A screenshot should be saved in the tmp directory");
  if (!saved) {
    return;
  }

  let data = await IOUtils.read(path);
  let image = await new Promise((resolve, reject) => {
    let blob = new Blob([data], { type: "image/png" });
    let reader = new FileReader();
    reader.onloadend = function () {
      let screenshot = new Image();
      screenshot.onload = function () {
        resolve(screenshot);
      };
      screenshot.src = reader.result;
    };
    reader.readAsDataURL(blob);
  });
  let canvas = document.createElement("canvas");
  canvas.width = image.naturalWidth;
  canvas.height = image.naturalHeight;
  let ctx = canvas.getContext("2d");
  ctx.drawImage(image, 0, 0);
  let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  let rgba = imageData.data;

  let found = false;
  for (let i = 0; i < rgba.length; i += 4) {
    if (rgba[i] === 0 && rgba[i + 1] === 255 && rgba[i + 2] === 0) {
      found = true;
      break;
    }
  }
  ok(found, "There should be a green pixel in the screenshot.");

  await IOUtils.remove(path);
}
