import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Input,
  OnDestroy,
  OnInit,
  Output,
  TemplateRef,
  ViewChild,
  ViewContainerRef,
  ViewEncapsulation
} from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Subscription } from 'rxjs';
import { delayWhen, filter } from 'rxjs/operators';
import { EVENTS } from './label.event';
import { LabelHub } from './label.hub';
import { IClickLabelsEvent, IDeleteLabelsEvent, IHoverInLabelsEvent, ILabelingEvent, ISafeAny, ITableMatrix } from './label.type';
import { functionProxy } from './label.util';

@Component({
  selector: 'bixi-label-core',
  template: `
  <div class="bixi-label-core">
    <iframe
      #iframe
      [src]="url"
      (load)="onLoad()"
      [attr.id]="hub.id"
      [title]="hub.id">
    </iframe>
    <bixi-label-tooltip
      *ngIf="hoverInLabelsEvent && !labelTooltipDisabled"
      [tooltip]="hoverInLabelsEvent"
      [content]="labelTooltipContent">
    </bixi-label-tooltip>
    <bixi-label-modal
      *ngIf="labelModalContent && labelingEvent && !labelModalDisabled"
      [dragBoundary]="dragBoundary"
      (close)="onCloseLabelModal()"
      [labeling]="labelingEvent"
      [title]="labelModalTitle"
      [content]="labelModalContent">
    </bixi-label-modal>
   </div>
  `,
  exportAs: 'bixiLabelText',
  encapsulation: ViewEncapsulation.None,
  preserveWhitespaces: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  // TODO 做主题切换
  styleUrls: ['./style/index.less']
})
export class BixiLabelCoreComponent implements OnInit, OnDestroy {
  subscription = new Subscription();
  _engine: string;
  url: SafeResourceUrl;
  hoverInLabelsEvent: IHoverInLabelsEvent | null = null;
  labelingEvent: ILabelingEvent | null = null;
  @Input() dragBoundary: string;
  @ViewChild('iframe', { read: ViewContainerRef }) iframe: ViewContainerRef;
  @Input() hub: LabelHub;
  @Input()
  set engine(val: string) {
    this.url = this.sanitizer.bypassSecurityTrustResourceUrl(`${val || './bixi-label/index.html'}`);
    this._engine = val;
  }
  get engine() {
    return this._engine;
  }
  @Input() labelTooltipContent: TemplateRef<ISafeAny> | undefined;
  @Input() labelTooltipDisabled: boolean = false;
  @Input() labelModalTitle: string;
  @Input() labelModalDisabled: boolean = false;
  @Input() labelModalContent: TemplateRef<ISafeAny> | undefined;
  @Output() startLabeling: EventEmitter<ILabelingEvent> = new EventEmitter<ILabelingEvent>();
  @Output() deleteLabels: EventEmitter<IDeleteLabelsEvent> = new EventEmitter<IDeleteLabelsEvent>();
  @Output() clickLabels: EventEmitter<IClickLabelsEvent> = new EventEmitter<IClickLabelsEvent>();
  @Output() pdfLoadFailed = new EventEmitter();
  @Output() pdfScrolling = new EventEmitter();
  @Output() mergeTables: EventEmitter<ITableMatrix[]> = new EventEmitter();
  @Output() splitTable: EventEmitter<ITableMatrix[]> = new EventEmitter();
  constructor(
    public sanitizer: DomSanitizer,
    private cdr: ChangeDetectorRef
  ) { }

  onLoad() {
    if (!this.hub) return;
    const api = this.api;
    this.hub.setApi(api);
    this.hub.setLoaded(!!api);
  }

  get api() {
    if (!this.iframe) return;
    const service = this.iframe.element.nativeElement.contentWindow.ProxyApiService2;
    if (this.hub.dev) {
      return functionProxy(service);
    }
    return service;
  }

  ngOnInit() {
    this.subscription.add(this.hub.init$
      .pipe(
        filter(i => !!i),
        delayWhen(() => this.hub.loaded$.pipe(filter(r => r === true)))
      )
      .subscribe(options => {
        if (!this.api) return;
        this.api.init(options);
      }));
    this.subscription.add(this.hub.labels$
      .pipe(
        filter(i => !!i),
        delayWhen(() => this.hub.ready$.pipe(filter(r => r === true)))
      )
      .subscribe((labels) => {
        if (!this.api) return;
        this.api.setLabels(labels);
      }));
    this.subscription.add(this.hub.tables$
      .pipe(
        filter(i => !!i),
        delayWhen(() => this.hub.ready$.pipe(filter(r => r === true)))
      )
      .subscribe((tables) => {
        if (!this.api) return;
        this.api.setTables(tables);
      }));
    this.subscription.add(this.hub.hoverInLabelsEvent$
      .subscribe(event => {
        this.hoverInLabelsEvent = event;
        this.cdr.detectChanges();
      }));
    this.subscription.add(this.hub.labelingEvent$
      .subscribe(event => {
        this.labelingEvent = event;
        this.cdr.detectChanges();
      }));
    window.addEventListener('message', this.messageHandler);
  }

  onCloseLabelModal() {
    this.hub.stopLabeling();
    this.hub.restore();
  }

  messageHandler = (e: MessageEvent) => {
    const { id, event, data } = e.data;
    if (this.hub.id !== id) return;
    if (this.hub.logAllEvents) {
      if (!this.hub.logIgnoreEvents.includes(event)) {
        console.log(`[@bixi/label] Event(${this.hub.id})`, e.data);
      }
    }
    switch (event) {
      case EVENTS.ready: {
        // 初始化标注器
        this.hub.setReady(true);
        this.hub.innerSetPagination({
          pageCount: data.pageCount
        });
        break;
      }
      case EVENTS.zoomChange: {
        this.hub.setZoom(data.zoom);
        break;
      }
      case EVENTS.startLabeling: {
        this.hub.startLabeling(data);
        this.startLabeling.emit(data);
        break;
      }
      case EVENTS.deleteLabels: {
        this.hub.deleteLabels(data);
        this.deleteLabels.emit(data);
        break;
      }
      case EVENTS.paginationChange: {
        this.hub.innerSetPagination({
          pageNumber: data.pageNumber
        });
        break;
      }
      case EVENTS.searchResult: {
        this.hub.setSearchResult(data);
        break;
      }
      case EVENTS.clickLabels: {
        this.clickLabels.emit(data);
        break;
      }
      case EVENTS.hoverInLabel: {
        this.hub.hoverInLabels(data);
        break;
      }
      case EVENTS.hoverOutLabel: {
        this.hub.hoverInLabels(null);
        break;
      }
      case EVENTS.pdfScrolling: {
        if (this.hub.hoverInLabelsEvent) {
          this.hub.hoverInLabels(null);
        }
        this.pdfScrolling.emit(true);
        break;
      }
      case EVENTS.pdfLoadFailed: {
        this.pdfLoadFailed.emit();
        break;
      }
      case EVENTS.log: {
        this.hub.setLog(data);
        break;
      }
      case EVENTS.resize: {
        this.hub.resize$.next();
        break;
      }
      case EVENTS.mergeTables: {
        this.mergeTables.emit(data);
        break;
      }
      case EVENTS.splitTable: {
        this.splitTable.emit(data);
        break;
      }
      default: {
        if (this.hub.logUnhandledEvents) {
          if (!this.hub.logIgnoreEvents.includes(event)) {
            console.warn(`[@bixi/label] Unhandled Event(${this.hub.id}) `, event, data);
          }
        }
        break;
      }
    }
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
    this.hub.destory();
    window.removeEventListener('message', this.messageHandler);
  }
}
