import { Directive, Renderer2, ElementRef, Input, ContentChildren, QueryList, ChangeDetectorRef, SimpleChanges, OnChanges, OnDestroy, AfterContentInit } from '@angular/core';
import { Router, RouterLinkWithHref, RouterEvent, NavigationEnd } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { RouterLinkDelayDirective } from './router-link-delay.directive';

@Directive({
  selector: '[routerLinkActiveDelay]',
  exportAs: 'routerLinkActiveDelay'
})
export class RouterLinkActiveDelayDirective implements OnChanges, OnDestroy, AfterContentInit {
  @Input() set routerLinkActiveDelay(data: string[] | string) {
    const classes: any = Array.isArray(data) ? data : data.split(' ');
    this.classes = classes.filter(c => !!c);
  }

  @Input() routerLinkActiveDelayOptions: { exact: boolean } = {exact: false};
  @ContentChildren(RouterLinkDelayDirective, {descendants: true}) links: QueryList<RouterLinkDelayDirective>;
  @ContentChildren(RouterLinkWithHref, {descendants: true}) linksWithHrefs: QueryList<RouterLinkWithHref>;

  public readonly isActive: boolean = false;
  private classes: string[] = [];
  private subscription: Subscription;

  constructor(private router: Router, private element: ElementRef, private renderer: Renderer2,
              private cdr: ChangeDetectorRef) {
    this.subscription = router.events.subscribe((s: RouterEvent) => {
      if (s instanceof NavigationEnd) {
        this.update();
      }
    });
  }

  ngAfterContentInit(): void {
    this.links.changes.subscribe(_ => this.update());
    this.linksWithHrefs.changes.subscribe(_ => this.update());
    this.update();
  }

  ngOnChanges(changes: SimpleChanges): void {
    this.update();
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }

  private update(): void {
    if (!this.links || !this.linksWithHrefs || !this.router.navigated) {
      return;
    }

    Promise.resolve().then(() => {
      const hasActiveLinks: boolean = this.hasActiveLinks();
      if (this.isActive !== hasActiveLinks) {
        (this as any).isActive = hasActiveLinks;
        this.classes.forEach((c) => {
          if (hasActiveLinks) {
            this.renderer.addClass(this.element.nativeElement, c);
          } else {
            this.renderer.removeClass(this.element.nativeElement, c);
          }
        });
      }
    });
  }

  private isLinkActive(router: Router): (link: (RouterLinkDelayDirective | RouterLinkWithHref)) => boolean {
    return (link: RouterLinkDelayDirective | RouterLinkWithHref) =>
      router.isActive(link.urlTree, this.routerLinkActiveDelayOptions.exact);
  }

  private hasActiveLinks(): boolean {
    return this.links.some(this.isLinkActive(this.router)) ||
      this.linksWithHrefs.some(this.isLinkActive(this.router));
  }
}
