import { AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, OnInit, Renderer2, ViewChild, ViewContainerRef, EmbeddedViewRef, ViewRef, TemplateRef, ChangeDetectorRef, HostBinding } from '@angular/core';
import { MatSidenav } from '@angular/material';
import { OverlayContainer } from '@angular/cdk/overlay';
import { HeaderService, IMenuItem, LoaderService, TokenService, FilterService, KnokService, UserService, RouterExtService } from '../core/shared';

import {
  ActivatedRoute, Event,
  NavigationEnd, NavigationError, NavigationStart, Router
} from '@angular/router';

import { TranslateService } from '@ngx-translate/core';
import { MediaMonitor, MediaChange, ObservableMedia } from '@angular/flex-layout';

import '../../../public/css/styles.scss';
import '@ng-select/ng-select/themes/default.theme.css';

import { Subscription } from 'rxjs/Subscription';
import { Subscriber } from 'rxjs/Subscriber';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { delay, share } from 'rxjs/operators';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import * as MDC from 'material-components-web';

@Component({
  selector: 'knk-app',
  styleUrls: ['./app.component.scss'],
  templateUrl: './app.component.pug'
})
export class AppComponent implements AfterViewInit, OnInit, OnDestroy {
  @HostBinding('class') componentCssClass: string;
  @ViewChild('mobilesidenav') sidenav: MatSidenav;
  @ViewChild('sidenavContent', {read: ElementRef}) sidenavContentRef: ElementRef;
  @ViewChild('header') header: ElementRef;
  @ViewChild('content') content: ElementRef;
  @ViewChild('panel', {read: ViewContainerRef}) panelRef: ViewContainerRef;

  watcher: Subscription;
  bSideBarOpen: boolean = false;
  sMqAlias: string = null;

  sideMenu: IMenuItem[];
  navigationSubscription: Subscription;
  loaderSubscription: Subscription;

  get contentOffsetTop(): string {
    let result: string = '0';
    if (this.header && this.header.nativeElement) {
      const header: HTMLElement = this.header.nativeElement as HTMLElement;
      const heightOfHeader: number = (header && header.clientHeight) || 0;
      result = `${heightOfHeader}px`;
    }
    return result;
  }

  private _nativeElem: HTMLElement;

  constructor(private changeDetector: ChangeDetectorRef,
              private router: Router, private route: ActivatedRoute, private routerExt: RouterExtService,
              private translateService: TranslateService, private headerService: HeaderService, private knokService: KnokService,
              private loaderService: LoaderService, private tokenService: TokenService,
              private ngZone: NgZone, private renderer: Renderer2, private element: ElementRef,
              public overlayContainer: OverlayContainer, public media: ObservableMedia, public monitor: MediaMonitor) {
    this._nativeElem = element.nativeElement;
  }

  ngOnInit(): void {
    this.watcher = this.media.subscribe((change: MediaChange) => {
      if (change) {
        this.sMqAlias = change.mqAlias;
      }
      // console.log(change);
    });

    const currLang: string = 'en';
    const availableLang: string[] = ['en', 'ru'];
    this.translateService.addLangs(availableLang);
    this.translateService.setDefaultLang(currLang);
    this.translateService.use(currLang);

    this.router.events
      .filter((e: Event) => e instanceof NavigationError)
      .subscribe((e: NavigationError) => {
        this.unsubscribeNavigation();
        console.log(e.error);
        if (e.error === 'missed lang') {
          this.router.navigate([this.translateService.currentLang, '404'], {skipLocationChange: true});
        }
        if (e.error === 'notfound') {
          this.router.navigate([this.translateService.currentLang, '404'], {skipLocationChange: true});
        }
      }, (error) => {
        this.unsubscribeNavigation();
        // if (e.error === 'missed lang') {
        this.router.navigate([this.translateService.currentLang, '404'], {skipLocationChange: true});
        // }
      });

    this.router.events
      .filter((e: Event) => e instanceof NavigationStart)
      .subscribe(() => {
        this.ngZone.runOutsideAngular(() => {
          this.navigationSubscription = this.loaderService.subscribeLoader(this);
        });
      });

    this.router.events
      .filter((e: Event) => e instanceof NavigationEnd)
      .subscribe((value: NavigationEnd) => {
        this.ngZone.runOutsideAngular(() => {
          this.unsubscribeNavigation();
        });
        this.hookHeaderOffsetChange();
      });

    this.sideMenu = this.headerService.getMenu();
    this.loaderSubscription = this.loaderService.loaderComplete.subscribe((isLoaded: boolean) => {
      this.toggleLoaded(isLoaded);
    });

    // todo: experimental
    if (this.knokService) {
      this.knokService.setPanelWatcher.subscribe((viewRef: ViewRef): void => {
        this.panelRef.remove(0);
        this.panelRef.insert(viewRef, 0);
        this.hookHeaderOffsetChange();
      });
      this.knokService.unsetPanelWatcher.subscribe((): void => {
        this.panelRef.clear();
        this.hookHeaderOffsetChange();
      });
    }

    if (this.renderer) {
      const sidenavContent: HTMLElement = this.sidenavContentRef.nativeElement as HTMLElement;
      this.renderer.listen(sidenavContent, 'scroll', (event: any): void => {
        const header: HTMLElement = this.header.nativeElement as HTMLElement;
        const target: HTMLElement = event.target as HTMLElement;
        const scrollLeft: number = (!!target.scrollLeft) ? ((~target.scrollLeft) > 0 ? (~target.scrollLeft - 1) : (~target.scrollLeft + 1)) : 0;

        const timeout: any = setTimeout((): void => {
          if (header && header.classList.contains('app__header--fixed')) {
            this.renderer.setStyle(header, 'left', `${scrollLeft}px`);
          }
          clearTimeout(timeout);
        }, 100);
      });
    }

    this.setupTheme('default-theme');
  }

  ngAfterViewInit(): void {
    // todo: create auto init directive and clean it
    MDC.autoInit(this.element.nativeElement);
  }

  ngOnDestroy(): void {
    this.unsubscribeNavigation();
    if (this.loaderSubscription) {
      this.loaderSubscription.unsubscribe();
    }

    if (this.overlayContainer.getContainerElement().classList.contains(this.componentCssClass)) {
      this.overlayContainer.getContainerElement().classList.remove(this.componentCssClass);
    }
    this.renderer.removeClass(document.body, this.componentCssClass);
  }

  setupTheme(theme: string): void {
    if (this.overlayContainer.getContainerElement().classList.contains(this.componentCssClass)) {
      this.overlayContainer.getContainerElement().classList.remove(this.componentCssClass);
    }
    this.renderer.removeClass(document.body, this.componentCssClass);

    this.componentCssClass = theme;

    this.overlayContainer.getContainerElement().classList.add(this.componentCssClass);
    this.renderer.addClass(document.body, this.componentCssClass);
  }

  onModeToggled(state: boolean): void {
    const theme: string = (!!state) ? 'default-theme' : 'night-theme';
    this.setupTheme(theme);
  }

  hookHeaderOffsetChange(): void {
    const timeout: any = setTimeout((): void => {
      if (this.content && this.content.nativeElement) {
        const content: HTMLElement = this.content.nativeElement as HTMLElement;
        this.renderer.setStyle(content, 'margin-top', this.contentOffsetTop);
      }
      clearTimeout(timeout);
    }, 10);
  }

  @HostListener('window:scroll', ['$event'])
  onWindowScroll(event: Event): void {
    if (this.header && this.header.nativeElement) {
      const header: HTMLElement = this.header.nativeElement as HTMLElement;
      const scrollTop: number = window.pageYOffset || document.documentElement.scrollTop || 0;

      if (header && header.classList.contains('app__header--fixed')) {
        if (scrollTop > 1) {
          this.renderer.addClass(header, 'app__header--scrolled');
        } else {
          this.renderer.removeClass(header, 'app__header--scrolled');
        }
      }
    }
  }

  showSideNav(): void {
    this.sidenav.open();
  }

  isUrl(s: string): boolean {
    let result: boolean = false;
    try {
      result = this.ngZone.runOutsideAngular<boolean>(() => {
        return this.router.routerState.snapshot.url.indexOf(s) !== -1;
      });
    } catch (e) {
      result = false;
    }
    return result;
  }

  isUrlEnded(s: string): boolean {
    let result: boolean = false;
    try {
      result = this.ngZone.runOutsideAngular<boolean>(() => {
        let url: string = this.router.routerState.snapshot.url;
        let parts: Array<string> = url.split('/');
        let val: string = (url.lastIndexOf('/') !== url.length - 1 ? parts[parts.length - 1] : parts[parts.length - 2]);
        return val === s;
      });
    } catch (e) {
      result = false;
    }
    return result;
  }

  inLocation(target: string): boolean {
    let search: string = ``;
    if (target.charAt(0) === '/' && target.charAt(target.length - 1) === '/') {
      search = target.substr(1, target.length - 2);
    } else if (target.charAt(0) === '/') {
      search = target.substr(1, target.length - 1);
    } else if (target.charAt(target.length - 1) === '/') {
      search = target.substr(0, target.length - 2);
    }
    search = `/${this.lang}/${search}`;
    return this.router.routerState.snapshot.url.substr(0, search.length) === search;
  }

  get lang(): string {
    return this.translateService.currentLang || this.translateService.defaultLang;
  }

  get isLogin(): boolean {
    return this.tokenService.isLogin;
  }

  public logout(): void {
    this.tokenService.releaseToken();
  }

  private unsubscribeNavigation(): void {
    if (this.navigationSubscription) {
      this.navigationSubscription.unsubscribe();
    }
  }

  private toggleLoaded(loaded: boolean): void {
    let loadingClass: string = 'loading';
    this.ngZone.runOutsideAngular(() => {
      if (loaded) {
        this.renderer.removeClass(this._nativeElem, loadingClass);
      } else {
        this.renderer.addClass(this._nativeElem, loadingClass);
      }
    });
  }
}
