import {
  Directive,
  HostListener,
  ComponentRef,
  ViewContainerRef,
  ComponentFactoryResolver,
  ComponentFactory,
  Input,
  OnChanges,
  SimpleChange
} from "@angular/core";
import { PopoverContent } from "./popover-content.component";

@Directive({
  selector: "[popover]",
  exportAs: "popover"
})
export class Popover implements OnChanges {

  // -------------------------------------------------------------------------
  // Properties
  // -------------------------------------------------------------------------

  private popover: ComponentRef<PopoverContent>;
  private visible: boolean;

  // -------------------------------------------------------------------------
  // Inputs / Outputs
  // -------------------------------------------------------------------------

  @Input("popover")
  content: string|PopoverContent;

  @Input()
  popoverDisabled: boolean;

  @Input()
  popoverAnimation: boolean;

  @Input()
  popoverPlacement: "top"|"bottom"|"left"|"right";

  @Input()
  popoverTitle: string;

  @Input()
  popoverOnHover: boolean = false;

  @Input()
  popoverCloseOnClickOutside: boolean;

  @Input()
  popoverCloseOnMouseOutside: boolean;

  @Input()
  popoverDismissTimeout: number = 0;

  @Input()
  flavor: 'default' | 'success' | 'danger' | 'warning' | 'info' = 'default';

  // -------------------------------------------------------------------------
  // Constructor
  // -------------------------------------------------------------------------

  constructor(private viewContainerRef: ViewContainerRef,
              private resolver: ComponentFactoryResolver) {
  }

  // -------------------------------------------------------------------------
  // Event listeners
  // -------------------------------------------------------------------------

  @HostListener("click")
  showOrHideOnClick(): void {
    if (this.popoverOnHover) return;
    if (this.popoverDisabled) return;
    this.toggle();
  }

  @HostListener("focusin")
  @HostListener("mouseenter")
  showOnHover(): void {
    if (!this.popoverOnHover) return;
    if (this.popoverDisabled) return;
    this.show();
  }

  @HostListener("focusout")
  @HostListener("mouseleave")
  hideOnHover(): void {
    if (this.popoverCloseOnMouseOutside) return; // don't do anything since not we control this
    if (!this.popoverOnHover) return;
    if (this.popoverDisabled) return;
    this.hide();
  }

  ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
    if (changes['popoverDisabled']) {
      if (changes['popoverDisabled'].currentValue) {
        this.hide();
      }
    }
  }

  // -------------------------------------------------------------------------
  // Public Methods
  // -------------------------------------------------------------------------

  toggle() {
    if (!this.visible) {
      this.show();
    } else {
      this.hide();
    }
  }

  show() {
    if (this.visible) return;

    this.visible = true;
    if (typeof this.content === "string") {
      const factory = this.resolver.resolveComponentFactory(PopoverContent);
      if (!this.visible)
        return;

      this.popover    = this.viewContainerRef.createComponent(factory);
      const popover   = this.popover.instance as PopoverContent;
      popover.popover = this;
      popover.content = this.content as string;
      if (this.popoverPlacement !== undefined)
        popover.placement = this.popoverPlacement;
      if (this.popoverAnimation !== undefined)
        popover.animation = this.popoverAnimation;
      if (this.popoverTitle !== undefined)
        popover.title = this.popoverTitle;
      if (this.popoverCloseOnClickOutside !== undefined)
        popover.closeOnClickOutside = this.popoverCloseOnClickOutside;
      if (this.popoverCloseOnMouseOutside !== undefined)
        popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside;
      popover.flavor = this.flavor;

      popover.onCloseFromOutside.subscribe(() => this.hide());
      // if dismissTimeout option is set, then this popover will be dismissed in dismissTimeout time
      if (this.popoverDismissTimeout > 0)
        setTimeout(() => this.hide(), this.popoverDismissTimeout);
    } else {
      const popover   = this.content as PopoverContent;
      popover.popover = this;
      if (this.popoverPlacement !== undefined)
        popover.placement = this.popoverPlacement;
      if (this.popoverAnimation !== undefined)
        popover.animation = this.popoverAnimation;
      if (this.popoverTitle !== undefined)
        popover.title = this.popoverTitle;
      if (this.popoverCloseOnClickOutside !== undefined)
        popover.closeOnClickOutside = this.popoverCloseOnClickOutside;
      if (this.popoverCloseOnMouseOutside !== undefined)
        popover.closeOnMouseOutside = this.popoverCloseOnMouseOutside;

      popover.onCloseFromOutside.subscribe(() => this.hide());
      // if dismissTimeout option is set, then this popover will be dismissed in dismissTimeout time
      if (this.popoverDismissTimeout > 0)
        setTimeout(() => this.hide(), this.popoverDismissTimeout);
      popover.show();
    }
  }

  hide() {
    if (!this.visible) return;

    this.visible = false;
    if (this.popover)
      this.popover.destroy();

    if (this.content instanceof PopoverContent)
      (this.content as PopoverContent).hideFromPopover();
  }

  getElement() {
    return this.viewContainerRef.element.nativeElement;
  }

}
