import { Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { NzSafeAny } from 'ng-zorro-antd/core/types';

@Directive({
  selector: 'bixi-layout-content, [bixi-layout-content]',
  exportAs: 'bixiLayoutContent',
  host: {
    class: 'bixi-layout-content'
  }
})
export class BixiLayoutContentDirective { }

@Directive({
  selector: 'bixi-layout-header, [bixi-layout-header]',
  exportAs: 'bixiLayoutHeader',
  host: {
    class: 'bixi-layout-header'
  }
})
export class BixiLayoutHeaderDirective { }

@Directive({
  selector: 'bixi-layout-menu, [bixi-layout-menu]',
  exportAs: 'bixiLayoutMenu',
  host: {
    class: 'bixi-layout-menu'
  }
})
export class BixiLayoutMenuDirective { }

@Directive({
  selector: '[nzBixiTemplateOutlet]',
  exportAs: 'nzBixiTemplateOutlet'
})
export class NzBixiTemplateOutletDirective implements OnChanges {
  private embeddedViewRef: EmbeddedViewRef<NzSafeAny> | null = null;
  @Input() nzBixiTemplateOutletContext: NzSafeAny | null = null;
  @Input() nzBixiTemplateOutlet: string | TemplateRef<NzSafeAny> | null = null;

  private recreateView(): void {
    this.viewContainer.clear();
    const isTemplateRef = this.nzBixiTemplateOutlet instanceof TemplateRef;
    const templateRef = (isTemplateRef ? this.nzBixiTemplateOutlet : this.templateRef) as NzSafeAny;
    this.embeddedViewRef = this.viewContainer.createEmbeddedView(templateRef, this.nzBixiTemplateOutletContext);
  }

  private updateContext(): void {
    const newCtx = this.nzBixiTemplateOutletContext;
    let oldCtx = [];
    if (this.embeddedViewRef) {
      oldCtx = this.embeddedViewRef.context as NzSafeAny;
    }
    if (newCtx) {
      for (const propName of Object.keys(newCtx)) {
        oldCtx[propName] = newCtx[propName];
      }
    }
  }

  constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<NzSafeAny>) { }

  ngOnChanges(changes: SimpleChanges): void {
    const shouldRecreateView = (ctxChanges: SimpleChanges): boolean => {
      const { nzBixiTemplateOutletContext, nzBixiTemplateOutlet } = ctxChanges;
      let shouldOutletRecreate = false;
      if (nzBixiTemplateOutlet) {
        if (nzBixiTemplateOutlet.firstChange) {
          shouldOutletRecreate = true;
        } else {
          const isPreviousOutletTemplate = nzBixiTemplateOutlet.previousValue instanceof TemplateRef;
          const isCurrentOutletTemplate = nzBixiTemplateOutlet.currentValue instanceof TemplateRef;
          shouldOutletRecreate = isPreviousOutletTemplate || isCurrentOutletTemplate;
        }
      }
      const hasContextShapeChanged = (ctxChange: SimpleChange): boolean => {
        const prevCtxKeys = Object.keys(ctxChange.previousValue || {});
        const currCtxKeys = Object.keys(ctxChange.currentValue || {});
        if (prevCtxKeys.length === currCtxKeys.length) {
          for (const propName of currCtxKeys) {
            if (prevCtxKeys.indexOf(propName) === -1) {
              return true;
            }
          }
          return false;
        } else {
          return true;
        }
      };
      const shouldContextRecreate = nzBixiTemplateOutletContext && hasContextShapeChanged(nzBixiTemplateOutletContext);
      return shouldContextRecreate || shouldOutletRecreate;
    };
    const recreateView = shouldRecreateView(changes);
    if (recreateView) {
      this.recreateView();
    } else {
      this.updateContext();
    }
  }
}


