/**
 * This is similar to the hitTest function in apz_test_utils.js, in that it
 * does a hit-test for a point and returns the result. The difference is that
 * in the fission world, the hit-test may land on an OOPIF, which means the
 * result information will be in the APZ test data for the OOPIF process. This
 * function checks both the current process and OOPIF process to see which one
 * got a hit result, and returns the result regardless of which process got it.
 * The caller is expected to check the layers id which will allow distinguishing
 * the two cases.
 */
async function hitTestOOPIF(point, iframeElement) {
  let getIframeCompositorTestData = async iframe => {
    let data = await SpecialPowers.spawn(iframe, [], () => {
      let utils = SpecialPowers.getDOMWindowUtils(content.window);
      return JSON.stringify(utils.getCompositorAPZTestData());
    });

    return JSON.parse(data);
  };

  let utils = SpecialPowers.getDOMWindowUtils(window);

  // Get the test data before doing the actual hit-test, to get a baseline
  // of what we can ignore.
  let oldParentTestData = utils.getCompositorAPZTestData();
  let oldIframeTestData = await getIframeCompositorTestData(iframeElement);

  let hittestPromise = SpecialPowers.spawnChrome([], () => {
    return new Promise(resolve => {
      browsingContext.topChromeWindow.addEventListener(
        "MozMouseHittest",
        () => {
          resolve();
        },
        { once: true }
      );
    });
  });
  await SpecialPowers.executeAfterFlushingMessageQueue();

  // Now do the hit-test
  dump(`Hit-testing point (${point.x}, ${point.y}) in fission context\n`);
  SpecialPowers.wrap(window).synthesizeMouseEvent(
    "MozMouseHittest",
    point.x,
    point.y,
    {},
    { ignoreRootScrollFrame: true }
  );

  await hittestPromise;

  // Collect the new test data
  let newParentTestData = utils.getCompositorAPZTestData();
  let newIframeTestData = await getIframeCompositorTestData(iframeElement);

  // See which test data has new hit results
  let hitResultCount = testData => {
    return Object.keys(testData.hitResults).length;
  };

  let hitIframe =
    hitResultCount(newIframeTestData) > hitResultCount(oldIframeTestData);
  let hitParent =
    hitResultCount(newParentTestData) > hitResultCount(oldParentTestData);

  // Extract the results from the appropriate test data
  let lastHitResult = testData => {
    let lastHit =
      testData.hitResults[Object.keys(testData.hitResults).length - 1];
    return {
      hitInfo: lastHit.hitResult,
      scrollId: lastHit.scrollId,
      layersId: lastHit.layersId,
    };
  };
  if (hitIframe && hitParent) {
    throw new Error(
      "Both iframe and parent got hit-results, that is unexpected!"
    );
  } else if (hitIframe) {
    return lastHitResult(newIframeTestData);
  } else if (hitParent) {
    return lastHitResult(newParentTestData);
  } else {
    throw new Error(
      "Neither iframe nor parent got the hit-result, that is unexpected!"
    );
  }
}
