/* 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/. */

"use strict";

const TOOLBARID = "test-toolbar-added-during-customize-mode";

// The ID of a button that is not placed (ie, is in the palette) by default
const kNonPlacedWidgetId = "open-file-button";

add_task(async function () {
  await startCustomizing();
  let toolbar = createToolbarWithPlacements(TOOLBARID, []);
  CustomizableUI.addWidgetToArea(kNonPlacedWidgetId, TOOLBARID);
  let button = document.getElementById(kNonPlacedWidgetId);
  ok(button, "Button should exist.");
  is(
    button.parentNode.localName,
    "toolbarpaletteitem",
    "Button's parent node should be a wrapper."
  );

  simulateItemDrag(button, gNavToolbox.palette);
  ok(
    !CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved to the palette"
  );
  ok(
    gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is in palette."
  );

  button.scrollIntoView();
  simulateItemDrag(button, toolbar);
  ok(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved out of palette"
  );
  is(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area,
    TOOLBARID,
    "Button's back on toolbar"
  );
  ok(
    toolbar.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is on toolbar."
  );

  await endCustomizing();
  isnot(
    button.parentNode.localName,
    "toolbarpaletteitem",
    "Button's parent node should not be a wrapper outside customize mode."
  );
  await startCustomizing();

  is(
    button.parentNode.localName,
    "toolbarpaletteitem",
    "Button's parent node should be a wrapper back in customize mode."
  );

  simulateItemDrag(button, gNavToolbox.palette);
  ok(
    !CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved to the palette"
  );
  ok(
    gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is in palette."
  );

  ok(
    !CustomizableUI.inDefaultState,
    "Not in default state while toolbar is not collapsed yet."
  );
  setToolbarVisibility(toolbar, false);
  ok(
    CustomizableUI.inDefaultState,
    "In default state while toolbar is collapsed."
  );

  setToolbarVisibility(toolbar, true);

  info(
    "Check that removing the area registration from within customize mode works"
  );
  CustomizableUI.unregisterArea(TOOLBARID);
  ok(
    CustomizableUI.inDefaultState,
    "Now that the toolbar is no longer registered, should be in default state."
  );
  ok(
    !gCustomizeMode.areas.has(toolbar),
    "Toolbar shouldn't be known to customize mode."
  );

  CustomizableUI.registerArea(TOOLBARID, { defaultPlacements: [] });
  CustomizableUI.registerToolbarNode(toolbar, []);
  ok(
    !CustomizableUI.inDefaultState,
    "Now that the toolbar is registered again, should no longer be in default state."
  );
  ok(
    gCustomizeMode.areas.has(toolbar),
    "Toolbar should be known to customize mode again."
  );

  button.scrollIntoView();
  simulateItemDrag(button, toolbar);
  ok(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved out of palette"
  );
  is(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area,
    TOOLBARID,
    "Button's back on toolbar"
  );
  ok(
    toolbar.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is on toolbar."
  );

  let otherWin = await openAndLoadWindow({}, true);
  let otherTB = otherWin.document.createXULElement("toolbar");
  otherTB.id = TOOLBARID;
  otherTB.setAttribute("customizable", "true");
  let wasInformedCorrectlyOfAreaAppearing = false;
  let listener = {
    onAreaNodeRegistered(aArea, aNode) {
      if (aNode == otherTB) {
        wasInformedCorrectlyOfAreaAppearing = true;
      }
    },
  };
  CustomizableUI.addListener(listener);
  otherWin.gNavToolbox.appendChild(otherTB);
  CustomizableUI.registerToolbarNode(otherTB);
  ok(
    wasInformedCorrectlyOfAreaAppearing,
    "Should have been told area was registered."
  );
  CustomizableUI.removeListener(listener);

  ok(
    otherTB.querySelector(`#${kNonPlacedWidgetId}`),
    "Button is on other toolbar, too."
  );

  simulateItemDrag(button, gNavToolbox.palette);
  ok(
    !CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved to the palette"
  );
  ok(
    gNavToolbox.palette.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is in palette."
  );
  ok(
    !otherTB.querySelector(`#${kNonPlacedWidgetId}`),
    "Button is in palette in other window, too."
  );

  button.scrollIntoView();
  simulateItemDrag(button, toolbar);
  ok(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId),
    "Button moved out of palette"
  );
  is(
    CustomizableUI.getPlacementOfWidget(kNonPlacedWidgetId).area,
    TOOLBARID,
    "Button's back on toolbar"
  );
  ok(
    toolbar.querySelector(`#${kNonPlacedWidgetId}`),
    "Button really is on toolbar."
  );
  ok(
    otherTB.querySelector(`#${kNonPlacedWidgetId}`),
    "Button is on other toolbar, too."
  );

  let wasInformedCorrectlyOfAreaDisappearing = false;
  // XXXgijs So we could be using promiseWindowClosed here. However, after
  // repeated random oranges, I'm instead relying on onWindowClosed below to
  // fire appropriately - it is linked to an unload event as well, and so
  // reusing it prevents a potential race between unload handlers where the
  // one from promiseWindowClosed could fire before the onWindowClosed
  // (and therefore onAreaNodeRegistered) one, causing the test to fail.
  let windowClosed = await new Promise(resolve => {
    listener = {
      onAreaNodeUnregistered(aArea, aNode, aReason) {
        if (aArea == TOOLBARID) {
          is(aNode, otherTB, "Should be informed about other toolbar");
          is(
            aReason,
            CustomizableUI.REASON_WINDOW_CLOSED,
            "Reason should be correct."
          );
          wasInformedCorrectlyOfAreaDisappearing =
            aReason === CustomizableUI.REASON_WINDOW_CLOSED;
        }
      },
      onWindowClosed(aWindow) {
        if (aWindow == otherWin) {
          resolve(aWindow);
        } else {
          info("Other window was closed!");
          info(
            "Other window title: " +
              (aWindow.document && aWindow.document.title)
          );
          info(
            "Our window title: " +
              (otherWin.document && otherWin.document.title)
          );
        }
      },
    };
    CustomizableUI.addListener(listener);
    otherWin.close();
  });

  is(
    windowClosed,
    otherWin,
    "Window should have sent onWindowClosed notification."
  );
  ok(
    wasInformedCorrectlyOfAreaDisappearing,
    "Should be told about window closing."
  );
  // Closing the other window should not be counted against this window's customize mode:
  is(
    button.parentNode.localName,
    "toolbarpaletteitem",
    "Button's parent node should still be a wrapper."
  );
  ok(
    gCustomizeMode.areas.has(toolbar),
    "Toolbar should still be a customizable area for this customize mode instance."
  );

  await gCustomizeMode.reset();

  await endCustomizing();

  CustomizableUI.removeListener(listener);
  wasInformedCorrectlyOfAreaDisappearing = false;
  listener = {
    onAreaNodeUnregistered(aArea, aNode, aReason) {
      if (aArea == TOOLBARID) {
        is(aNode, toolbar, "Should be informed about this window's toolbar");
        is(
          aReason,
          CustomizableUI.REASON_AREA_UNREGISTERED,
          "Reason for final removal should be correct."
        );
        wasInformedCorrectlyOfAreaDisappearing =
          aReason === CustomizableUI.REASON_AREA_UNREGISTERED;
      }
    },
  };
  CustomizableUI.addListener(listener);
  removeCustomToolbars();
  ok(
    wasInformedCorrectlyOfAreaDisappearing,
    "Should be told about area being unregistered."
  );
  CustomizableUI.removeListener(listener);
  ok(
    CustomizableUI.inDefaultState,
    "Should be fine after exiting customize mode."
  );
});
