import { Utility } from '../../core/utility';
import './tooltips.sass';
import { MovementObserver } from '../../lib/movement-observer/movement-observer';
import { EventManager, EventWrapper, EVENT_TYPE } from '../../lib/event-manager/event-manager';

const TOOLTIP_CLASS = 'tooltip';
const TOOLTIP_INITIALIZED_CLASS = 'tooltip--initialized';
const TOOLTIP_OPEN_CLASS = 'tooltip--active';
const TOOLTIP_ORIENTATION_MARGIN = 10;
const TOOLTIP_CENTER_THRESHOLD = 20;

@Utility({
  selector: `.${TOOLTIP_CLASS}`,
})
export class Tooltip {
  _element;
  _handle;
  _content;

  _movementObserver;
  _eventManager;

  _openedPersistent = false;

  _xOffset = 3;
  _yOffset = -10;

  _xArrowOffset = 18;
  
  constructor(element) {
    if (!element) {
      throw new Error('Tooltip utility cannot be setup without an element!');
    }

    if (element.classList.contains(TOOLTIP_INITIALIZED_CLASS)) {
      return false;
    }

    const content = element.querySelector('.tooltip__content');

    if (!content) {
      throw new Error('Tooltip utility cannot be setup without content!');
    }

    this._content = content;

    this._element = element;
    this._handle = element.querySelector('.tooltip__handle') || element;

    this._eventManager = new EventManager();

    this._movementObserver = new MovementObserver(this._handle, { leadingCallback: this.close.bind(this) });

    element.classList.add(TOOLTIP_INITIALIZED_CLASS);
  }

  start() {
    const mouseOverEv = new EventWrapper(EVENT_TYPE.MOUSE_OVER, (() => { this.open(false); }).bind(this), this._element);
    const mouseOutEv = new EventWrapper(EVENT_TYPE.MOUSE_OUT, (this._leave.bind(this)).bind(this), this._element);
    const contentMouseOut = new EventWrapper(EVENT_TYPE.MOUSE_OUT, (this._leave.bind(this)).bind(this), this._content);
    const clickEv = new EventWrapper(EVENT_TYPE.CLICK, this._click.bind(this), this._element);
    this._eventManager.registerListeners([mouseOverEv, mouseOutEv, contentMouseOut, clickEv]);
  }

  open(persistent) {
    if (this.isOpen())
      return;
    
    this._element.classList.add(TOOLTIP_OPEN_CLASS);
    this._reposition();
    this._movementObserver.observe();
    this._openedPersistent = !!persistent;
  }

  close() {
    if (!this.isOpen())
      return;
    
    this._movementObserver.unobserve();
    this._element.classList.remove(TOOLTIP_OPEN_CLASS);
    this._openedPersistent = false;
  }

  isOpen() {
    return this._element.classList.contains(TOOLTIP_OPEN_CLASS);
  }

  _click(event) {
    if (this.isOpen() && !this._openedPersistent) {
      this._openedPersistent = true;
      return;
    }

    if (this._content == event.target || this._content.contains(event.target))
      return;
    
    if (this.isOpen())
      this.close();
    else
      this.open(true);
  }

  _leave(event) {
    if (!this.isOpen() || this._openedPersistent)
      return;
    
    const newElement = event.toElement || event.relatedTarget;

    // console.log('tooltips', '_leave', event, this._element, newElement);

    if (this._element === newElement || this._element.contains(newElement) || this._content === newElement || this._content.contains(newElement))
      return;

    this.close();
  }

  _reposition(forceRight, forceBottom) {
    const doRight = forceRight === undefined ? this._decideRight() : !!forceRight;
    const doBottom = forceBottom === undefined ? this._decideBottom() : !!forceBottom;

    // console.log('doRight', doRight);
    // console.log('doBottom', doBottom);

    if (doBottom)
      this._element.classList.add('tooltip--bottom');
    else
      this._element.classList.remove('tooltip--bottom');
    
    if (doRight)
      this._element.classList.add('tooltip--right');
    else
      this._element.classList.remove('tooltip--right');
    
    const handleCoords = this._handle.getBoundingClientRect();

    for (let _i of [1,2]) { // eslint-disable-line no-unused-vars
      const left = doRight ? handleCoords.right - this._getXOffset() - this._content.offsetWidth : handleCoords.left + this._getXOffset();
      const top = doBottom ? handleCoords.bottom - this._yOffset : handleCoords.top + this._yOffset - this._content.offsetHeight;

      this._content.style.left = `${left}px`;
      this._content.style.top = `${top}px`;
    }
  }

  _getXOffset() {
    if (this._decideCenter())
      return this._handle.offsetWidth / 2 - this._xArrowOffset;
    else
      return this._xOffset;
  }

  _decideCenter() {
    return this._handle.offsetWidth <= TOOLTIP_CENTER_THRESHOLD;
  }

  _decideBottom() {
    const handleCoords = this._handle.getBoundingClientRect();
    const windowHeight = window.innerHeight || document.documentElement.clientHeight;

    this._reposition(false, false);
    const top = handleCoords.top + this._yOffset - this._content.offsetHeight - 2 * TOOLTIP_ORIENTATION_MARGIN;
    const isHeight = Math.min(this._content.offsetHeight + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowHeight - top));

    this._reposition(false, true);
    const topBottom = handleCoords.bottom - this._yOffset;
    const isHeightBottom = Math.min(this._content.offsetHeight + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowHeight - topBottom));

    // console.log('_decideBottom', isHeight, isHeightBottom);

    return isHeightBottom > isHeight;
  }

  _decideRight() {
    const handleCoords = this._handle.getBoundingClientRect();
    const windowWidth = window.innerWidth || document.documentElement.clientWidth;
    
    this._reposition(false, false);
    const left = handleCoords.left + this._getXOffset();
    const isWidth = Math.min(this._content.offsetWidth + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowWidth - left));

    this._reposition(true, false);
    const leftRight = handleCoords.right - this._getXOffset() - this._content.offsetWidth - 2 * TOOLTIP_ORIENTATION_MARGIN;
    const isWidthRight = Math.min(this._content.offsetWidth + 2 * TOOLTIP_ORIENTATION_MARGIN, Math.max(0, windowWidth - leftRight));
    
    // console.log('_decideRight', isWidth, isWidthRight);

    return isWidthRight > isWidth;
  }

  
  destroy() {
    this._eventManager.cleanUp();
    this._movementObserver.unobserve();
    const toolTipsRegex = RegExp(/\btooltip--.+\b/, 'g');
    this._element.className = this._element.className.replace(toolTipsRegex, '');
  }
};
