/**
 * @file flash-media-source.js
 */
import document from 'global/document';
import videojs from 'video.js';
import FlashSourceBuffer from './flash-source-buffer';
import FlashConstants from './flash-constants';
import {parseContentType} from './codec-utils';

/**
 * A flash implmentation of HTML MediaSources and a polyfill
 * for browsers that don't support native or HTML MediaSources..
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
 * @class FlashMediaSource
 * @extends videojs.EventTarget
 */
export default class FlashMediaSource extends videojs.EventTarget {
  constructor() {
    super();
    this.sourceBuffers = [];
    this.readyState = 'closed';

    this.on(['sourceopen', 'webkitsourceopen'], (event) => {
      // find the swf where we will push media data
      this.swfObj = document.getElementById(event.swfId);
      this.player_ = videojs(this.swfObj.parentNode);
      this.tech_ = this.swfObj.tech;
      this.readyState = 'open';

      this.tech_.on('seeking', () => {
        let i = this.sourceBuffers.length;

        while (i--) {
          this.sourceBuffers[i].abort();
        }
      });

      // trigger load events
      if (this.swfObj) {
        this.swfObj.vjs_load();
      }
    });
  }

  /**
   * We have this function so that the html and flash interfaces
   * are the same.
   *
   * @private
   */
  addSeekableRange_() {
    // intentional no-op
  }

  /**
   * Create a new flash source buffer and add it to our flash media source.
   *
   * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
   * @param {String} type the content-type of the source
   * @return {Object} the flash source buffer
   */
  addSourceBuffer(type) {
    let parsedType = parseContentType(type);
    let sourceBuffer;

    // if this is an FLV type, we'll push data to flash
    if (parsedType.type === 'video/mp2t' || parsedType.type === 'audio/mp2t') {
      // Flash source buffers
      sourceBuffer = new FlashSourceBuffer(this);
    } else {
      throw new Error('NotSupportedError (Video.js)');
    }

    this.sourceBuffers.push(sourceBuffer);
    return sourceBuffer;
  }

  /**
   * Signals the end of the stream.
   *
   * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
   * @param {String=} error Signals that a playback error
   * has occurred. If specified, it must be either "network" or
   * "decode".
   */
  endOfStream(error) {
    if (error === 'network') {
      // MEDIA_ERR_NETWORK
      this.tech_.error(2);
    } else if (error === 'decode') {
      // MEDIA_ERR_DECODE
      this.tech_.error(3);
    }
    if (this.readyState !== 'ended') {
      this.readyState = 'ended';
      this.swfObj.vjs_endOfStream();
    }
  }

}

/**
  * Set or return the presentation duration.
  *
  * @param {Double} value the duration of the media in seconds
  * @param {Double} the current presentation duration
  * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
  */
try {
  Object.defineProperty(FlashMediaSource.prototype, 'duration', {
    /**
     * Return the presentation duration.
     *
     * @return {Double} the duration of the media in seconds
     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
     */
    get() {
      if (!this.swfObj) {
        return NaN;
      }
      // get the current duration from the SWF
      return this.swfObj.vjs_getProperty('duration');
    },
    /**
     * Set the presentation duration.
     *
     * @param {Double} value the duration of the media in seconds
     * @return {Double} the duration of the media in seconds
     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
     */
    set(value) {
      let i;
      let oldDuration = this.swfObj.vjs_getProperty('duration');

      this.swfObj.vjs_setProperty('duration', value);

      if (value < oldDuration) {
        // In MSE, this triggers the range removal algorithm which causes
        // an update to occur
        for (i = 0; i < this.sourceBuffers.length; i++) {
          this.sourceBuffers[i].remove(value, oldDuration);
        }
      }

      return value;
    }
  });
} catch (e) {
  // IE8 throws if defineProperty is called on a non-DOM node. We
  // don't support IE8 but we shouldn't throw an error if loaded
  // there.
  FlashMediaSource.prototype.duration = NaN;
}

for (let property in FlashConstants) {
  FlashMediaSource[property] = FlashConstants[property];
}

