/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

/**
 * This function tests that the browser saves the last location of size of
 * the PiP window and will open the next PiP window in the same location
 * with the size. It adjusts for aspect ratio by keeping the same height and
 * adjusting the width of the PiP window.
 */

add_task(async () => {
  await BrowserTestUtils.withNewTab(
    {
      url: TEST_PAGE,
      gBrowser,
    },
    // Function to switch video source.
    async browser => {
      async function switchVideoSource(src) {
        let videoResized = BrowserTestUtils.waitForEvent(pipWin, "resize");
        await ContentTask.spawn(browser, { src }, async ({ src }) => {
          let doc = content.document;
          let video = doc.getElementById("with-controls");
          video.src = src;
        });
        await videoResized;
      }
      // This function is used because the rounding of the width can be off
      // by about 1 pixel sometimes so this checks that val1 and val2 are
      // within 1 pixel
      function checkIfEqual(val1, val2, str) {
        let equal = Math.abs(val1 - val2);
        if (equal <= 1) {
          is(equal <= 1, true, str);
        } else {
          is(val1, val2, str);
        }
      }

      // Used for clearing the size and location of the PiP window
      const PLAYER_URI =
        "chrome://global/content/pictureinpicture/player.xhtml";

      // The PiP window now stores information between tests and needs to be
      // cleared before the test begins
      function clearSaved() {
        let xulStore = Services.xulStore;
        xulStore.setValue(PLAYER_URI, "picture-in-picture", "left", NaN);
        xulStore.setValue(PLAYER_URI, "picture-in-picture", "top", NaN);
        xulStore.setValue(PLAYER_URI, "picture-in-picture", "width", NaN);
        xulStore.setValue(PLAYER_URI, "picture-in-picture", "height", NaN);
      }

      function getAvailScreenSize(screen) {
        let screenLeft = {},
          screenTop = {},
          screenWidth = {},
          screenHeight = {};
        screen.GetAvailRectDisplayPix(
          screenLeft,
          screenTop,
          screenWidth,
          screenHeight
        );
        let fullLeft = {},
          fullTop = {},
          fullWidth = {},
          fullHeight = {};
        screen.GetRectDisplayPix(fullLeft, fullTop, fullWidth, fullHeight);

        // We have to divide these dimensions by the CSS scale factor for the
        // display in order for the video to be positioned correctly on displays
        // that are not at a 1.0 scaling.
        let scaleFactor =
          screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
        screenWidth.value *= scaleFactor;
        screenHeight.value *= scaleFactor;
        screenLeft.value =
          (screenLeft.value - fullLeft.value) * scaleFactor + fullLeft.value;
        screenTop.value =
          (screenTop.value - fullTop.value) * scaleFactor + fullTop.value;

        return [
          screenLeft.value,
          screenTop.value,
          screenWidth.value,
          screenHeight.value,
        ];
      }

      let screen = Cc["@mozilla.org/gfx/screenmanager;1"]
        .getService(Ci.nsIScreenManager)
        .screenForRect(1, 1, 1, 1);

      let [
        defaultX,
        defaultY,
        defaultWidth,
        defaultHeight,
      ] = getAvailScreenSize(screen);

      // Default size of PiP window
      let rightEdge = defaultX + defaultWidth;
      let bottomEdge = defaultY + defaultHeight;

      // tab height
      // Used only for Linux as the PiP window has a tab
      let tabHeight = 35;

      // clear already saved information
      clearSaved();

      // Open PiP
      let pipWin = await triggerPictureInPicture(browser, "with-controls");
      ok(pipWin, "Got Picture-in-Picture window.");

      let defaultPiPWidth = pipWin.innerWidth;
      let defaultPiPHeight = pipWin.innerHeight;

      // Check that it is opened at default location
      checkIfEqual(
        pipWin.screenX,
        rightEdge - defaultPiPWidth,
        "Default PiP X location"
      );
      if (AppConstants.platform == "linux") {
        checkIfEqual(
          pipWin.screenY,
          bottomEdge - defaultPiPHeight - tabHeight,
          "Default PiP Y location"
        );
      } else {
        checkIfEqual(
          pipWin.screenY,
          bottomEdge - defaultPiPHeight,
          "Default PiP Y location"
        );
      }
      checkIfEqual(pipWin.innerHeight, defaultPiPHeight, "Default PiP height");
      checkIfEqual(pipWin.innerWidth, defaultPiPWidth, "Default PiP width");

      let top = defaultY;
      let left = defaultX;
      pipWin.moveTo(left, top);
      let height = pipWin.innerHeight / 2;
      let width = pipWin.innerWidth / 2;
      pipWin.resizeTo(width, height);

      // CLose first PiP window and open another
      await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
      pipWin = await triggerPictureInPicture(browser, "with-controls");
      ok(pipWin, "Got Picture-in-Picture window.");

      // PiP is opened at 0, 0 with size 1/4 default width and 1/4 default height
      checkIfEqual(pipWin.screenX, left, "Opened at last X location");
      checkIfEqual(pipWin.screenY, top, "Opened at last Y location");
      checkIfEqual(
        pipWin.innerHeight,
        height,
        "Opened with 1/2 default height"
      );
      checkIfEqual(pipWin.innerWidth, width, "Opened with 1/2 default width");

      // Mac and Linux did not allow moving to coordinates offscreen so this
      // test is skipped on those platforms
      if (AppConstants.platform == "win") {
        // Move to -1111, -1111 and adjust size to 1/4 width and 1/4 height
        left = -11111;
        top = -11111;
        pipWin.moveTo(left, top);
        pipWin.resizeTo(pipWin.innerWidth / 4, pipWin.innerHeight / 4);

        await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
        pipWin = await triggerPictureInPicture(browser, "with-controls");
        ok(pipWin, "Got Picture-in-Picture window.");

        // because the coordinates are off screen, the default size and location will be used
        checkIfEqual(
          pipWin.screenX,
          rightEdge - defaultPiPWidth,
          "Opened at default X location"
        );
        checkIfEqual(
          pipWin.screenY,
          bottomEdge - defaultPiPHeight,
          "Opened at default Y location"
        );
        checkIfEqual(
          pipWin.innerWidth,
          defaultPiPWidth,
          "Opened at default PiP width"
        );
        checkIfEqual(
          pipWin.innerHeight,
          defaultPiPHeight,
          "Opened at default PiP height"
        );
      }

      // Linux doesn't handle switching the video source well and it will
      // cause the tests to failed in unexpected ways. Possibly caused by
      // bug 1594223 https://bugzilla.mozilla.org/show_bug.cgi?id=1594223
      if (AppConstants.platform != "linux") {
        // Save width and height for when aspect ratio is changed
        height = pipWin.innerHeight;
        width = pipWin.innerWidth;

        left = 200;
        top = 100;
        pipWin.moveTo(left, top);

        // Now switch the video so the video ratio is different
        await switchVideoSource("test-video-cropped.mp4");
        await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
        pipWin = await triggerPictureInPicture(browser, "with-controls");
        ok(pipWin, "Got Picture-in-Picture window.");

        checkIfEqual(pipWin.screenX, left, "Opened at last X location");
        checkIfEqual(pipWin.screenY, top, "Opened at last Y location");
        checkIfEqual(
          pipWin.innerHeight,
          height,
          "Opened height with previous width"
        );
        checkIfEqual(
          pipWin.innerWidth,
          height * (pipWin.innerWidth / pipWin.innerHeight),
          "Width is changed to adjust for aspect ration"
        );

        left = 300;
        top = 300;
        pipWin.moveTo(left, top);
        pipWin.resizeTo(defaultPiPWidth / 2, defaultPiPHeight / 2);

        // Save height for when aspect ratio is changed
        height = pipWin.innerHeight;

        // Now switch the video so the video ratio is different
        await switchVideoSource("test-video.mp4");
        await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
        pipWin = await triggerPictureInPicture(browser, "with-controls");
        ok(pipWin, "Got Picture-in-Picture window.");

        checkIfEqual(pipWin.screenX, left, "Opened at last X location");
        checkIfEqual(pipWin.screenY, top, "Opened at last Y location");
        checkIfEqual(pipWin.innerHeight, height, "Opened with previous height");
        checkIfEqual(
          pipWin.innerWidth,
          height * (pipWin.innerWidth / pipWin.innerHeight),
          "Width is changed to adjust for aspect ration"
        );
      }

      // Move so that part of PiP is off screen (bottom right)

      left = rightEdge - Math.round((3 * pipWin.innerWidth) / 4);
      top = bottomEdge - Math.round((3 * pipWin.innerHeight) / 4);

      let movePromise = BrowserTestUtils.waitForEvent(
        pipWin.windowRoot,
        "MozUpdateWindowPos"
      );
      pipWin.moveTo(left, top);
      await movePromise;

      await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
      pipWin = await triggerPictureInPicture(browser, "with-controls");
      ok(pipWin, "Got Picture-in-Picture window.");

      // Redefine top and left to where the PiP windop will open
      left = rightEdge - pipWin.innerWidth;
      top = bottomEdge - pipWin.innerHeight;

      // await new Promise(r => setTimeout(r, 5000));
      // PiP is opened bottom right but not off screen
      checkIfEqual(
        pipWin.screenX,
        left,
        "Opened at last X location but shifted back on screen"
      );
      if (AppConstants.platform == "linux") {
        checkIfEqual(
          pipWin.screenY,
          top - tabHeight,
          "Opened at last Y location but shifted back on screen"
        );
      } else {
        checkIfEqual(
          pipWin.screenY,
          top,
          "Opened at last Y location but shifted back on screen"
        );
      }

      // Move so that part of PiP is off screen (top left)
      left = defaultX - Math.round(pipWin.innerWidth / 4);
      top = defaultY - Math.round(pipWin.innerHeight / 4);

      movePromise = BrowserTestUtils.waitForEvent(
        pipWin.windowRoot,
        "MozUpdateWindowPos"
      );
      pipWin.moveTo(left, top);
      await movePromise;

      await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
      pipWin = await triggerPictureInPicture(browser, "with-controls");
      ok(pipWin, "Got Picture-in-Picture window.");

      // PiP is opened top left on screen
      checkIfEqual(
        pipWin.screenX,
        defaultX,
        "Opened at last X location but shifted back on screen"
      );
      checkIfEqual(
        pipWin.screenY,
        defaultY,
        "Opened at last Y location but shifted back on screen"
      );

      if (AppConstants.platform != "linux") {
        // test that if video is on right edge and new video with smaller width
        // is opened next, it is still on the right edge
        left = rightEdge - pipWin.innerWidth;
        top = Math.round(bottomEdge / 4);

        pipWin.moveTo(left, top);

        // Used to ensure that video width decreases for next PiP window
        width = pipWin.innerWidth;
        checkIfEqual(
          pipWin.innerWidth + pipWin.screenX,
          rightEdge,
          "Video is on right edge before video is changed"
        );

        // Now switch the video so the video width is smaller
        await switchVideoSource("test-video-cropped.mp4");
        await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
        pipWin = await triggerPictureInPicture(browser, "with-controls");
        ok(pipWin, "Got Picture-in-Picture window.");

        checkIfEqual(
          pipWin.innerWidth < width,
          true,
          "New video width is smaller"
        );
        checkIfEqual(
          pipWin.innerWidth + pipWin.screenX,
          rightEdge,
          "Video is on right edge after video is changed"
        );
      }

      await ensureMessageAndClosePiP(browser, "with-controls", pipWin, true);
    }
  );
});
