import {Component, OnInit, Input, ElementRef, EventEmitter, Output, OnChanges, OnDestroy} from '@angular/core';
import {CloudinaryVideo} from '@cloudinary/url-gen';

import {
  cancelCurrentlyRunningPlugins,
  HtmlVideoLayer,
  Plugins,
  VideoPoster,
  VideoSources
} from '@cloudinary/html';


/**
 * @memberOf AngularSDK
 * @type {Component}
 * @description The Cloudinary video component.
 * @prop {CloudinaryVideo} transformation Generated by @cloudinary/url-gen
 * @prop {VideoPoster} transformation Generated by @cloudinary/url-gen
 * @prop {Plugins} plugins Advanced image component plugins lazyload()
 * @prop videoAttributes Optional attributes include controls, loop, muted, poster, preload, autoplay
 * @prop videoEvents Optional video events include play, loadstart, playing, error, ended
 * @prop {VideoSources} sources Optional sources to generate
 * @example
 *  <caption>
 *  Using custom defined resources.
 * </caption>
 *   vid = new CloudinaryVideo('dog', {cloudName: 'demo'});
 *   sources = [
 {
        type: 'mp4',
        codecs: ['vp8', 'vorbis'],
        transcode: videoCodec(auto())
},
 {
        type: 'webm',
        codecs: ['avc1.4D401E', 'mp4a.40.2'],
         transcode: videoCodec(auto())
      }];
 *
 * <advanced-video [cldvid]="vid" [sources]="sources" controls></advanced-video>
 */
@Component({
  // tslint:disable-next-line:component-selector
  selector: 'advanced-video',
  template: `<video (play)="emitPlayEvent()"
                    (loadstart)="emitLoadstartEvent()"
                    (playing)="emitPlayingEvent()"
                    (error)="emitErrorEvent"
                    (ended)="emitEndedEvent">
            </video>`,
})
export class CloudinaryVideoComponent implements OnInit, OnChanges, OnDestroy {
  constructor(private el: ElementRef) { }

  @Input('cldVid') cldVid: CloudinaryVideo;
  @Input('cldPoster') cldPoster: VideoPoster;
  @Input('sources') sources: VideoSources;
  @Input('plugins') plugins: Plugins;
  @Input('poster') poster: string;
  @Input('innerRef') innerRef: ElementRef;

  // Event emitters
  @Output() play: EventEmitter<any> = new EventEmitter();
  @Output() loadstart: EventEmitter<any> = new EventEmitter();
  @Output() playing: EventEmitter<any> = new EventEmitter();
  @Output() error: EventEmitter<any> = new EventEmitter();
  @Output() ended: EventEmitter<any> = new EventEmitter();

  // supported video attributes
  controls = this.el.nativeElement.attributes.controls;
  loop = this.el.nativeElement.attributes.loop;
  muted = this.el.nativeElement.attributes.muted;
  preload = this.el.nativeElement.attributes.preload;
  autoPlay = this.el.nativeElement.attributes.autoplay;
  playsInline = this.el.nativeElement.attributes.playsInline;

  private htmlVideoLayerInstance: HtmlVideoLayer;

  /**
   * On init creates a new HTMLVideoLayer instance and initializes with ref to video element,
   * user generated cloudinaryVideo and the plugins to be used.
   */
  ngOnInit() {
    this.htmlVideoLayerInstance = new HtmlVideoLayer(
      this.el.nativeElement.children[0],
      this.cldVid,
      this.sources,
      this.plugins,
      this.getVideoAttributes(),
      this.cldPoster
      );

    // check if video should be muted. We need to take care of this here since Angular has a bug with binding the muted
    // attribute
    if (this.muted) {
      this.el.nativeElement.children[0].muted = true;
    }

    // attach ref to innerRef input
    this.attachRef();
  }

  /**
   * On update, we cancel running plugins and update the video instance if the src
   * was changed.
   */
  ngOnChanges() {
    if (this.htmlVideoLayerInstance) {
      cancelCurrentlyRunningPlugins(this.htmlVideoLayerInstance.htmlPluginState);
      this.htmlVideoLayerInstance.update(this.cldVid, this.sources, this.plugins, this.getVideoAttributes(), this.cldPoster);
    }
  }

  /**
   * On destroy, we cancel the currently running plugins.
   */
  ngOnDestroy() {
    // Safely cancel running events on destroy
    cancelCurrentlyRunningPlugins(this.htmlVideoLayerInstance.htmlPluginState);
  }

  /**
   * Returns video attributes.
   */
  getVideoAttributes() {
    return {
      controls: this.controls,
      loop: this.loop,
      muted: this.muted,
      poster: this.poster,
      preload: this.preload,
      autoplay: this.autoPlay,
      playsinline: this.playsInline
    };
  }

  emitPlayEvent() {
    this.play.emit();
  }

  emitLoadstartEvent() {
    this.loadstart.emit();
  }

  emitPlayingEvent() {
    this.playing.emit();
  }

  emitErrorEvent() {
    this.error.emit();
  }

  emitEndedEvent() {
    this.ended.emit();
  }

  /**
   * Attach both this.videoRef and props.innerRef as ref to the given element.
   */
  attachRef() {
    if (this.innerRef) {
      this.innerRef.nativeElement = this.el.nativeElement.children[0];
    }
  }
}