const EventEmitter = require('eventemitter3');

/**
 * <h3>Represents browser API for Gallery IPX.</h3>
 * <p>
 * The API is available on any page that runs one or more Brightcove embedded experiences.
 * It is designed to help 3rd parties with Brightcove experiences integration.
 * Provides access to current player, videos and state information as well as
 * listeners for playback-related events.
 * </p>
 * <p>
 * Before accessing the Client API, you will need to get the correct embedded IPX on the page.
 * They can be accessed by the id of the <div> element you used when you embedded it
 * by running:
 * </p>
 * <p>
 *  <strong>window.top.bcov.gal.getEmbed(bc-embed-<i>exp_id</i>),</strong>
 * where <i>exp_id</i> is the experience id.
 * </p>
 * <p>
 * You can also get an object with all IPX available on a page by running <strong>window.top.bcov.gal.getEmbeds()</strong>.
 * </p>
 * @hideconstructor
 */
class ClientApi extends EventEmitter {
  /**
   * Returns current player that was active on the page
   * @returns {ClientApi~Player}
   * @private
   */
  getCurrentPlayer () {
    /**
    * See [Player Documentation]{@link https://brightcovelearning.github.io/Brightcove-API-References/brightcove-player/current-release/Player.html} for more information about the Player object.
    * @typedef {Object} ClientApi~Player
    */
    return this.player;
  }

  /**
   * Saves current player. If the player has changed, emits 'playerChanged' event
   * @param {ClientApi~Player}
   * @private
   */
  setCurrentPlayer (player) {
    if (player !== this.player) {
      this.player = player;
      /**
       * playerChanged event. Emitted when a different player is going to be used for playback.
       * Can be generated multiple times for experiences that have multiple players.
       * @event ClientApi#playerChanged
       * @param {ClientApi~Player}
       */
      this.emit('playerChanged', this.player);
    }
  }

  /**
   * Returns currently playing video or the last video that was played
   * @returns {ClientApi~Video} Video Metadata object
   */
  getCurrentVideo () {
    /**
    * See [Video Metadata Information] {@link https://support.brightcove.com/video-metadata-mediainfo} for more information about the video format.
    * for detailed description
    * @typedef {Object} ClientApi~Video
    */
    return this.video;
  }

  /**
   * Saves current video. If the video has changed, emits 'videoChanged' event
   * @param {ClientApi~Video}
   * @private
   */
  setCurrentVideo (video) {
    if (video !== this.video) {
      this.video = video;
      /**
       * videoChanged event. Emitted when current video has changed.
       * @event ClientApi#videoChanged
       * @param {ClientApi~Video}
       */
      this.emit('videoChanged', this.video);
    }
  }

  /**
   * Returns current state of the IPX
   * @returns {string} State name, for example 'before' or 'after' for Live Event IPX
   */
  getCurrentState () {
    return this.state;
  }

  /**
   * Saves current state. If the state has changed, emits 'stateChanged' event
   * @param {string} state State name
   * @private
   */
  setCurrentState (state) {
    if (state !== this.state) {
      this.state = state;
      /**
       * stateChanged event. Emitted when a state of the embed experience is changed,
       * for example from "Pre-event" to "Live".
       * @event ClientApi#stateChanged
       * @param {string} state. Currently supported states: before, during, after, playback.
       */
      this.emit('stateChanged', state);
    }
  }

  /**
   * Returns current list of all videos, available for the current state
   * @returns {Array.ClientApi~Video} Array of Video Metadata objects
   */
  getAllVideos () {
    return this.videos;
  }

  /**
   * Emits 'playerLoaded' event when player is initialized. Sends current player to all listeners
   * @private
   */
  playerLoadStart () {
    /**
     * playerLoaded event. Emitted when a player is loaded.
     * Can be generated multiple times for experiences that have multiple players.
     * @event ClientApi#playerLoaded
     * @param {ClientApi~Player}
     */
    this.emit('playerLoaded', this.player);
  }

  /**
   * Emits 'videoLoaded' event when video is loaded and ready to be played.
   * Sends current video to all listeners
   * @private
   */
  playerLoadedMetadata () {
    /**
     * videoLoaded event. Emitted when video is fully loaded and ready to play.
     * Can be generated multiple times when user changes the video
     * @event ClientApi#videoLoaded
     * @param {ClientApi~Video}
     */
    this.emit('videoLoaded', this.video);
  }

  /**
   * Emits 'videoStarted' event when playback is started or resumed.
   * Sends current video to all listeners
   * @private
   */
  videoStarted () {
    /**
     * videoStarted event. Emitted when a video playback is started by user
     * @event ClientApi#videoStarted
     * @param {ClientApi~Video}
     */
    this.emit('videoStarted', this.video);
  }

  /**
   * Emits 'videoPaused' event when playback is stopped or finished.
   * Sends current video to all listeners
   * @private
   */
  videoPaused () {
    /**
     * videoPaused event. Emitted when a video playback is paused by user
     * @event ClientApi#videoPaused
     * @param {ClientApi~Video}
     */
    this.emit('videoPaused', this.video);
  }

  /**
   * Extracts information from an interation object and creates
   * Interaction Event.
   * Adds information about current playback, like:
   *  - video id
   *  - video title
   *  - current playback time
   *
   * @param {Object} interaction IPX interaction object
   * @returns {ClientApi~InteractivityEvent}
   * @private
   */
  getInteractionEvent (interaction) {
    /**
    * Describes current status of an interactivity event. Includes information about the interactivity as well as it's position in the video
    * @typedef {Object} ClientApi~InteractivityEvent
    * @property {string} element - type of the interaction. For example, 'link', 'card', 'html', 'image'
    * @property {string} linkText - link text if applicable
    * @property {string} linkUrl - link URL if applicable
    * @property {number} videoId - current video id
    * @property {string} videoTitle - current video title
    * @property {number} videoTime - playback position for the current video
    */
    return {
      element: interaction.type,
      linkText: interaction.text || interaction.linkText,
      linkUrl: interaction.href,
      videoId: this.video ? this.video.id : '',
      videoTitle: this.video ? this.video.name : '',
      videoTime: this.player ? this.player.currentTime() : '',
    };
  }
  /**
   * Sendns 'interactionStart' event to all subscribers
   * @param {ClientApi~InteractivityEvent}
   * @private
   */
  interactionStarted (interaction) {
    /**
     * interactionStart event. Emitted when a video interactivity element, like card or a link appears in the video.
     * @event ClientApi#interactionStart
     * @param {ClientApi~InteractivityEvent}
     */
    this.emit('interactionStart', this.getInteractionEvent(interaction));
  }

  /**
   * Sendns 'interactionEnd' event to all subscribers
   * @param {ClientApi~InteractivityEvent}
   * @private
   */
  interactionEnded (interaction) {
    /**
     * interactionEnd event. Emitted when a video interactivity element, like card or a link disappears from the video.
     * @event ClientApi#interactionEnd
     * @param {ClientApi~InteractivityEvent}
     */
    this.emit('interactionEnd', this.getInteractionEvent(interaction));
  }

  /**
   * Sendns 'interactionClick' event to all subscribers
   * @param {ClientApi~InteractivityEvent}
   * @private
   */
  interactionClicked (interaction) {
    /**
     * interactionClick event. Emitted when user has clicked on an interactivity element.
     * This includes both video interactions as well as custom HTML and image links.
     * @event ClientApi#interactionClick
     * @param {ClientApi~InteractivityEvent}
     */
    this.emit('interactionClick', this.getInteractionEvent(interaction));
  }

  /**
   * Sendns 'interactionCardPanelOpen' event to all subscribers
   * @param {ClientApi~InteractivityEvent}
   * @private
   */
  interactionCardPanelOpen (interactions) {
    const interactionEvents = interactions.map(interaction => this.getInteractionEvent(interaction));
    /**
     * interactionCardPanelOpen event. Emitted when user clicked on the interactivity information link.
     * All active interactions become visible in the side panel.
     * @event ClientApi#interactionCardPanelOpen
     * @param {ClientApi~InteractivityEvent[]}
     */
    this.emit('interactionCardPanelOpen', interactionEvents);
  }

  /**
   * Sends 'interactionCardPanelClose' event to all subscribers
   * @param {ClientApi~InteractivityEvent}
   * @private
   */
  interactionCardPanelClose (interactions) {
    const interactionEvents = interactions.map(interaction => this.getInteractionEvent(interaction));
    /**
     * interactionCardPanelClose event. Emitted when user closed the interactivity information side panel.
     * @event ClientApi#interactionCardPanelClose
     * @param {ClientApi~InteractivityEvent[]}
     */
    this.emit('interactionCardPanelClose', interactionEvents);
  }
}

/**
 * Allows to register a callback for an embedded experience event.
 * @method ClientApi#on
 * @param {string} Name of the event.
 * @param {function} callback
 */

/**
 * Allows to register a one-time callback function for the event named eventName.
 * The next time eventName is triggered, this callback is removed and then invoked.
 * @method ClientApi#once
 * @param {string} Name of the event.
 * @param {function} callback
 */

/**
 * Removes the specified listener callback from the listener array for the event named eventName.
 * @method ClientApi#off
 * @param {string} Name of the event.
 * @param {function} callback
 */
module.exports = {
  ClientApi,
};