/**
 * @file menu-button.js
 */
import Button from '../button.js' ;
import Component from '../component.js' ;
import Menu from './menu.js' ;
import * as Dom from '../utils/dom.js' ;
import * as Events from '../utils/events.js' ;
import {toTitleCase} de '../utils/string-cases.js' ;
import { IS_IOS } from '../utils/browser.js' ;
import document from 'global/document' ;
import keycode from 'keycode' ;

/**
 * Une classe `MenuButton` pour n'importe quel {@link Menu} popup.
 *
 * @extends Component
 */
class MenuButton 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.menuButton_ = new Button(player, options) ;

    this.menuButton_.controlText(this.controlText_) ;
    this.menuButton_.el_.setAttribute('aria-haspopup', 'true') ;

    // Ajouter les valeurs de buildCSSClass au bouton, pas au wrapper
    const buttonClass = Button.prototype.buildCSSClass() ;

    this.menuButton_.el_.className = this.buildCSSClass() + ' ' + buttonClass ;
    this.menuButton_.removeClass('vjs-control') ;

    this.addChild(this.menuButton_) ;

    this.update() ;

    this.enabled_ = true ;

    const handleClick = (e) => this.handleClick(e) ;

    this.handleMenuKeyUp_ = (e) => this.handleMenuKeyUp(e) ;

    this.on(this.menuButton_, 'tap', handleClick) ;
    this.on(this.menuButton_, 'click', handleClick) ;
    this.on(this.menuButton_, 'keydown', (e) => this.handleKeyDown(e)) ;
    this.on(this.menuButton_, 'mouseenter', () => {
      this.addClass('vjs-hover') ;
      this.menu.show() ;
      Events.on(document, 'keyup', this.handleMenuKeyUp_) ;
    }) ;
    this.on('mouseleave', (e) => this.handleMouseLeave(e)) ;
    this.on('keydown', (e) => this.handleSubmenuKeyDown(e)) ;
  }

  /**
   * Mettre à jour le menu en fonction de l'état actuel de ses éléments.
   */
  update() {
    const menu = this.createMenu() ;

    if (this.menu) {
      this.menu.dispose() ;
      this.removeChild(this.menu) ;
    }

    this.menu = menu ;
    this.addChild(menu) ;

    /**
     * Suivre l'état du bouton de menu
     *
     * @type {Booléen}
     * @private
     */
    this.buttonPressed_ = false ;
    this.menuButton_.el_.setAttribute('aria-expanded', 'false') ;

    if (this.items && this.items.length <= this.hideThreshold_) {
      this.hide() ;
      this.menu.contentEl_.removeAttribute('role') ;

    } else {
      this.show() ;
      this.menu.contentEl_.setAttribute('role', 'menu') ;
    }
  }

  /**
   * Créez le menu et ajoutez-y tous les éléments.
   *
   * @return {Menu}
   *         Le menu construit
   */
  createMenu() {
    const menu = new Menu(this.player_, { menuButton : this }) ;

    /**
     * Masquer le menu si le nombre d'éléments est inférieur ou égal à ce seuil. Par défaut, il s'agit de
     * à 0 et chaque fois que nous ajouterons des éléments qui peuvent être cachés dans le menu, nous l'incrémenterons. Nous listons
     * car à chaque fois que nous lançons `createMenu`, nous devons réinitialiser la valeur.
     *
     * @protégé
     * @type {Nombre}
     */
    this.hideThreshold_ = 0 ;

    // Ajouter un élément de la liste des titres en haut de la page
    if (this.options_.title) {
      const titleEl = Dom.createEl('li', {
        className : 'vjs-menu-title',
        textContent : toTitleCase(this.options_.title),
        tabIndex : -1
      }) ;

      const titleComponent = new Component(this.player_, {el : titleEl}) ;

      menu.addItem(titleComponent) ;
    }

    this.items = this.createItems() ;

    if (this.items) {
      // Ajouter des éléments de menu au menu
      for (let i = 0 ; i < this.items.length ; i++) {
        menu.addItem(this.items[i]) ;
      }
    }

    menu de retour ;
  }

  /**
   * Créez la liste des éléments du menu. Spécifique à chaque sous-classe.
   *
   * @abstract
   */
  createItems() {}

  /**
   * Créez l'élément DOM `MenuButtons`.
   *
   * @return {Element}
   *         L'élément qui est créé.
   */
  createEl() {
    return super.createEl('div', {
      className : this.buildWrapperCSSClass()
    }, {
    }) ;
  }

  /**
   * Permettre aux sous-composants d'empiler les noms de classe CSS pour l'élément enveloppant
   *
   * @return {string}
   *         L'enveloppe DOM `className` construite
   */
  buildWrapperCSSClass() {
    let menuButtonClass = 'vjs-menu-button' ;

    // Si l'option inline est passée, nous voulons utiliser des styles différents.
    if (this.options_.inline === true) {
      menuButtonClass += '-inline' ;
    } else {
      menuButtonClass += '-popup' ;
    }

    // TODO : Corrigez le CSS pour que cela ne soit pas nécessaire
    const buttonClass = Button.prototype.buildCSSClass() ;

    return `vjs-menu-button ${menuButtonClass} ${buttonClass} ${super.buildCSSClass()}` ;
  }

  /**
   * Construit le DOM par défaut `className`.
   *
   * @return {string}
   *         Le `nom de classe` du DOM pour cet objet.
   */
  buildCSSClass() {
    let menuButtonClass = 'vjs-menu-button' ;

    // Si l'option inline est passée, nous voulons utiliser des styles différents.
    if (this.options_.inline === true) {
      menuButtonClass += '-inline' ;
    } else {
      menuButtonClass += '-popup' ;
    }

    return `vjs-menu-button ${menuButtonClass} ${super.buildCSSClass()}` ;
  }

  /**
   * Obtenir ou définir le texte de contrôle localisé qui sera utilisé pour l'accessibilité.
   *
   * > NOTE : Il provient de l'élément interne `menuButton_`.
   *
   * @param {string} [texte]
   *        Texte de contrôle pour l'élément.
   *
   * @param {Element} [el=this.menuButton_.el()]
   *        Élément sur lequel le titre doit être placé.
   *
   * @return {string}
   *         - Le texte du contrôle lors de l'obtention de
   */
  controlText(text, el = this.menuButton_.el()) {
    return this.menuButton_.controlText(text, el) ;
  }

  /**
   * Se débarrasse du `menu-button` et de tous ses composants enfants.
   */
  dispose() {
    this.handleMouseLeave() ;
    super.dispose() ;
  }

  /**
   * Gère un clic sur un `MenuButton`.
   * Voir {@link ClickableComponent#handleClick} pour les instances où cette fonction est appelée.
   *
   * @param {EventTarget~Event} event
   *        L'événement `keydown`, `tap` ou `click` qui a provoqué l'activation de cette fonction est le suivant
   *        appelé.
   *
   * @listens tap
   * @listens click
   */
  handleClick(event) {
    if (this.buttonPressed_) {
      this.unpressButton() ;
    } else {
      this.pressButton() ;
    }
  }

  /**
   * Gère le `mouseleave` pour le `MenuButton`.
   *
   * @param {EventTarget~Event} event
   *        L'événement `mouseleave` qui a provoqué l'appel de cette fonction.
   *
   * @listens mouseleave
   */
  handleMouseLeave(event) {
    this.removeClass('vjs-hover') ;
    Events.off(document, 'keyup', this.handleMenuKeyUp_) ;
  }

  /**
   * Mettre l'accent sur le bouton lui-même, et non sur cet élément
   */
  focus() {
    this.menuButton_.focus() ;
  }

  /**
   * Retirer le focus du bouton actuel, et non de cet élément
   */
  blur() {
    this.menuButton_.blur() ;
  }

  /**
   * Gère les touches de tabulation, d'échappement, de flèche vers le bas et de flèche vers le haut pour `MenuButton`. Voir
   * {@link ClickableComponent#handleKeyDown} pour les cas où cette fonction est appelée.
   *
   * @param {EventTarget~Event} event
   *        L'événement `keydown` qui a provoqué l'appel de cette fonction.
   *
   * @listens keydown
   */
  handleKeyDown(event) {

    // La touche Escape ou Tab permet d'appuyer sur le "bouton"
    if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
      if (this.buttonPressed_) {
        this.unpressButton() ;
      }

      // Ne pas utiliser preventDefault pour la touche Tab - nous voulons toujours perdre le focus
      if (!keycode.isEventKey(event, 'Tab')) {
        event.preventDefault() ;
        // Remettre le focus sur le bouton du menu
        this.menuButton_.focus() ;
      }
    // Flèche vers le haut ou Flèche vers le bas ; appuyez également sur le bouton pour ouvrir le menu
    } else if (keycode.isEventKey(event, 'Up') || keycode.isEventKey(event, 'Down')) {
      if (!this.buttonPressed_) {
        event.preventDefault() ;
        this.pressButton() ;
      }
    }
  }

  /**
   * Gère un événement `keyup` sur un `MenuButton`. L'écouteur pour cela est ajouté dans
   * le constructeur.
   *
   * @param {EventTarget~Event} event
   *        Événement presse-clé
   *
   * @listens keyup
   */
  handleMenuKeyUp(event) {
    // La touche Escape masque le menu contextuel
    if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
      this.removeClass('vjs-hover') ;
    }
  }

  /**
   * Le nom de cette méthode est désormais délégué à `handleSubmenuKeyDown`. Cela signifie que
   * toute personne appelant `handleSubmenuKeyPress` ne verra pas ses appels de méthode
   * cessent de fonctionner.
   *
   * @param {EventTarget~Event} event
   *        L'événement qui a provoqué l'appel de cette fonction.
   */
  handleSubmenuKeyPress(event) {
    this.handleSubmenuKeyDown(event) ;
  }

  /**
   * Gérer un événement `keydown` sur un sous-menu. L'écouteur pour cela est ajouté dans
   * le constructeur.
   *
   * @param {EventTarget~Event} event
   *        Événement presse-clé
   *
   * @listens keydown
   */
  handleSubmenuKeyDown(event) {
    // La touche Escape ou Tab permet d'appuyer sur le "bouton"
    if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
      if (this.buttonPressed_) {
        this.unpressButton() ;
      }
      // Ne pas utiliser preventDefault pour la touche Tab - nous voulons toujours perdre le focus
      if (!keycode.isEventKey(event, 'Tab')) {
        event.preventDefault() ;
        // Remettre le focus sur le bouton du menu
        this.menuButton_.focus() ;
      }
    } else {
      // NOTE : Il s'agit d'un cas particulier dans lequel nous ne transmettons pas les messages non traités de
      // événements keydown jusqu'au gestionnaire de composants, parce qu'il est
      // il s'agit simplement de gérer le keydown du `MenuItem`
      // dans le `Menu` qui transmet déjà les touches inutilisées vers le haut.
    }
  }

  /**
   * Place le `MenuButton` actuel dans un état pressé.
   */
  pressButton() {
    if (this.enabled_) {
      this.buttonPressed_ = true ;
      this.menu.show() ;
      this.menu.lockShowing() ;
      this.menuButton_.el_.setAttribute('aria-expanded', 'true') ;

      // place le focus dans le sous-menu, sauf sur iOS où cela se traduit par
      // comportement de défilement indésirable lorsque le lecteur se trouve dans une iframe
      if (IS_IOS && Dom.isInFrame()) {
        // Retour anticipé pour que le menu ne soit pas focalisé
        retour ;
      }

      ce.menu.focus() ;
    }
  }

  /**
   * Retire le `MenuButton` actuel de l'état pressé.
   */
  unpressButton() {
    if (this.enabled_) {
      this.buttonPressed_ = false ;
      this.menu.unlockShowing() ;
      this.menu.hide() ;
      this.menuButton_.el_.setAttribute('aria-expanded', 'false') ;
    }
  }

  /**
   * Désactive le bouton de menu. Ne permettez pas de cliquer dessus.
   */
  disable() {
    this.unpressButton() ;

    this.enabled_ = false ;
    this.addClass('vjs-disabled') ;

    this.menuButton_.disable() ;
  }

  /**
   * Active le `MenuButton`. Autorisez le clic dessus.
   */
  enable() {
    this.enabled_ = true ;
    this.removeClass('vjs-disabled') ;

    this.menuButton_.enable() ;
  }
}

Component.registerComponent('MenuButton', MenuButton) ;
exporter le bouton de menu par défaut ;