// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

(function () {
  let notes = document.querySelector("details");
  // Create an unattached DOM node for the code below.
  if (!notes) {
    notes = document.createElement("details");
  }
  let popIn = document.createElement("button");

  // Mark the speaker note window defunct. This means that it will no longer
  // show the notes.
  function markDefunct() {
    const main = document.querySelector("main");
    const h4 = document.createElement("h4");
    h4.append("(You can close this window now.)");
    main.replaceChildren(h4);
    window.location.hash = "#speaker-notes-defunct";
  }

  // Update the window. This shows/hides controls as necessary for regular and
  // speaker note pages.
  function applyState() {
    if (window.location.hash == "#speaker-notes-open") {
      if (getState() != "popup") {
        markDefunct();
      }
      return;
    }

    switch (getState()) {
      case "popup":
        popIn.classList.remove("hidden");
        notes.classList.add("hidden");
        break;
      case "inline-open":
        popIn.classList.add("hidden");
        notes.open = true;
        notes.classList.remove("hidden");
        break;
      case "inline-closed":
        popIn.classList.add("hidden");
        notes.open = false;
        notes.classList.remove("hidden");
        break;
    }
  }

  // Get the state of the speaker note window: "inline-open", "inline-closed",
  // or "popup".
  function getState() {
    return window.localStorage["speakerNotes"] || "inline-closed";
  }

  // Set the state of the speaker note window. Call applyState as needed
  // afterwards.
  function setState(state) {
    window.localStorage["speakerNotes"] = state;
  }

  // Create controls for a regular page.
  function setupRegularPage() {
    // Create pop-in button.
    popIn.setAttribute("id", "speaker-notes-toggle");
    popIn.setAttribute("type", "button");
    popIn.setAttribute("title", "Close speaker notes");
    popIn.setAttribute("aria-label", "Close speaker notes");
    popIn.classList.add("icon-button");
    let popInIcon = document.createElement("i");
    popInIcon.classList.add("fa", "fa-window-close-o");
    popIn.append(popInIcon);
    popIn.addEventListener("click", (event) => {
      setState("inline-open");
      applyState();
    });
    document.querySelector(".left-buttons").append(popIn);

    // Create speaker notes.
    notes.addEventListener("toggle", (event) => {
      setState(notes.open ? "inline-open" : "inline-closed");
    });

    let summary = document.createElement("summary");
    notes.insertBefore(summary, notes.firstChild);

    let h4 = document.createElement("h4");
    h4.setAttribute("id", "speaker-notes");
    h4.append("Speaker Notes");
    h4.addEventListener("click", (event) => {
      // Update fragment as if we had clicked a link. A regular a element would
      // result in double-fire of the event.
      window.location.hash = "#speaker-notes";
    });
    summary.append(h4);

    // Create pop-out button.
    let popOutLocation = new URL(window.location.href);
    popOutLocation.hash = "#speaker-notes-open";
    let popOut = document.createElement("button");
    popOut.classList.add("icon-button", "pop-out");
    popOut.addEventListener("click", (event) => {
      let popup = window.open(popOutLocation.href, "speakerNotes", "popup");
      if (popup) {
        setState("popup");
        applyState();
        // bind the popup to reset the speaker note state on close of the popup
        popup.onload = () => {
          popup.onbeforeunload = () => {
            setState("inline-open");
            applyState();
          };
        };
      } else {
        window.alert(
          "Could not open popup, please check your popup blocker settings."
        );
      }
    });
    let popOutIcon = document.createElement("i");
    popOutIcon.classList.add("fa", "fa-external-link");
    popOut.append(popOutIcon);
    summary.append(popOut);
  }

  // Create headers on the print page.
  function setupPrintPage() {
    for (const notes of document.querySelectorAll("details")) {
      notes.open = true;
      let summary = document.createElement("summary");
      notes.insertBefore(summary, notes.firstChild);
      let h4 = document.createElement("h4");
      h4.append("Speaker Notes");
      summary.append(h4);
    }
  }

  // Create controls for a speaker note window.
  function setupSpeakerNotes() {
    // Hide sidebar and buttons.
    document.querySelector("html").classList.remove("sidebar-visible");
    document.querySelector("html").classList.add("sidebar-hidden");
    document.querySelector(".left-buttons").classList.add("hidden");
    document.querySelector(".right-buttons").classList.add("hidden");

    // Hide content except for the speaker notes and h1 elements.
    const main = document.querySelector("main");
    let children = main.childNodes;
    let i = 0;
    while (i < children.length) {
      const node = children[i];
      switch (node.tagName) {
        case "DETAILS":
          // We found the speaker notes: extract their content.
          let div = document.createElement("div");
          div.replaceChildren(...node.childNodes);
          node.replaceWith(div);
          i += 1;
          break;
        case "H1":
          // We found a header: turn it into a smaller header for the speaker
          // note window.
          let h4 = document.createElement("h4");
          let pageLocation = new URL(window.location.href);
          pageLocation.hash = "";
          let a = document.createElement("a");
          a.setAttribute("href", pageLocation.href);
          a.append(node.innerText);
          h4.append("Speaker Notes for ", a);
          node.replaceWith(h4);
          i += 1;
          break;
        default:
          // We found something else: remove it.
          main.removeChild(node);
      }
    }

    // Update prev/next buttons to keep speaker note state.
    document
      .querySelectorAll('a[rel="prev"], a[rel="next"]')
      .forEach((elem) => {
        elem.href += "#speaker-notes-open";
      });
  }

  let timeout = null;
  // This will fire on _other_ open windows when we change window.localStorage.
  window.addEventListener("storage", (event) => {
    switch (event.key) {
      case "currentPage":
        if (getState() == "popup") {
          // We link all windows when we are showing speaker notes.
          window.location.pathname = event.newValue;
        }
        break;
      case "speakerNotes":
        // When navigating to another page, we see two state changes in rapid
        // succession:
        //
        // - "popup" -> "inline-open"
        // - "inline-open" -> "popup"
        //
        // When the page is closed, we only see:
        //
        // - "popup" -> "inline-open"
        //
        // We can use a timeout to detect the difference. The effect is that
        // showing the speaker notes is delayed by 500 ms when closing the
        // speaker notes window.
        if (timeout) {
          clearTimeout(timeout);
        }
        timeout = setTimeout(applyState, 500);
        break;
    }
  });
  window.localStorage["currentPage"] = window.location.pathname;

  // We encode the kind of page in the location hash:
  switch (window.location.hash) {
    case "#speaker-notes-open":
      // We are on a page in the speaker notes.
      setupSpeakerNotes();
      break;
    case "#speaker-notes-defunct":
      // We are on a page in a defunct speaker note window. We keep the state
      // unchanged and mark the window defunct.
      setupSpeakerNotes();
      markDefunct();
      break;
    default:
      if (window.location.pathname.endsWith("/print.html")) {
        setupPrintPage();
        return;
      }

      // We are on a regular page. We force the state to "inline-open" if this
      // looks like a direct link to the speaker notes.
      if (window.location.hash == "#speaker-notes") {
        setState("inline-open");
      }
      applyState();
      setupRegularPage();
  }
})();