text: /*!
   * Copyright (c) 2015-2018 Cisco Systems, Inc. See LICENSE file.
   */
  
  import LoggerProxy from '../common/logs/logger-proxy';
  import {_ACTIVE_, RECONNECTION} from '../constants';
  import ReconnectionError from '../common/errors/reconnection';
  import PeerConnectionManager from '../peer-connection-manager';
  import Media from '../media';
  
  /**
   * @export
   * @class ReconnectionManager
  */
  export default class ReconnectionManager {
    /**
     * @param {Meeting} meeting
     */
    constructor(meeting) {
      this.status = RECONNECTION.STATE.DEFAULT_STATUS;
      this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;
      this.spark = meeting.spark;
      this.configure(meeting);
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {undefined}
     */
    configure(meeting) {
      this.backOff = meeting.config.reconnection.retry.backOff.start;
    }
  
    /**
     * @returns {undefined}
     */
    reset() {
      this.status = RECONNECTION.STATE.DEFAULT_STATUS;
      this.tryCount = RECONNECTION.STATE.DEFAULT_TRY_COUNT;
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {Boolean}
     * @returns {ReconnectionError}
     */
    validate(meeting) {
      if (meeting.config.reconnection.enabled) {
        if (
          meeting.locusInfo &&
            meeting.locusInfo.fullState &&
            (
              meeting.locusInfo.fullState.state === _ACTIVE_ &&
              meeting.locusInfo.fullState.active
            )
        ) {
          if (meeting.spark.internal.mercury.connected) {
            if (
              this.status !== RECONNECTION.STATE.DEFAULT_STATUS ||
              this.status !== RECONNECTION.STATE.COMPLETE
            ) {
              return true;
            }
  
            return new ReconnectionError(new Error('Multiple reconnections cannot occur concurrently'));
          }
  
          return new ReconnectionError(new Error('mercury is not connected, cannot reconnect'));
        }
  
        return new ReconnectionError(new Error('locus server for this call is not active, cannot reconnect'));
      }
  
      return new ReconnectionError(new Error('Reconnection is not enabled.'));
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {Promise}
     */
    reconnect(meeting) {
      const validatedReconnect = this.validate(meeting);
  
      return validatedReconnect === true ? this.execute(meeting) : Promise.reject(validatedReconnect);
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {Promise}
     */
    execute(meeting) {
      this.status = RECONNECTION.STATE.IN_PROGRESS;
  
      return this.media(meeting)
        .then((media) => {
          LoggerProxy.logger.log(`ReconnectionManager->execute#Media reestablished at: ${new Date()}`);
          this.status = RECONNECTION.STATE.COMPLETE;
  
          return Promise.resolve(media);
        })
        .catch((err) => { // eslint-disable-line
          LoggerProxy.logger.error(`ReconnectionManager->execute#Media reestablishment failed at: ${new Date()}`);
          if (this.tryCount > meeting.config.reconnection.retry.times) {
            this.status = RECONNECTION.STATE.FAILURE;
  
            return Promise.reject(new ReconnectionError(err));
          }
          // exponential backoff and retry logic
          setTimeout(() =>
            this.reconnect(meeting),
          this.backOff *= meeting.config.reconnection.retry.backOff.rate);
        });
    }
  
    reconnectMedia(meeting, recreate = false) {
      let promise = null;
  
      if (recreate) {
        promise = Media.attachMedia(meeting.mediaProperties)
          .then((peerConnection) => meeting.setRemoteStream(peerConnection));
      }
      else {
        promise = Media.reconnectMedia(meeting.mediaProperties.peerConnection);
      }
  
      return Promise.resolve(promise)
        .then(() => meeting.roap
          .sendRoapMediaRequest({
            sdp: meeting.mediaProperties.peerConnection.sdp,
            roapSeq: meeting.roapSeq,
            meeting // or can pass meeting ID
          }));
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {Promise} see #Meeting.addMedia
     */
    media(meeting) {
      this.tryCount += 1;
      LoggerProxy.logger.log(`Reconnection->media#Begin reestablishment of media at ${new Date()}`);
  
  
      // TODO: chekc peerconnection status
      // create an offer
      return this.reconnectMedia(meeting)
        .then((media) => Promise.resolve(media))
        .catch((err) => {
          LoggerProxy.logger.warn(`Reconnection->media#Media could not reuse peer connection, failed with ${err}`);
          // TODO: test getting to this scenario
          ReconnectionManager.cleanup(meeting);
          meeting.mediaProperties.reInitiatePeerconnection();
  
          return meeting.reconnectMedia(true)
            .catch((error) => {
              LoggerProxy.logger.error(`ReconnectionManager->media#Error reconnecting , error: ${error}`);
            });
        });
    }
  
    /**
     * @param {Meeting} meeting
     * @returns {undefined}
     */
    static cleanup(meeting) {
      // close pcs, unset to null and reuse the streams with new pcs
      PeerConnectionManager.close(meeting.mediaProperties.peerConnection);
      meeting.mediaProperties.unsetPeerConnection();
    }
  }
  