import {Component, ComponentInterface, h, Prop, State, Listen, Element} from '@stencil/core';
// For easy reference
let keys = {
  end: 35,
  home: 36,
  left: 37,
  up: 38,
  right: 39,
  down: 40,
  delete: 46,
};

// Add or substract depending on key pressed
let direction = {
  37: -1,
  38: -1,
  39: 1,
  40: 1,
};
let checkTimeout;
let focusTimeout;
let scrollTimeout;

@Component({
  tag: 'hy-tabs',
  styleUrl: 'hy-tabs.scss',
  shadow: false,
})
export class HyTabs implements ComponentInterface {
  @Prop() tabId?: string;
  @Prop() tabListLabel: string = '';
  @State() focusTimeoutCleared: boolean = true;
  @State() tabButtonTitles: object[];
  @State() tabPanelsState: NodeListOf<Element>[];
  @State() tabButtons: NodeListOf<Element>[];
  @State() tabList: NodeListOf<Element>[];
  @Element() el: HTMLElement;
  @Prop() headerstyle: string = 'common';

  componentWillLoad() {
    const tabItems = this.el.querySelectorAll('hy-tabs-item');
    if (tabItems) {
      this.tabButtonTitles = Array.from(tabItems).map((panel) => {
        return {
          title: panel.getAttribute('tab-title'),
          id: panel.getAttribute('tab-title').toLowerCase().replace(/\W/g, '-'),
        };
      });
    }
  }

  componentDidLoad() {
    let hyMainDiv = this.el.closest('.hy-main');
    if (hyMainDiv) {
      if (!hyMainDiv.classList.contains('with-sidebar')) {
        this.headerstyle = 'large';
      }
    }

    const tabContainer = this.el.querySelector('.hy-tabs__container') as any;

    if (tabContainer) {
      this.generateArrays(tabContainer);

      const leftButton = this.el.querySelectorAll('.hy-tab-scroll__left')[0];
      const rightButton = this.el.querySelectorAll('.hy-tab-scroll__right')[0];
      const tabList = this.tabList as any;

      this.checkScrollHidden(tabList, leftButton, rightButton);

      tabList.addEventListener(
        'scroll',
        () => {
          scrollTimeout = setTimeout(() => {
            window.clearTimeout(scrollTimeout);
            this.checkScrollHidden(tabList, leftButton, rightButton);
          }, 250);
        },
        false
      );

      if (tabList.offSetWidth <= document.body.scrollWidth) {
        leftButton.classList.add('is-hidden');
        rightButton.classList.add('is-hidden');
      }

      const oneTabWidth = 250;
      rightButton.addEventListener('click', (e) => {
        e.preventDefault();
        rightButton.classList.add('is-disabled');
        tabList.scrollBy({
          top: 0,
          left: +oneTabWidth,
          behavior: 'smooth',
        });
        checkTimeout = window.setTimeout(() => {
          window.clearTimeout(checkTimeout);
          this.checkScrollHidden(tabList, leftButton, rightButton);
          rightButton.classList.remove('is-disabled');
        }, 250);
      });

      leftButton.addEventListener('click', (e) => {
        e.preventDefault();
        leftButton.classList.add('is-disabled');
        tabList.scrollBy({
          top: 0,
          left: -oneTabWidth,
          behavior: 'smooth',
        });
        checkTimeout = window.setTimeout(() => {
          window.clearTimeout(checkTimeout);
          this.checkScrollHidden(tabList, leftButton, rightButton);
          leftButton.classList.remove('is-disabled');
        }, 250);
      });
    }
  }

  generateArrays(tc) {
    this.tabList = tc.querySelectorAll('[role="tablist"]')[0] as any;
    this.tabButtons = tc.querySelectorAll('[role="tab"]') as any;
    this.tabPanelsState = [tc.querySelectorAll('[role="tabpanel"]')] as any;

    if (this.tabButtons.length > 0) {
      this.addListeners(this.tabButtons, 1);
      this.activateTab(this.tabButtons[0], true);
    }
  }

  addListeners(tabs, index) {
    if (tabs.length > 1) {
      for (let i = 0; i < tabs.length; ++i) {
        tabs[index].addEventListener('click', this.clickEventListener);
        tabs[index].addEventListener('keydown', this.keydownEventListener);
        //tabs[index].addEventListener('keyup', this.keyupEventListener);
        tabs[index].index = index;
      }
    }
  }

  checkScrollHidden(tablist, leftButton, rightButton) {
    if (tablist.scrollLeft === 0) {
      leftButton.classList.add('is-hidden');
    } else {
      leftButton.classList.remove('is-hidden');
    }
    if (tablist.scrollLeft + tablist.clientWidth >= tablist.scrollWidth - 1) {
      rightButton.classList.add('is-hidden');
    } else {
      rightButton.classList.remove('is-hidden');
    }
  }

  // When a tab is clicked, activateTab is fired to activate it
  @Listen('click')
  clickEventListener(event) {
    if (event) {
      const target = event.target;
      const tabs = this.tabButtonTitles;
      if (tabs) {
        tabs.forEach((tab) => {
          const id = Object.values(tab)[1];
          if (id === target.id) {
            this.activateTab(target, true);
          }
        });
        event.stopPropagation();
        event.stopImmediatePropagation();
      }
    }
  }

  @Listen('keydown')
  keydownEventListener(event) {
    const key = event.keyCode;
    const tabs = this.tabButtonTitles as any;
    switch (key) {
      case keys.end:
        event.preventDefault();
        // Activate last tab
        this.activateTab(tabs[tabs.length - 1], true);
        break;
      case keys.home:
        event.preventDefault();
        // Activate first tab
        this.activateTab(tabs[0], true);
        break;

      // Up and down are in keydown
      // because we need to prevent page scroll >:)
      case keys.up:
      case keys.down:
        if (this.focusTimeoutCleared) {
          this.determineOrientation(event);
        }
        break;
    }
  }

  @Listen('keydown')
  keyupEventListener(event) {
    const key = event.keyCode;

    switch (key) {
      case keys.left:
      case keys.right:
        event.preventDefault();
        if (this.focusTimeoutCleared) {
          this.determineOrientation(event);
        }
        break;
    }
    event.stopPropagation();
    event.stopImmediatePropagation();
  }

  determineOrientation(event) {
    const leftButton = this.el.querySelectorAll('.hy-tab-scroll__left')[0];
    const rightButton = this.el.querySelectorAll('.hy-tab-scroll__right')[0];
    const tabList = this.tabList as any;

    this.checkScrollHidden(tabList, leftButton, rightButton);

    const key = event.keyCode;
    const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
    let proceed = false;
    if (vertical) {
      if (key === keys.up || key === keys.down) {
        event.preventDefault();
        proceed = true;
      }
    } else {
      if (key === keys.left || key === keys.right) {
        proceed = true;
      }
    }

    if (proceed) {
      this.switchTabOnArrowPress(event);
    }
  }

  switchTabOnArrowPress(event) {
    const pressed = event.keyCode;

    if (direction[pressed]) {
      const target = event.target;
      const tabs = this.tabButtons as any;

      for (let i = 0; i < tabs.length; i++) {
        if (tabs[i].id === target.id) {
          if (i > 0) {
            if (direction[pressed] === -1) {
              tabs[i - 1].focus();
              this.focusEventHandler(tabs[i - 1]);
              break;
            }
          }
          if (i < tabs.length - 1) {
            if (direction[pressed] === 1) {
              tabs[i + 1].focus();
              this.focusEventHandler(tabs[i + 1]);
              break;
            }
          }
        }
      }
    }
  }

  // Activates any given tab panel
  activateTab(tab, setFocus) {
    setFocus = setFocus || true;
    // Deactivate all other tabs
    this.deactivateTabs(this.tabButtons);
    // Remove tabindex attribute
    tab.removeAttribute('tabindex');
    tab.setAttribute('aria-selected', 'true');
    const controls = tab.getAttribute('aria-controls');
    this.el.querySelector(`#${controls}`).removeAttribute('hidden');

    if (setFocus) {
      tab.focus();
    }
  }

  // Deactivate all tabs and tab panels
  deactivateTabs(tabs) {
    for (let t = 0; t < tabs.length; t++) {
      tabs[t].setAttribute('tabindex', '-1');
      tabs[t].setAttribute('aria-selected', 'false');
      tabs[t].removeEventListener('focus', this.focusEventHandler);
    }
    const panels = this.tabPanelsState[0];
    for (let p = 0; p < panels.length; p++) {
      panels[p].setAttribute('hidden', 'hidden');
    }
  }

  @Listen('focus')
  focusEventHandler(tab) {
    const target = tab;
    this.focusTimeoutCleared = false;
    focusTimeout = window.setTimeout(() => {
      window.clearTimeout(focusTimeout);
      this.focusTimeoutCleared = true;
      const focused = document.activeElement;
      if (target === focused) {
        this.activateTab(target, false);
      }
    }, 250);
  }

  render() {
    const classComponentAttributes = ['hy-tabs__container', `hy-tabs__container__${this.headerstyle}`].join(' ');
    const id = this.tabId.toLowerCase().replace(/\W/g, '-');

    return [
      <hy-box pt="1.25, 1.25, 1.5, 2.5" />,
      <div id={id} class={classComponentAttributes}>
        <div class="hy-tablist-container">
          <button tabindex="-1" class="hy-tab-scroll hy-tab-scroll__left is-hidden" aria-hidden="true">
            <span>
              <hy-icon icon={'hy-icon-caret-left'} size={16} />
            </span>
          </button>
          <div role="tablist" aria-label={this.tabListLabel}>
            {this.tabButtonTitles &&
              this.tabButtonTitles.map((item) => {
                const title = Object.values(item)[0];
                const id = title.toLowerCase().replace(/\W/g, '-');
                return (
                  <button aria-selected="false" aria-controls={`${id}-tab`} class={this.headerstyle} role="tab" id={id}>
                    <span>{title}</span>
                  </button>
                );
              })}
          </div>
          <button tabindex="-1" class="hy-tab-scroll hy-tab-scroll__right" aria-hidden="true">
            <span>
              <hy-icon icon={'hy-icon-caret-right'} size={16} />
            </span>
          </button>
        </div>
        <slot></slot>
      </div>,
      <hy-box mb="1.75, 1.75, 2, 2.5" />,
    ];
  }
}