/**
 * @file progress-control.js
 */
import Component from '../../component.js' ;
import * as Dom from '../../utils/dom.js' ;
import clamp from '../../utils/clamp.js' ;
import {bind, throttle, UPDATE_REFRESH_INTERVAL} from '../../utils/fn.js' ;
import {silencePromesse} de '../../utils/promesse' ;

import './seek-bar.js' ;

/**
 * Le composant Progress Control contient la barre de recherche, la progression du chargement,
 * et la progression du jeu.
 *
 * @extends Component
 */
class ProgressControl extends Component {

  /**
   * Crée une instance de cette classe.
   *
   * @param {Player} player
   *        Le `Player` auquel cette classe doit être attachée.
   *
   * @param {Objet} [options]
   *        La mémoire clé/valeur des options du lecteur.
   */
  constructor(player, options) {
    super(player, options) ;
    this.handleMouseMove = throttle(bind(this, this.handleMouseMove), UPDATE_REFRESH_INTERVAL) ;
    this.throttledHandleMouseSeek = throttle(bind(this, this.handleMouseSeek), UPDATE_REFRESH_INTERVAL) ;
    this.handleMouseUpHandler_ = (e) => this.handleMouseUp(e) ;
    this.handleMouseDownHandler_ = (e) => this.handleMouseDown(e) ;

    this.enable() ;
  }

  /**
   * Créer l'élément DOM du `Composant`
   *
   * @return {Element}
   *         L'élément qui a été créé.
   */
  createEl() {
    return super.createEl('div', {
      className : 'vjs-progress-control vjs-control'
    }) ;
  }

  /**
   * Lorsque la souris passe au-dessus du `ProgressControl`, la position du pointeur
   * est transmise au composant `MouseTimeDisplay`.
   *
   * @param {EventTarget~Event} event
   *        L'événement `mousemove` qui a provoqué l'exécution de cette fonction.
   *
   * @listen mousemove
   */
  handleMouseMove(event) {
    const seekBar = this.getChild('seekBar') ;

    if (!seekBar) {
      retour ;
    }

    const playProgressBar = seekBar.getChild('playProgressBar') ;
    const mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay') ;

    if (!playProgressBar && !mouseTimeDisplay) {
      retour ;
    }

    const seekBarEl = seekBar.el() ;
    const seekBarRect = Dom.findPosition(seekBarEl) ;
    let seekBarPoint = Dom.getPointerPosition(seekBarEl, event).x ;

    // Le skin par défaut a un espace de chaque côté de la `SeekBar`. Cela signifie que
    // qu'il est possible de déclencher ce comportement en dehors des limites de la catégorie
    // la `SeekBar`. Cela nous permet de nous y tenir à tout moment.
    seekBarPoint = clamp(seekBarPoint, 0, 1) ;

    if (mouseTimeDisplay) {
      mouseTimeDisplay.update(seekBarRect, seekBarPoint) ;
    }

    if (playProgressBar) {
      playProgressBar.update(seekBarRect, seekBar.getProgress()) ;
    }

  }

  /**
   * Une version réduite de l'écouteur {@link ProgressControl#handleMouseSeek}.
   *
   * @method ProgressControl#throttledHandleMouseSeek
   * @param {EventTarget~Event} event
   *        L'événement `mousemove` qui a provoqué l'exécution de cette fonction.
   *
   * @listen mousemove
   * @listen touchmove
   */

  /**
   * Gère les événements `mousemove` ou `touchmove` sur le `ProgressControl`.
   *
   * @param {EventTarget~Event} event
   *        événement `mousedown` ou `touchstart` qui a déclenché cette fonction
   *
   * @listens mousemove
   * @listens touchmove
   */
  handleMouseSeek(event) {
    const seekBar = this.getChild('seekBar') ;

    if (seekBar) {
      seekBar.handleMouseMove(event) ;
    }
  }

  /**
   * Des contrôles sont actuellement activés pour ce contrôle de progression.
   *
   * @return {boolean}
   *         true si les contrôles sont activés, false sinon
   */
  enabled() {
    return this.enabled_ ;
  }

  /**
   * Désactiver tous les contrôles sur le contrôle de progression et ses enfants
   */
  disable() {
    this.children().forEach((child) => child.disable && child.disable()) ;

    if (!this.enabled()) {
      retour ;
    }

    this.off(['mousedown', 'touchstart'], this.handleMouseDownHandler_) ;
    this.off(this.el_, 'mousemove', this.handleMouseMove) ;

    this.removeListenersAddedOnMousedownAndTouchstart() ;

    this.addClass('disabled') ;

    this.enabled_ = false ;

    // Rétablir l'état normal de la lecture si les commandes sont désactivées pendant le balayage
    if (this.player_.scrubbing()) {
      const seekBar = this.getChild('seekBar') ;

      this.player_.scrubbing(false) ;

      if (seekBar.videoWasPlaying) {
        silencePromesse(this.player_.play()) ;
      }
    }
  }

  /**
   * Activer tous les contrôles sur le contrôle de progression et ses enfants
   */
  enable() {
    this.children().forEach((child) => child.enable && child.enable()) ;

    if (this.enabled()) {
      retour ;
    }

    this.on(['mousedown', 'touchstart'], this.handleMouseDownHandler_) ;
    this.on(this.el_, 'mousemove', this.handleMouseMove) ;
    this.removeClass('disabled') ;

    this.enabled_ = true ;
  }

  /**
   * Écouteurs de nettoyage après que l'utilisateur a fini d'interagir avec les contrôles de progression
   */
  removeListenersAddedOnMousedownAndTouchstart() {
    const doc = this.el_.ownerDocument ;

    this.off(doc, 'mousemove', this.throttledHandleMouseSeek) ;
    this.off(doc, 'touchmove', this.throttledHandleMouseSeek) ;
    this.off(doc, 'mouseup', this.handleMouseUpHandler_) ;
    this.off(doc, 'touchend', this.handleMouseUpHandler_) ;
  }

  /**
   * Gère les événements `mousedown` ou `touchstart` sur le `ProgressControl`.
   *
   * @param {EventTarget~Event} event
   *        événement `mousedown` ou `touchstart` qui a déclenché cette fonction
   *
   * @listens mousedown
   * @listens touchstart
   */
  handleMouseDown(event) {
    const doc = this.el_.ownerDocument ;
    const seekBar = this.getChild('seekBar') ;

    if (seekBar) {
      seekBar.handleMouseDown(event) ;
    }

    this.on(doc, 'mousemove', this.throttledHandleMouseSeek) ;
    this.on(doc, 'touchmove', this.throttledHandleMouseSeek) ;
    this.on(doc, 'mouseup', this.handleMouseUpHandler_) ;
    this.on(doc, 'touchend', this.handleMouseUpHandler_) ;
  }

  /**
   * Gère les événements `mouseup` ou `touchend` sur le `ProgressControl`.
   *
   * @param {EventTarget~Event} event
   *        l'événement `mouseup` ou `touchend` qui a déclenché cette fonction.
   *
   * @listens touchend
   * @listens mouseup
   */
  handleMouseUp(event) {
    const seekBar = this.getChild('seekBar') ;

    if (seekBar) {
      seekBar.handleMouseUp(event) ;
    }

    this.removeListenersAddedOnMousedownAndTouchstart() ;
  }
}

/**
 * Options par défaut pour `ProgressControl` (contrôle de progression)
 *
 * @type {Objet}
 * @private
 */
ProgressControl.prototype.options_ = {
  enfants : [
    seekBar" (barre de recherche)
  ]
};

Component.registerComponent('ProgressControl', ProgressControl) ;
export default ProgressControl ;