/**
 * @file menu.js
 */
import Component from '../component.js' ;
import document from 'global/document' ;
import * as Dom from '../utils/dom.js' ;
import * as Events from '../utils/events.js' ;
import keycode from 'keycode' ;

/**
 * Le composant Menu est utilisé pour construire des menus contextuels, y compris des menus de sous-titres et de
 * menus de sélection des sous-titres.
 *
 * @extends Component
 */
class Menu extends Component {

  /**
   * Créer une instance de cette classe.
   *
   * @param {Player} player
   *        le lecteur auquel ce composant doit s'attacher
   *
   * @param {Objet} [options]
   *        Objet de noms et de valeurs d'options
   *
   */
  constructor(player, options) {
    super(player, options) ;

    if (options) {
      this.menuButton_ = options.menuButton ;
    }

    this.focusedChild_ = -1 ;

    this.on('keydown', (e) => this.handleKeyDown(e)) ;

    // Toutes les instances d'éléments de menu partagent le même gestionnaire de flou fourni par le conteneur de menu.
    this.boundHandleBlur_ = (e) => this.handleBlur(e) ;
    this.boundHandleTapClick_ = (e) => this.handleTapClick(e) ;
  }

  /**
   * Ajouter des récepteurs d'événements à l'élément {@link MenuItem}.
   *
   * @param {Object} component
   *        L'instance du `MenuItem` à laquelle ajouter des listeners.
   *
   */
  addEventListenerForItem(component) {
    if ( !(component instanceof Component)) {
      retour ;
    }

    this.on(component, 'blur', this.boundHandleBlur_) ;
    this.on(component, ['tap', 'click'], this.boundHandleTapClick_) ;
  }

  /**
   * Supprime les récepteurs d'événements de l'élément {@link MenuItem}.
   *
   * @param {Object} component
   *        L'instance du `MenuItem` à supprimer des listeners.
   *
   */
  removeEventListenerForItem(component) {
    if ( !(component instanceof Component)) {
      retour ;
    }

    this.off(component, 'blur', this.boundHandleBlur_) ;
    this.off(component, ['tap', 'click'], this.boundHandleTapClick_) ;
  }

  /**
   * Cette méthode sera appelée indirectement lorsque le composant aura été ajouté
   * avant que le composant ne s'ajoute à la nouvelle instance de menu par `addItem`.
   * Dans ce cas, l'instance de menu d'origine supprimera le composant
   * en appelant `removeChild`.
   *
   * @param {Object} component
   *        L'instance de l'élément de menu `MenuItem`
   */
  removeChild(component) {
    if (typeof component === 'string') {
      component = this.getChild(component) ;
    }

    this.removeEventListenerForItem(component) ;
    super.removeChild(component) ;
  }

  /**
   * Ajoute un {@link MenuItem} au menu.
   *
   * @param {Objet|chaîne} composant
   *        Le nom ou l'instance du `MenuItem` à ajouter.
   *
   */
  addItem(component) {
    const childComponent = this.addChild(component) ;

    if (childComponent) {
      this.addEventListenerForItem(childComponent) ;
    }
  }

  /**
   * Créer l'élément DOM `Menu`.
   *
   * @return {Element}
   *         l'élément qui a été créé
   */
  createEl() {
    const contentElType = this.options_.contentElType || 'ul' ;

    this.contentEl_ = Dom.createEl(contentElType, {
      className : 'vjs-menu-content'
    }) ;

    this.contentEl_.setAttribute('role', 'menu') ;

    const el = super.createEl('div', {
      append : this.contentEl_,
      className : 'vjs-menu'
    }) ;

    el.appendChild(this.contentEl_) ;

    // Empêcher les clics de remonter. Nécessaire pour les boutons de menu,
    // lorsqu'un clic sur le parent est significatif
    Events.on(el, 'click', function(event) {
      event.preventDefault() ;
      event.stopImmediatePropagation() ;
    }) ;

    return el ;
  }

  dispose() {
    this.contentEl_ = null ;
    this.boundHandleBlur_ = null ;
    this.boundHandleTapClick_ = null ;

    super.dispose() ;
  }

  /**
   * Appelé lorsqu'un `MenuItem` perd le focus.
   *
   * @param {EventTarget~Event} event
   *        L'événement `blur` qui a provoqué l'appel de cette fonction.
   *
   * @listens blur
   */
  handleBlur(event) {
    const relatedTarget = event.relatedTarget || document.activeElement ;

    // Fermer le menu contextuel lorsque l'utilisateur clique en dehors du menu
    if (!this.children().some((element) => {
      return element.el() === relatedTarget ;
    })) {
      const btn = this.menuButton_ ;

      if (btn && btn.buttonPressed_ && relatedTarget !== btn.el().firstChild) {
        btn.unpressButton() ;
      }
    }
  }

  /**
   * Appelé lorsqu'un `MenuItem` est cliqué ou tapé.
   *
   * @param {EventTarget~Event} event
   *        L'événement `click` ou `tap` qui a provoqué l'appel de cette fonction.
   *
   * @listens click,tap
   */
  handleTapClick(event) {
    // Décrocher le bouton de menu associé et lui redonner la priorité
    if (this.menuButton_) {
      this.menuButton_.unpressButton() ;

      const childComponents = this.children() ;

      if (!Array.isArray(childComponents)) {
        retour ;
      }

      const foundComponent = childComponents.filter(component => component.el() === event.target)[0] ;

      if (!foundComponent) {
        retour ;
      }

      // ne pas mettre en évidence le bouton de menu si l'élément est un élément de réglage de légende
      // parce que le centre d'intérêt se déplacera ailleurs
      if (foundComponent.name() !== 'CaptionSettingsMenuItem') {
        this.menuButton_.focus() ;
      }
    }
  }

  /**
   * Gère un événement `keydown` sur ce menu. Cet écouteur est ajouté dans le constructeur.
   *
   * @param {EventTarget~Event} event
   *        Un événement `keydown` qui s'est produit dans le menu.
   *
   * @listens keydown
   */
  handleKeyDown(event) {

    // Flèches de gauche et de bas
    if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
      event.preventDefault() ;
      event.stopPropagation() ;
      this.stepForward() ;

    // Flèches vers le haut et vers la droite
    } else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
      event.preventDefault() ;
      event.stopPropagation() ;
      this.stepBack() ;
    }
  }

  /**
   * Permet de passer à l'élément de menu suivant (inférieur) pour les utilisateurs du clavier.
   */
  stepForward() {
    let stepChild = 0 ;

    if (this.focusedChild_ !== undefined) {
      stepChild = this.focusedChild_ + 1 ;
    }
    this.focus(stepChild) ;
  }

  /**
   * Permet de passer à l'élément de menu précédent (supérieur) pour les utilisateurs du clavier.
   */
  stepBack() {
    let stepChild = 0 ;

    if (this.focusedChild_ !== undefined) {
      stepChild = this.focusedChild_ - 1 ;
    }
    this.focus(stepChild) ;
  }

  /**
   * Mettre le focus sur un {@link MenuItem} dans le `Menu`.
   *
   * @param {Objet|chaîne} [item=0]
   *        Index de l'élément enfant sur lequel l'accent est mis.
   */
  focus(item = 0) {
    const children = this.children().slice() ;
    const haveTitle = children.length && children[0].hasClass('vjs-menu-title') ;

    if (haveTitle) {
      enfants.shift() ;
    }

    if (children.length > 0) {
      if (item < 0) {
        item = 0 ;
      } else if (item >= children.length) {
        item = children.length - 1 ;
      }

      this.focusedChild_ = item ;

      children[item].el_.focus() ;
    }
  }
}

Component.registerComponent('Menu', Menu) ;
exporter le menu par défaut ;