import { I18N } from 'aurelia-i18n';
import { autoinject, bindable, customAttribute } from "aurelia-framework";
import { UITether } from "../utils/ui-tether";
import { UIInternal } from "../utils/ui-internal";
import { Subscription } from "aurelia-event-aggregator";

/*** Global tooltip container ***/
let TooltipEl: HTMLDivElement & { tether?: UITether.Tether };

/*** Id seed ***/
let seed = 0;

@autoinject()
@customAttribute("ui-tooltip")
export class UITooltip {
  /**
   * Tooltip value
   * `ui-tooltip="value.bind:''"`
   */
  @bindable({ primaryProperty: true })
  public value;

  /**
   * Tooltip label
   * `ui-tooltip="label.bind:''"`
   */
  @bindable()
  public label;

  /**
   * Tooltip classes
   * `ui-tooltip="classes.bind:''; value.bind:''"`
   */
  @bindable()
  public classes = "";

  /**
   * Tooltip theme
   * `ui-tooltip="theme.bind:''; value.bind:''"`
   */
  @bindable()
  public theme = "";

  /**
   * Tooltip position
   * @unused
   * @values "left" | "right" | "top" | "bottom"
   * `ui-tooltip="position.bind:''; value.bind:''"`
   */
  @bindable()
  public position: "left" | "right" | "top" | "bottom" = "bottom";

  /**
   * Tooltip open delay in ms. 100 is the default value.
   * `ui-tooltip="delay.bind:100; value.bind:''"`
   * @memberof UITooltip
   */
  @bindable()
  public delay = 100;

  /**
   * Tooltip useClick
   * `ui-tooltip="use-click.one-time:''; value.bind:''"`
   */
  @bindable()
  public useClick: boolean = false;

  /*** Start private props ***/
  private id = `tooltip-${seed++}`;

  private timer;
  private parentEl: Element | HTMLElement;

  private shown: boolean = false;

  private disposables: Array<Subscription> = [];

  /*** End private props ***/

  constructor(protected element: Element, private i18n: I18N) {
  }

  protected attached(): void {
    /*** Find parent element ***/
    if (this.element.nodeType === Node.ELEMENT_NODE) {
      this.parentEl = this.element;
    }
    if (this.element.nodeType === Node.COMMENT_NODE) {
      this.parentEl = this.element.previousElementSibling;
    }

    /*** Create tooltip container ***/
    if (!TooltipEl) {
      TooltipEl = document.createElement("div");
      TooltipEl.className = "ui-tooltip";
      TooltipEl.tether = UITether.tether(this.parentEl, TooltipEl, {
        anchorPosition: "tc",
        attachToViewport: true,
        position: "bc",
        resize: false
      });
    }

    /*** Attach event listeners ***/
    if (this.useClick) {
      this.parentEl.addEventListener("click", this.toggleFn);
    }
    else {
      this.parentEl.addEventListener("mouseenter", this.showFn);
      this.parentEl.addEventListener("mouseleave", this.hideFn);
    }
  }

  protected detached(): void {
    /*** Hide tooltip ***/
    this.hide();
    /*** Detach event listeners ***/
    if (this.useClick) {
      this.parentEl.removeEventListener("click", this.toggleFn);
      this.disposeListeners();
    }
    else {
      this.parentEl.removeEventListener("mouseenter", this.showFn);
      this.parentEl.removeEventListener("mouseleave", this.hideFn);
    }
  }

  protected show(): void {
    /*** Ignore if now value ***/
    if (isEmpty(this.value) && isEmpty(this.label)) {
      return;
    }
    /*** Update tooltip container ***/
    TooltipEl.className = `ui-tooltip ui-theme--${this.theme} ${this.classes}`;
    TooltipEl.innerHTML = this.value ?? this.i18n.tr(this.label);
    TooltipEl.dataset.id = this.id;
    TooltipEl.dataset.pos = this.position;

    /*** Update tooltip tether ***/
    let anchorPosition: UITether.Position = "tc";
    let position: UITether.Position = "bc";
    switch (this.position) {
      case "right":
        anchorPosition = "cr";
        position = "cl";
        break;
      case "left":
        anchorPosition = "cl";
        position = "cr";
        break;
      case "bottom":
        anchorPosition = "bc";
        position = "tc";
        break;
    }
    TooltipEl.tether.updatePosition(this.parentEl, { position, anchorPosition });

    /*** Set display timeout ***/
    this.timer = setTimeout(() => (TooltipEl.dataset.open = "true"), this.delay);
  }

  protected hide() {
    /*** Clear display timeout ***/
    clearTimeout(this.timer);
    /*** Hide tooltip ***/
    TooltipEl.dataset.open = "false";
  }

  protected valueChanged() {
    /*** Update tooltip if visible tooltip is this.id ***/
    if (TooltipEl && TooltipEl.dataset.open === "true" && TooltipEl.dataset.id === this.id) {
      this.show();
    }
  }

  /*** Event handler callbacks ***/
  private showFn = () => this.show();
  private hideFn = () => this.hide();

  private toggleFn = () => {
    if (this.shown) {
      this.hideTooltip();
    }
    else {
      this.show();
      this.shown = true;

      this.disposables.push(
        UIInternal.subscribe(UIInternal.EVT_VIEWPORT_CLICK, t => this.close(t)),
        UIInternal.subscribe(UIInternal.EVT_VIEWPORT_WHEEL, t => this.close(t)),
        UIInternal.subscribe(UIInternal.EVT_VIEWPORT_RESIZE, () => this.show())
      );
    }
  }

  private close(t: Element) {
    if (this.shown && t != this.parentEl) {
      this.hideTooltip();
    }
  }

  private hideTooltip() {
    this.hide();
    this.shown = false;

    this.disposeListeners();
  }

  private disposeListeners(): void {
    this.disposables.forEach(d => d.dispose());
  }
}