import { Directive, ElementRef, Output, EventEmitter, HostListener, OnInit, OnDestroy, Input } from '@angular/core';
import { Subject } from 'rxjs';
import { skip, takeUntil } from 'rxjs/operators';

@Directive({ selector: '[clickOutside]' })
export class ClickOutsideDirective implements OnInit, OnDestroy {
  clickedOutside$ = new Subject();
  destroy$ = new Subject();

  @Input() includeTouchEvents = true;

  @Output() clickOutside = new EventEmitter<void>();

  @HostListener('document:click', ['$event.target', '$event'])
  @HostListener('document:touchstart', ['$event.target'])
  onClick(target: HTMLElement, event: MouseEvent | TouchEvent) {
    const isClickedInside = this.elementRef.nativeElement.contains(target);

    if (!isClickedInside) {
      if (!this.includeTouchEvents && this.isTouchEvent(event)) {
        return;
      }

      this.clickedOutside$.next();
    }
  }

  constructor(private elementRef: ElementRef) {}

  ngOnInit() {
    this.clickedOutside$.pipe(takeUntil(this.destroy$), skip(1)).subscribe((_) => this.clickOutside.emit());
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  private isTouchEvent(event: MouseEvent | TouchEvent | undefined): boolean {
    return !(event instanceof MouseEvent);
  }
}
