// 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/.
/* eslint-env node */

/*
Ensure the `--firefox.preference=network.http.http3.enable:true` is
set for this test.
*/

async function getNumImagesLoaded(elementSelector, commands) {
  return commands.js.run(`
    let sum = 0;
    document.querySelectorAll(${elementSelector}).forEach(e => {
      sum += e.complete & e.naturalHeight != 0;
    });
    return sum;
  `);
}

async function waitForImgLoadEnd(
  prevCount,
  maxStableCount,
  iterationDelay,
  timeout,
  commands,
  context,
  counter,
  elementSelector
) {
  let starttime = await commands.js.run(`return performance.now();`);
  let endtime = await commands.js.run(`return performance.now();`);
  let changing = true;
  let newCount = -1;
  let stableCount = 0;

  while (
    ((await commands.js.run(`return performance.now();`)) - starttime <
      timeout) &
    changing
  ) {
    // Wait a bit before making another round
    await commands.wait.byTime(iterationDelay);
    newCount = await counter(elementSelector, commands);
    context.log.debug(`${newCount}, ${prevCount}, ${stableCount}`);

    // Check if we are approaching stability
    if (newCount == prevCount) {
      // Gather the end time now
      if (stableCount == 0) {
        endtime = await commands.js.run(`return performance.now();`);
      }
      stableCount++;
    } else {
      prevCount = newCount;
      stableCount = 0;
    }

    if (stableCount >= maxStableCount) {
      // Stability achieved
      changing = false;
    }
  }

  return {
    start: starttime,
    end: endtime,
    numResources: newCount,
  };
}

async function test(context, commands) {
  let rootUrl = "https://www.google.com/search?q=kittens&tbm=isch";
  let waitTime = 1000;
  let numScrolls = 10;

  const average = arr => arr.reduce((p, c) => p + c, 0) / arr.length;

  if (typeof context.options.browsertime !== "undefined") {
    if (typeof context.options.browsertime.waitTime !== "undefined") {
      waitTime = context.options.browsertime.waitTime;
    }
    if (typeof context.options.browsertime.numScrolls !== "undefined") {
      numScrolls = context.options.browsertime.numScrolls;
    }
  }

  // Make firefox learn of HTTP/3 server
  await commands.navigate(rootUrl);

  let cycles = 1;
  for (let cycle = 0; cycle < cycles; cycle++) {
    // Measure the pageload
    await commands.measure.start("pageload");
    await commands.navigate(rootUrl);
    await commands.measure.stop();

    async function getHeight() {
      return commands.js.run(`return document.body.scrollHeight;`);
    }

    let vals = [];
    let badIterations = 0;
    let prevHeight = 0;
    for (let iteration = 0; iteration < numScrolls; iteration++) {
      // Get current image count
      let currCount = await getNumImagesLoaded(`".isv-r img"`, commands);
      prevHeight = await getHeight();

      // Scroll to a ridiculously high value for "infinite" down-scrolling
      commands.js.runAndWait(`
        window.scrollTo({ top: 100000000 })
      `);

      /*
      The maxStableCount of 22 was chosen as a trade-off between fast iterations
      and minimizing the number of bad iterations.
      */
      let results = await waitForImgLoadEnd(
        currCount,
        22,
        100,
        120000,
        commands,
        context,
        getNumImagesLoaded,
        `".isv-r img"`
      );

      // Gather metrics
      let ndiff = results.numResources - currCount;
      let tdiff = (results.end - results.start) / 1000;

      // Check if we had a bad iteration
      if (ndiff == 0) {
        // Check if the end of the search results was reached
        if (prevHeight == (await getHeight())) {
          context.log.info("Reached end of page.");
          break;
        }
        context.log.info("Bad iteration, redoing...");
        iteration--;
        badIterations++;
        if (badIterations == 5) {
          throw new Error("Too many bad scroll iterations occurred");
        }
        continue;
      }

      context.log.info(`${ndiff}, ${tdiff}`);
      vals.push(ndiff / tdiff);

      // Wait X seconds before scrolling again
      await commands.wait.byTime(waitTime);
    }

    if (!vals.length) {
      throw new Error("No requestsPerSecond values were obtained");
    }

    commands.measure.result[0].browserScripts.pageinfo.imagesPerSecond =
      average(vals);

    // Test clicking and and opening an image
    await commands.wait.byTime(waitTime);
    commands.click.byJs(`
      const links = document.querySelectorAll(".islib"); links[links.length-1]
    `);
    let results = await waitForImgLoadEnd(
      0,
      22,
      50,
      120000,
      commands,
      context,
      getNumImagesLoaded,
      `"#islsp img"`
    );
    commands.measure.result[0].browserScripts.pageinfo.imageLoadTime =
      results.end - results.start;
  }
}

module.exports = {
  test,
  owner: "Network Team",
  component: "netwerk",
  name: "g-image",
  description: "Measures the number of images per second after a scroll.",
};
