/**
 * @file text-track-settings.js
 */
import window from 'global/window' ;
import Component from '../component' ;
import ModalDialog from '../modal-dialog' ;
import {createEl} from '../utils/dom' ;
import * as Obj from '../utils/obj' ;
import log from '../utils/log' ;

const LOCAL_STORAGE_KEY = 'vjs-text-track-settings' ;

const COLOR_BLACK = ['#000', 'Black'] ;
const COLOR_BLUE = ['#00F', 'Blue'] ;
const COLOR_CYAN = ['#0FF', 'Cyan'] ;
const COLOR_GREEN = ['#0F0', 'Green'] ;
const COLOR_MAGENTA = ['#F0F', 'Magenta'] ;
const COLOR_RED = ['#F00', 'Red'] ;
const COLOR_WHITE = ['#FFF', 'White'] ;
const COLOR_YELLOW = ['#FF0', 'Yellow'] ;

const OPACITY_OPAQUE = ['1', 'Opaque'] ;
const OPACITY_SEMI = ['0.5', 'Semi-Transparent'] ;
const OPACITY_TRANS = ['0', 'Transparent'] ;

// Configuration des différents éléments <select> dans le DOM de ce composant.
//
// Les clés possibles sont les suivantes :
//
// `default` :
// L'indice de l'option par défaut. Ne doit être fourni que s'il n'est pas nul.
// `parser` :
// Une fonction qui est utilisée pour analyser la valeur de l'option sélectionnée en
// une manière personnalisée.
// `selector` :
// Le sélecteur utilisé pour trouver l'élément <select> associé.
const selectConfigs = {
  backgroundColor : {
    selector : '.vjs-bg-color > select',
    id : 'captions-background-color-%s',
    étiquette : couleur",
    options : [
      COLOR_BLACK,
      COLOR_WHITE,
      COLOR_RED,
      COLOR_GREEN,
      COLOR_BLUE,
      COLOR_YELLOW,
      COLOR_MAGENTA,
      COLOR_CYAN
    ]
  },

  backgroundOpacity : {
    selector : '.vjs-bg-opacity > select',
    id : "captions-background-opacity-%s",
    étiquette : transparence",
    options : [
      OPACITY_OPAQUE,
      OPACITY_SEMI,
      OPACITY_TRANS
    ]
  },

  color : {
    selector : '.vjs-fg-color > select',
    id : 'captions-foreground-color-%s',
    étiquette : couleur",
    options : [
      COLOR_WHITE,
      COLOR_BLACK,
      COLOR_RED,
      COLOR_GREEN,
      COLOR_BLUE,
      COLOR_YELLOW,
      COLOR_MAGENTA,
      COLOR_CYAN
    ]
  },

  edgeStyle : {
    selector : '.vjs-edge-style > select',
    id : '%s',
    étiquette : style de bord de texte",
    options : [
      ['none', 'None'],
      ["élevé", "élevé"],
      ["depressed", "Depressed"],
      ['uniforme', 'Uniforme'],
      ['dropshadow', 'Dropshadow']
    ]
  },

  fontFamily : {
    selector : '.vjs-font-family > select',
    id : 'captions-font-family-%s',
    étiquette : famille de polices",
    options : [
      ["proportionalSansSerif", "Proportional Sans-Serif"],
      ['monospaceSansSerif', 'Monospace Sans-Serif'],
      ["proportionalSerif", "Proportional Serif"],
      ['monospaceSerif', 'Monospace Serif'],
      ['casual', 'Casual'],
      ['script', 'Script'],
      ['small-caps', 'Small Caps']
    ]
  },

  fontPercent : {
    selector : '.vjs-font-percent > select',
    id : 'captions-font-size-%s',
    étiquette : taille de la police",
    options : [
      ['0.50', '50%'],
      ['0.75', '75%'],
      ['1.00', '100%'],
      ['1.25', '125%'],
      ['1.50', '150%'],
      ['1.75', '175%'],
      ['2.00', '200%'],
      ['3.00', '300%'],
      ['4.00', '400%']
    ],
    par défaut : 2,
    parser : (v) => v === '1.00' ? null : Nombre(v)
  },

  textOpacity : {
    selector : '.vjs-text-opacity > select',
    id : "captions-foreground-opacity-%s",
    étiquette : transparence",
    options : [
      OPACITY_OPAQUE,
      OPACITY_SEMI
    ]
  },

  // Les options de cet objet sont définies ci-dessous.
  windowColor : {
    selector : '.vjs-window-color > select',
    id : 'captions-window-color-%s',
    étiquette : couleur
  },

  // Les options de cet objet sont définies ci-dessous.
  windowOpacity : {
    selector : '.vjs-window-opacity > select',
    id : 'captions-window-opacity-%s',
    étiquette : transparence",
    options : [
      OPACITY_TRANS,
      OPACITY_SEMI,
      OPACITY_OPAQUE
    ]
  }
};

selectConfigs.windowColor.options = selectConfigs.backgroundColor.options ;

/**
 * Obtenir la valeur réelle d'une option.
 *
 * @param {string} value
 *         La valeur à obtenir
 *
 * @param {Fonction} [parser]
 *         Fonction optionnelle pour ajuster la valeur.
 *
 * @return {Mixed}
 *         - Sera `indéfini` si aucune valeur n'existe
 *         - Sera `undefined` si la valeur donnée est "none".
 *         - Dans le cas contraire, il s'agit de la valeur réelle.
 *
 * @private
 */
function parseOptionValue(value, parser) {
  if (parser) {
    valeur = parser(valeur) ;
  }

  if (value && value !== 'none') {
    valeur de retour ;
  }
}

/**
 * Obtient la valeur de l'élément <option> sélectionné dans un élément <select> .
 *
 * @param {Element} el
 *         l'élément à rechercher
 *
 * @param {Fonction} [parser]
 *         Fonction optionnelle pour ajuster la valeur.
 *
 * @return {Mixed}
 *         - Sera `indéfini` si aucune valeur n'existe
 *         - Sera `undefined` si la valeur donnée est "none".
 *         - Dans le cas contraire, il s'agit de la valeur réelle.
 *
 * @private
 */
function getSelectedOptionValue(el, parser) {
  const value = el.options[el.options.selectedIndex].value ;

  return parseOptionValue(value, parser) ;
}

/**
 * Définit l'élément <option> sélectionné dans un élément <select> sur la base d'un élément
 * valeur donnée.
 *
 * @param {Element} el
 *        L'élément à examiner.
 *
 * @param {string} value
 *        la propriété à regarder.
 *
 * @param {Fonction} [parser]
 *        Fonction optionnelle permettant d'ajuster la valeur avant la comparaison.
 *
 * @private
 */
function setSelectedOption(el, value, parser) {
  if (!value) {
    retour ;
  }

  for (let i = 0 ; i < el.options.length ; i++) {
    if (parseOptionValue(el.options[i].value, parser) === value) {
      el.selectedIndex = i ;
      pause ;
    }
  }
}

/**
 * Manipuler les paramètres des pistes de texte.
 *
 * @extends ModalDialog
 */
class TextTrackSettings extends ModalDialog {

  /**
   * 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) {
    options.temporary = false ;

    super(player, options) ;
    this.updateDisplay = this.updateDisplay.bind(this) ;

    // remplir la fenêtre modale et faire semblant de l'avoir ouverte
    this.fill() ;
    this.hasBeenOpened_ = this.hasBeenFilled_ = true ;

    this.endDialog = createEl('p', {
      className : 'vjs-control-text',
      textContent : this.localize('Fin de la fenêtre de dialogue.')
    }) ;
    this.el().appendChild(this.endDialog) ;

    this.setDefaults() ;

    // Récupère `persistTextTrackSettings` des options du lecteur s'il n'est pas passé dans les options de l'enfant
    if (options.persistTextTrackSettings === undefined) {
      this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings ;
    }

    this.on(this.$('.vjs-done-button'), 'click', () => {
      this.saveSettings() ;
      this.close() ;
    }) ;

    this.on(this.$('.vjs-default-button'), 'click', () => {
      this.setDefaults() ;
      this.updateDisplay() ;
    }) ;

    Obj.each(selectConfigs, config => {
      this.on(this.$(config.selector), 'change', this.updateDisplay) ;
    }) ;

    if (this.options_.persistTextTrackSettings) {
      this.restoreSettings() ;
    }
  }

  dispose() {
    this.endDialog = null ;

    super.dispose() ;
  }

  /**
   * Créer un élément <select> avec des options configurées.
   *
   * @param {string} key
   *        Clé de configuration à utiliser lors de la création.
   *
   * @return {string}
   *         Une chaîne HTML.
   *
   * @private
   */
  createElSelect_(key, legendId = '', type = 'label') {
    const config = selectConfigs[key] ;
    const id = config.id.replace('%s', this.id_) ;
    const selectLabelledbyIds = [legendId, id].join(' ').trim() ;

    retour [
      `<${type} id="${id}" class="${type === 'label' ? 'vjs-label' : ''}">`,
      this.localize(config.label),
      `</${type}>`,
      `<select aria-labelledby="${selectLabelledbyIds}">`
    ].
      concat(config.options.map(o => {
        const optionId = id + '-' + o[1].replace(/\W+/g, '') ;

        retour [
          `<option id="${optionId}" value="${o[0]}" `,
          `aria-labelledby="${selectLabelledbyIds} ${optionId}">`,
          this.localize(o[1]),
          '</option>'
        ].join('') ;
      })).
      concat('</select>').join('') ;
  }

  /**
   * Créer un élément de couleur de premier plan pour le composant
   *
   * @return {string}
   *         Une chaîne HTML.
   *
   * @private
   */
  createElFgColor_() {
    const legendId = `captions-text-legend-${this.id_}` ;

    retour [
      '<fieldset class="vjs-fg-color vjs-track-setting">',
      `<legend id= "${legendId}">`,
      this.localize('Text'),
      </legend>',
      this.createElSelect_('color', legendId),
      '<span class="vjs-text-opacity vjs-opacity">',
      this.createElSelect_('textOpacity', legendId),
      '</span>',
      '</fieldset>'
    ].join('') ;
  }

  /**
   * Créer un élément de couleur d'arrière-plan pour le composant
   *
   * @return {string}
   *         Une chaîne HTML.
   *
   * @private
   */
  createElBgColor_() {
    const legendId = `captions-background-${this.id_}` ;

    retour [
      '<fieldset class="vjs-bg-color vjs-track-setting">',
      `<legend id= "${legendId}">`,
      this.localize('Background'),
      </legend>',
      this.createElSelect_('backgroundColor', legendId),
      '<span class="vjs-bg-opacity vjs-opacity">',
      this.createElSelect_('backgroundOpacity', legendId),
      '</span>',
      '</fieldset>'
    ].join('') ;
  }

  /**
   * Créer un élément de couleur de fenêtre pour le composant
   *
   * @return {string}
   *         Une chaîne HTML.
   *
   * @private
   */
  createElWinColor_() {
    const legendId = `captions-window-${this.id_}` ;

    retour [
      '<fieldset class="vjs-window-color vjs-track-setting">',
      `<legend id= "${legendId}">`,
      this.localize('Window'),
      </legend>',
      this.createElSelect_('windowColor', legendId),
      '<span class="vjs-window-opacity vjs-opacity">',
      this.createElSelect_('windowOpacity', legendId),
      '</span>',
      '</fieldset>'
    ].join('') ;
  }

  /**
   * Créer des éléments de couleur pour le composant
   *
   * @return {Element}
   *         L'élément qui a été créé
   *
   * @private
   */
  createElColors_() {
    return createEl('div', {
      className : 'vjs-track-settings-colors',
      innerHTML : [
        this.createElFgColor_(),
        this.createElBgColor_(),
        this.createElWinColor_()
      ].join('')
    }) ;
  }

  /**
   * Créer des éléments de police pour le composant
   *
   * @return {Element}
   *         L'élément qui a été créé.
   *
   * @private
   */
  createElFont_() {
    return createEl('div', {
      className : 'vjs-track-settings-font',
      innerHTML : [
        '<fieldset class="vjs-font-percent vjs-track-setting">',
        this.createElSelect_('fontPercent', '', 'legend'),
        '</fieldset>',
        '<fieldset class="vjs-edge-style vjs-track-setting">',
        this.createElSelect_('edgeStyle', '', 'legend'),
        '</fieldset>',
        '<fieldset class="vjs-font-family vjs-track-setting">',
        this.createElSelect_('fontFamily', '', 'legend'),
        '</fieldset>'
      ].join('')
    }) ;
  }

  /**
   * Créer des contrôles pour le composant
   *
   * @return {Element}
   *         L'élément qui a été créé.
   *
   * @private
   */
  createElControls_() {
    const defaultsDescription = this.localize('restaurer tous les paramètres aux valeurs par défaut') ;

    return createEl('div', {
      className : 'vjs-track-settings-controls',
      innerHTML : [
        `<button type="button" class="vjs-default-button" title="${defaultsDescription}">`,
        this.localize('Reset'),
        `<span class="vjs-control-text"> ${defaultsDescription}</span>`,
        '</button>',
        `<button type="button" class="vjs-done-button">${this.localize('Done')}</button>`
      ].join('')
    }) ;
  }

  content() {
    retour [
      this.createElColors_(),
      this.createElFont_(),
      this.createElControls_()
    ] ;
  }

  label() {
    return this.localize('Boîte de dialogue des paramètres de légende') ;
  }

  description() {
    return this.localize('Début de la fenêtre de dialogue. Escape annulera et fermera la fenêtre") ;
  }

  buildCSSClass() {
    return super.buildCSSClass() + ' vjs-text-track-settings' ;
  }

  /**
   * Obtient un objet contenant les paramètres de la piste de texte (ou null).
   *
   * @return {Object}
   *         Un objet contenant des valeurs de configuration analysées à partir du DOM ou du localStorage.
   */
  getValues() {
    return Obj.reduce(selectConfigs, (accum, config, key) => {
      const value = getSelectedOptionValue(this.$(config.selector), config.parser) ;

      if (value !== undefined) {
        accum[key] = value ;
      }

      retour accum ;
    }, {}) ;
  }

  /**
   * Définit les paramètres de la piste de texte à partir d'un objet de valeurs.
   *
   * @param {Object} values
   *        Un objet contenant des valeurs de configuration analysées à partir du DOM ou de localStorage.
   */
  setValues(values) {
    Obj.each(selectConfigs, (config, key) => {
      setSelectedOption(this.$(config.selector), values[key], config.parser) ;
    }) ;
  }

  /**
   * Remet tous les éléments `<select>` à leur valeur par défaut.
   */
  setDefaults() {
    Obj.each(selectConfigs, (config) => {
      const index = config.hasOwnProperty('default') ? config.default : 0 ;

      this.$(config.selector).selectedIndex = index ;
    }) ;
  }

  /**
   * Restaurer les paramètres de la piste de texte à partir de localStorage
   */
  restoreSettings() {
    laisser les valeurs ;

    essayez {
      values = JSON.parse(window.localStorage.getItem(LOCAL_STORAGE_KEY)) ;
    } catch (err) {
      log.warn(err) ;
    }

    if (values) {
      this.setValues(values) ;
    }
  }

  /**
   * Enregistrer les paramètres de la piste de texte dans localStorage
   */
  saveSettings() {
    if (!this.options_.persistTextTrackSettings) {
      retour ;
    }

    const values = this.getValues() ;

    essayez {
      if (Object.keys(values).length) {
        window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values)) ;
      } else {
        window.localStorage.removeItem(LOCAL_STORAGE_KEY) ;
      }
    } catch (err) {
      log.warn(err) ;
    }
  }

  /**
   * Mise à jour de l'affichage des paramètres de la piste de texte
   */
  updateDisplay() {
    const ttDisplay = this.player_.getChild('textTrackDisplay') ;

    if (ttDisplay) {
      ttDisplay.updateDisplay() ;
    }
  }

  /**
   * floutage conditionnel de l'élément et recentrage du bouton des légendes
   *
   * @private
   */
  conditionalBlur_() {
    this.previouslyActiveEl_ = null ;

    const cb = this.player_.controlBar ;
    const subsCapsBtn = cb && cb.subsCapsButton ;
    const ccBtn = cb && cb.captionsButton ;

    if (subsCapsBtn) {
      subsCapsBtn.focus() ;
    } else if (ccBtn) {
      ccBtn.focus() ;
    }
  }

}

Component.registerComponent('TextTrackSettings', TextTrackSettings) ;

exporter les paramètres par défaut de TextTrackSettings ;