import { AfterContentInit, AfterViewInit, Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { interval, Subscription } from 'rxjs';

@Directive({ selector: '[uiElementDistance]', exportAs: 'uiElementDistance' })
export class UiElementDistanceDirective implements OnInit, OnDestroy, AfterViewInit {
  private _subscriptions = new Subscription();

  private _distance: number;

  get distance() {
    return this._distance;
  }

  get nativeElement() {
    return this._elementRef.nativeElement;
  }

  @Input('uiElementDistance') targetElement: HTMLElement | ElementRef;

  @Output() distanceChange = new EventEmitter<number>();

  constructor(private _elementRef: ElementRef) {}

  ngOnInit() {
    this._subscriptions.add(
      interval(500).subscribe(() => {
        this.calculateDistance();
      })
    );
  }

  ngOnDestroy() {
    this._subscriptions.unsubscribe();
  }

  ngAfterViewInit() {
    this.calculateDistance();
  }

  getHtmlElement(element: HTMLElement | ElementRef): HTMLElement {
    return element instanceof HTMLElement ? element : element.nativeElement;
  }

  getElementDistance(): number {
    const startElement = this.getHtmlElement(this.nativeElement);
    const endElement = this.getHtmlElement(this.targetElement);

    const startRect = startElement.getBoundingClientRect();
    const endRect = endElement.getBoundingClientRect();

    const startTop = startRect.top;
    const startLeft = startRect.left;
    const endTop = endRect.top;
    const endLeft = endRect.left;

    const distance = Math.sqrt(Math.pow(startTop - endTop, 2) + Math.pow(startLeft - endLeft, 2));

    return Math.floor(distance);
  }

  calculateDistance() {
    const distance = this.getElementDistance();

    if (this._distance !== distance) {
      this._distance = distance;
      this.distanceChange.emit(this._distance);
    }
  }
}
