import ElementHelpers from '../helpers/element-helpers';
import scrollToElement from '../helpers/scrollToElement';

/**
 * scroll.js
 * scroll script that adds a gradient and handles anchors
 */

/**
 * Declare constants
 */
const attributeBase = 'data-rs-scroll';

export class Scroll {
  constructor(element) {
    this.element = element;
    this.list = ElementHelpers.getElementsByAttribute(this.attributes.list);
    this.link = ElementHelpers.getElementsByAttribute(this.attributes.link);

    this.addEventHandlers();
  }

  /**
   * Declare attribute constants
   */
  get attributes() {
    return {
      list: attributeBase + '-list',
      link: attributeBase + '-link'
    };
  }

  /**
   * Add event handlers
   */
  addEventHandlers() {
    this.addGradientToScrollingList();
    this.addAndRemoveGradientOnScroll();
    this.handleScrollOnClick();
    this.addAndRemoveDividerOnScroll();
    this.addDividerToScrollingList();
  }

  /**
   * Adds a gradient when it is possible to scroll, by comparing offset height with scroll height
   */
  addGradientToScrollingList() {
    this.element.addEventListener('click', function () {
      const list = this.nextElementSibling;

      const offsetHeight = list.offsetHeight;
      const scrollHeight = list.scrollHeight;
      const scrollTop = list.scrollTop;

      const isScrollable = scrollHeight > offsetHeight;
      const isScrolledToBottom = scrollHeight - offsetHeight - scrollTop < 1;

      if (isScrollable && !isScrolledToBottom) {
        if (list.classList.contains('scroll-gradient')) {
          list.classList.remove('scroll-gradient');
        } else {
          list.classList.add('scroll-gradient');
        }
      } else {
        list.classList.remove('scroll-gradient');
      }
    });
  }

  /**
   * removes gradient when scrolled all the way to the bottom
   * adds gradient when scrolled to the top
   */
  addAndRemoveGradientOnScroll() {
    this.list.forEach((item) => {
      item.addEventListener('scroll', () => {
        const list = item;
        const scrollTop = list.scrollTop;
        const scrollHeight = list.scrollHeight;
        const offsetHeight = list.offsetHeight;

        const isScrollable = scrollHeight > offsetHeight;

        const isScrolledToBottom = scrollHeight - offsetHeight - scrollTop < 1;

        if (isScrolledToBottom && isScrollable) {
          list.classList.remove('scroll-gradient');
        } else {
          list.classList.add('scroll-gradient');
        }
      });
    });
  }

  /**
   * adds divider when scrolled more than 1px from top
   * removes the divider when scrolled to top
   */
  addAndRemoveDividerOnScroll() {
    this.list.forEach((item) => {
      const element = item.previousElementSibling;
      item.addEventListener('scroll', () => {
        const list = item;
        const scrollTop = list.scrollTop;
        const scrollHeight = list.scrollHeight;
        const offsetHeight = list.offsetHeight;

        const isScrollable = scrollHeight > offsetHeight;

        if (isScrollable && scrollTop > 1) {
          element.classList.add('scroll-divider');
        } else {
          element.classList.remove('scroll-divider');
        }
      });
    });
  }

  /**
   * Removes the divider when closing the element
   */
  addDividerToScrollingList() {
    this.element.addEventListener('click', function () {
      if (this.classList.contains('scroll-divider')) {
        this.classList.remove('scroll-divider');
      }
    });
  }

  /**
   * scrolls the screen to the id of the content defined in the href
   * uses scrolltoelement helper
   * sets and removes 'is-active state'
   */
  handleScrollOnClick() {
    this.link.forEach((item) => {
      const _this = this;
      item.addEventListener('click', function () {
        const link = this.querySelector('a');
        const href = link.getAttribute('href').substring(1);
        const elem = document.getElementById(href);
        const navHeight = _this.element.offsetHeight;

        let scrollExtraOffset = ElementHelpers.isDesktop() ? null : -navHeight;

        const stickyClass = _this.element.parentElement.dataset.rsSticky;
        const parentContainsStickyClass =
          _this.element.parentElement.classList.contains(stickyClass);

        const parentHeight = _this.element.parentElement.offsetHeight;

        // If the parentcontainer is not sticky it needs to add an extra space to the scroll, because while scrolling down
        // the navigation becomes absolute and the height will be taken out of the DOM
        if (!parentContainsStickyClass) {
          scrollExtraOffset = -parentHeight;
        }

        scrollToElement(elem, 500, scrollExtraOffset);
        _this.link.forEach((item) => {
          item.classList.remove('is-active');
        });

        this.classList.add('is-active');
      });
    });
  }

  /**
   * Get selector
   */
  static getSelector() {
    return `[${attributeBase}]`;
  }
}
