/**
 * Rocketship UI JS.
 *
 * Contains: triggers for functions.
 * Functions themselves are split off and grouped below each behavior.
 */
(function(Drupal, once, window, document) {
  "use strict";

  // Set namespace for UI javascript.
  if (typeof window.rocketshipUI === 'undefined') {
    window.rocketshipUI = {};
  }

  const self = window.rocketshipUI;

  ///////////////////////////////////////////////////////////////////////
  // Cache variables available across the namespace
  ///////////////////////////////////////////////////////////////////////

  // Track if scrollHandling has already been fired to prevent multiples.
  self.scrollHandlingVar = false;
  // Track if there's a fixed header.
  self.hasFixedHeader = false;

  ///////////////////////////////////////////////////////////////////////
  // Behavior: triggers
  ///////////////////////////////////////////////////////////////////////

  Drupal.behaviors.rocketshipUIStickyHeader = {
    attach: function (context, settings) {
      // Cache variables.
      const stickyHeader = context.querySelector('.wrapper--page-top');
      const headerTop = context.querySelector('.header-top');

      // Sticky header js is only loaded if theme setting is checked.
      // Appearance Settings in /admin/appearance/settings/my_theme_name.
      if (stickyHeader) {
        self.fixedTop(stickyHeader, headerTop);
      }
    }
  };

  ///////////////////////////////////////////////////////////////////////
  // Functions
  ///////////////////////////////////////////////////////////////////////

  /**
   * FixedTop (with custom JS).
   *
   * Set a fixed element while scrolling e.g. a fixed header.
   */
  self.fixedTop = (pin, offsetElement)=> {
    self.hasFixedHeader = true;

    // Keep track of scrolling (needed for fixed stuff).
    self.scrollHandling(pin, offsetElement);

    if (typeof offsetElement === 'undefined') {
      offsetElement = false;
    }

    // On window resize, check again for the right position.
    // And recalculate the height.
    window.addEventListener('resize',() => {
      self.handleStickyHeader(pin, offsetElement);
      self.resizeSpacer(pin);
      let handler = setTimeout(() => {
        clearTimeout(handler);
        self.resizeSpacer(pin);
      }, 300);
    });

    // Same on scroll.
    window.addEventListener('scroll', () => {
      self.handleStickyHeader(pin, offsetElement);
      self.resizeSpacer(pin);
      let handler = setTimeout(() =>{
        clearTimeout(handler);
        self.resizeSpacer(pin);
      }, 300);
    });
  };

  /**
   * ResizeSpacer.
   *
   * Adjusts the height of a spacer element to match the height of a pinned element.
   */
  self.resizeSpacer = (pin) => {
    const spacer = document.querySelector('.sticky-spacer');
    const pinHeight = pin.offsetHeight;
    spacer.style.height = pinHeight + 'px';
  };

  /**
   * HandleStickyHeader.
   *
   * Manages the behavior of a sticky header, including positioning and class toggling.
   */
  self.handleStickyHeader = (pin, offsetElement) => {
    // If scrolling below the height of the top navigation,
    // make pin fixed.

    const adminToolbar = document.querySelector('#toolbar-bar');
    const adminToolbarTray = document.querySelector('#toolbar-item-administration-tray');
    let adminToolbarHeight = 0;
    let adminToolbarTrayHeight = 0;
    let offsetHeader;
    let offset = 0;

    // Check for elements before the fixed header,
    // to push fixed header down.
    if (adminToolbar) {
      adminToolbarHeight = adminToolbar.offsetHeight;
    }

    if (adminToolbarTray && adminToolbarTray.classList.contains('toolbar-tray-horizontal')) {
      adminToolbarTrayHeight = adminToolbarTray.offsetHeight;
    }

    // Offset by space from admin toolbar
    // and also the tray, but only if it's a horizontal one.
    offsetHeader = adminToolbarHeight + adminToolbarTrayHeight;

    // Save open and collapsed header heights.
    if (offsetElement) {
      offset = offsetElement.offsetHeight;
    }

    // Move header down with space from admin and stuff.
    pin.style.top = offsetHeader + 'px';
    pin.classList.add('is-fixed');

    const topHeight = offsetElement?.offsetHeight ?? 0;

    // Check if we're at top of the page or not.
    if (self.getScrollTop() > offset + topHeight) {
      pin.classList.add('not-top');
    } else {
      pin.classList.remove('not-top');
    }
  };

  /**
   * ScrollHandling.
   *
   * Adds various classes to track scrolling.
   */
  self.scrollHandling = (pin, offsetElement) => {
    // Make sure this function hasn't already been fired.
    if (self.scrollHandlingVar === false) {
      self.scrollHandlingVar = true;

      // At page load, only set scrolling stuff if scrolled partly down the page.
      if (self.getScrollTop() > 28) {
        // Force window resize.
        window.dispatchEvent(new Event('resize'));
        // Set fixed stuff, after giving admin toolbar time to recalc its height.
        let timoutResize = setTimeout(() => {
          clearTimeout(timoutResize);
          self.handleStickyHeader(pin, offsetElement);
          document.body.classList.add('js-scrolling');
          document.body.classList.add('js-scrolling-up');
        }, 50);
      }

      window.addEventListener('scroll', () => {
        self.scrollDirection(pin, offsetElement);
      });
    }
  };

  /**
   * ScrollDirection.
   *
   * Determines the direction of scrolling and applies corresponding classes.
   */
  self.scrollDirection = (pin, offsetElement) => {
    let st = self.getScrollTop();

    // We are scrolling down from our previous position.
    if (st > self.lastScrollTop) {
      // We are scrolling past the page top.
      if (st > 0) {
        document.body.classList.add('js-scrolling');
        document.body.classList.remove('js-scrolling-up');
        document.body.classList.add('js-scrolling-down');
        // Pull up header top so it is hidden.
        const marginTop = offsetElement?.offsetHeight ?? 0;
        pin.style.marginTop = `-${marginTop}px`;
      }
    } else if (st < self.lastScrollTop) {
      // We are scrolling up.

      // We are scrolling past the page top.
      if (st > 0) {
        document.body.classList.add('js-scrolling');
        document.body.classList.remove('js-scrolling-down');
        document.body.classList.add('js-scrolling-up');
        // Reset header top so it is visible.
        pin.style.marginTop = 0;
      } else {
        // We're at the top, reset the body classes.
        document.body.classList.remove('js-scrolling');
        document.body.classList.remove('js-scrolling-up');
        document.body.classList.remove('js-scrolling-down');
      }
    }

    self.lastScrollTop = st;
  };

  /**
   * GetScrollTop.
   *
   * Returns the current vertical scroll position of the window.
   */
  self.getScrollTop = () => {
    return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
  };

})(Drupal, once, window, document);
