/**
 * @file text-track-display.js
 */
import Component from '../component' ;
import * as Fn from '../utils/fn.js' ;
import * as Dom from '../utils/dom.js' ;
import window from 'global/window' ;

const darkGray = '#222' ;
const lightGray = '#ccc' ;
const fontMap = {
  monospace : "monospace",
  sansSerif : "sans-serif",
  serif : "serif",
  monospaceSansSerif : '"Andale Mono", "Lucida Console", monospace",
  monospaceSerif : '"Courier New", monospace",
  proportionalSansSerif : "sans-serif",
  proportionalSerif : "serif",
  casual : '"Comic Sans MS", Impact, fantasy",
  script : '"Monotype Corsiva", cursive",
  smallcaps : '"Andale Mono", "Lucida Console", monospace, sans-serif
};

/**
 * Construit une couleur rgba à partir d'un code couleur hexadécimal donné.
 *
 * @param {number} color
 *        Numéro hexadécimal de la couleur, comme #f0e ou #f604e2.
 *
 * @param {number} opacity
 *        Valeur de l'opacité, 0,0 - 1,0.
 *
 * @return {string}
 *         La couleur rgba qui a été créée, comme 'rgba(255, 0, 0, 0.3)'.
 */
export function constructColor(color, opacity) {
  laissez hex ;

  if (color.length === 4) {
    // la couleur ressemble à "#f0e"
    hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3] ;
  } else if (color.length === 7) {
    // la couleur ressemble à "#f604e2"
    hex = color.slice(1) ;
  } else {
    throw new Error('Code de couleur invalide fourni, ' + color + ' ; doit être formaté comme par exemple #f0e ou #f604e2.') ;
  }
  return 'rgba(' +
    parseInt(hex.slice(0, 2), 16) + ',' +
    parseInt(hex.slice(2, 4), 16) + ',' +
    parseInt(hex.slice(4, 6), 16) + ',' +
    opacity + ')' ;
}

/**
 * Essayer de mettre à jour le style d'un élément DOM. Certains changements de style entraîneront une erreur,
 * en particulier dans IE8. Ce devrait être des noops.
 *
 * @param {Element} el
 *        L'élément DOM à styliser.
 *
 * @param {string} style
 *        La propriété CSS de l'élément qui doit être stylisée.
 *
 * @param {string} rule
 *        Règle de style à appliquer à la propriété.
 *
 * @private
 */
function tryUpdateStyle(el, style, rule) {
  essayez {
    el.style[style] = rule ;
  } catch (e) {

    // Satisfait le linter.
    retour ;
  }
}

/**
 * Composant permettant d'afficher les repères de la piste de texte.
 *
 * @extends Component
 */
class TextTrackDisplay 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.
   *
   * @param {Component~ReadyCallback} [ready]
   *        La fonction à appeler lorsque `TextTrackDisplay` est prêt.
   */
  constructor(player, options, ready) {
    super(player, options, ready) ;

    const updateDisplayHandler = (e) => this.updateDisplay(e) ;

    player.on('loadstart', (e) => this.toggleDisplay(e)) ;
    player.on('texttrackchange', updateDisplayHandler) ;
    player.on('loadedmetadata', (e) => this.preselectTrack(e)) ;

    // Cette fonction était appelée lors de l'initialisation du joueur, mais provoquait une erreur
    // si une piste doit être affichée par défaut et que l'affichage n'a pas encore été chargé.
    // Devrait probablement être déplacé vers un chargeur de piste externe lorsque nous prendrons en charge
    // les pistes qui n'ont pas besoin d'être affichées.
    player.ready(Fn.bind(this, function() {
      if (player.tech_ && player.tech_.featuresNativeTextTracks) {
        this.hide() ;
        retour ;
      }

      player.on('fullscreenchange', updateDisplayHandler) ;
      player.on('playerresize', updateDisplayHandler) ;

      window.addEventListener('orientationchange', updateDisplayHandler) ;
      player.on('dispose', () => window.removeEventListener('orientationchange', updateDisplayHandler)) ;

      const tracks = this.options_.playerOptions.tracks || [] ;

      for (let i = 0 ; i < tracks.length ; i++) {
        this.player_.addRemoteTextTrack(tracks[i], true) ;
      }

      this.preselectTrack() ;
    })) ;
  }

  /**
  * Présélectionner une piste en respectant cette priorité :
  * - correspond à la langue et au type du {@link TextTrack} précédemment sélectionné
  * - correspond uniquement à la langue du {@link TextTrack} précédemment sélectionné
  * - est la première piste de légendes par défaut
  * - est la première piste de description par défaut
  *
  * @listens Player#loadstart
  */
  preselectTrack() {
    const modes = {captions : 1, sous-titres : 1} ;
    const trackList = this.player_.textTracks() ;
    const userPref = this.player_.cache_.selectedLanguage ;
    let firstDesc ;
    let firstCaptions ;
    let preferredTrack ;

    for (let i = 0 ; i < trackList.length ; i++) {
      const track = trackList[i] ;

      si (
        userPref && userPref.enabled &&
        userPref.language && userPref.language === track.language &&
        type de voie dans les modes
      ) {
        // Choisissez toujours la piste qui correspond à la langue et au genre
        if (track.kind === userPref.kind) {
          preferredTrack = track ;
        // ou choisir la première piste qui correspond à la langue
        } else if (!preferredTrack) {
          preferredTrack = track ;
        }

      // tout effacer si l'élément de menu offTextTrack a été cliqué
      } else if (userPref && !userPref.enabled) {
        piste préférée = null ;
        firstDesc = null ;
        firstCaptions = null ;

      } else if (track.default) {
        if (track.kind === 'descriptions' && !firstDesc) {
          firstDesc = track ;
        } else if (track.kind in modes && !firstCaptions) {
          firstCaptions = track ;
        }
      }
    }

    // La piste préférée correspond à la préférence de l'utilisateur et prend le nom de "piste préférée"
    // priorité sur toutes les autres pistes.
    // Ainsi, afficher la piste préférée avant la première piste par défaut
    // et la piste des sous-titres/captions avant la piste des descriptions
    if (preferredTrack) {
      preferredTrack.mode = 'showing' ;
    } else if (firstCaptions) {
      firstCaptions.mode = "showing" ;
    } else if (firstDesc) {
      firstDesc.mode = 'showing' ;
    }
  }

  /**
   * Transforme l'affichage des {@link TextTrack} de l'état actuel en l'autre état.
   * Il n'y a que deux États :
   * - montré
   * - caché
   *
   * @listens Player#loadstart
   */
  toggleDisplay() {
    if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
      this.hide() ;
    } else {
      this.show() ;
    }
  }

  /**
   * Crée l'élément DOM du {@link Component}.
   *
   * @return {Element}
   *         L'élément qui a été créé.
   */
  createEl() {
    return super.createEl('div', {
      className : 'vjs-text-track-display'
    }, {
      traduire" : "oui",
      aria-live" : "off",
      'aria-atomic' : 'true'
    }) ;
  }

  /**
   * Efface tous les {@link TextTrack} affichés.
   */
  clearDisplay() {
    if (typeof window.WebVTT === 'function') {
      window.WebVTT.processCues(window, [], this.el_) ;
    }
  }

  /**
   * Mettre à jour la piste de texte affichée lorsqu'un {@link Player#texttrackchange} ou un {@link Player#texttrack change} est affiché
   * un {@link Player#fullscreenchange} est déclenché.
   *
   * @listens Player#texttrackchange
   * @listens Player#fullscreenchange
   */
  updateDisplay() {
    const tracks = this.player_.textTracks() ;
    const allowMultipleShowingTracks = this.options_.allowMultipleShowingTracks ;

    this.clearDisplay() ;

    if (allowMultipleShowingTracks) {
      const showingTracks = [] ;

      for (let i = 0 ; i < tracks.length ; ++i) {
        const track = tracks[i] ;

        if (track.mode !== 'showing') {
          continuer ;
        }
        showingTracks.push(track) ;
      }
      this.updateForTrack(showingTracks) ;
      retour ;
    }

    // Modèle de priorité d'affichage des pistes : si plusieurs pistes sont "affichées",
    // afficher la première piste de "sous-titres" ou de "sous-titres" qui est "montrée",
    // sinon, afficher la première piste "descriptions" qui est "montrée"

    let descriptionsTrack = null ;
    let captionsSubtitlesTrack = null ;
    let i = tracks.length ;

    while (i--) {
      const track = tracks[i] ;

      if (track.mode === 'showing') {
        if (track.kind === 'descriptions') {
          descriptionsTrack = track ;
        } else {
          captionsSubtitlesTrack = track ;
        }
      }
    }

    if (captionsSubtitlesTrack) {
      if (this.getAttribute('aria-live') !== 'off') {
        this.setAttribute('aria-live', 'off') ;
      }
      this.updateForTrack(captionsSubtitlesTrack) ;
    } else if (descriptionsTrack) {
      if (this.getAttribute('aria-live') !== 'assertive') {
        this.setAttribute('aria-live', 'assertive') ;
      }
      this.updateForTrack(descriptionsTrack) ;
    }
  }

  /**
   * Style des repères actifs {@Link TextTrack} en fonction des {@Link TextTrackSettings}.
   *
   * @param {TextTrack} track
   *        Objet de piste de texte contenant des repères actifs à styliser.
   */
  updateDisplayState(track) {
    const overrides = this.player_.textTrackSettings.getValues() ;
    const cues = track.activeCues ;

    let i = cues.length ;

    while (i--) {
      const cue = cues[i] ;

      if (!cue) {
        continuer ;
      }

      const cueDiv = cue.displayState ;

      if (overrides.color) {
        cueDiv.firstChild.style.color = overrides.color ;
      }
      if (overrides.textOpacity) {
        tryUpdateStyle(
          cueDiv.firstChild,
          couleur",
          constructColor(
            overrides.color || '#fff',
            overrides.textOpacity
          )
        ) ;
      }
      if (overrides.backgroundColor) {
        cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor ;
      }
      if (overrides.backgroundOpacity) {
        tryUpdateStyle(
          cueDiv.firstChild,
          couleur d'arrière-plan",
          constructColor(
            overrides.backgroundColor || '#000',
            overrides.backgroundOpacity
          )
        ) ;
      }
      if (overrides.windowColor) {
        if (overrides.windowOpacity) {
          tryUpdateStyle(
            cueDiv,
            couleur d'arrière-plan",
            constructColor(overrides.windowColor, overrides.windowOpacity)
          ) ;
        } else {
          cueDiv.style.backgroundColor = overrides.windowColor ;
        }
      }
      if (overrides.edgeStyle) {
        if (overrides.edgeStyle === 'dropshadow') {
          cueDiv.firstChild.style.textShadow = `2px 2px 3px ${darkGray}, 2px 2px 4px ${darkGray}, 2px 2px 5px ${darkGray}` ;
        } else if (overrides.edgeStyle === 'raised') {
          cueDiv.firstChild.style.textShadow = `1px 1px ${darkGray}, 2px 2px ${darkGray}, 3px 3px ${darkGray}` ;
        } else if (overrides.edgeStyle === 'depressed') {
          cueDiv.firstChild.style.textShadow = `1px 1px ${lightGray}, 0 1px ${lightGray}, -1px -1px ${darkGray}, 0 -1px ${darkGray}` ;
        } else if (overrides.edgeStyle === 'uniform') {
          cueDiv.firstChild.style.textShadow = `0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}, 0 0 4px ${darkGray}` ;
        }
      }
      if (overrides.fontPercent && overrides.fontPercent !== 1) {
        const fontSize = window.parseFloat(cueDiv.style.fontSize) ;

        cueDiv.style.fontSize = (fontSize * overrides.fontPercent) + 'px' ;
        cueDiv.style.height = 'auto' ;
        cueDiv.style.top = 'auto' ;
      }
      if (overrides.fontFamily && overrides.fontFamily !== 'default') {
        if (overrides.fontFamily === 'small-caps') {
          cueDiv.firstChild.style.fontVariant = 'small-caps' ;
        } else {
          cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily] ;
        }
      }
    }
  }

  /**
   * Ajouter un {@link TextTrack} à la {@link Tech}s {@link TextTrackList}.
   *
   * @param {TextTrack|TextTrack[]} tracks
   *        Objet ou tableau de pistes de texte à ajouter à la liste.
   */
  updateForTrack(tracks) {
    if (!Array.isArray(tracks)) {
      pistes = [pistes] ;
    }
    if (typeof window.WebVTT !== 'function' ||
      tracks.every((track)=> {
        return !track.activeCues ;
      })) {
      retour ;
    }

    const cues = [] ;

    // pousser tous les signaux actifs de la piste
    for (let i = 0 ; i < tracks.length ; ++i) {
      const track = tracks[i] ;

      for (let j = 0 ; j < track.activeCues.length ; ++j) {
        cues.push(track.activeCues[j]) ;
      }
    }

    // supprime tous les signaux avant d'en traiter de nouveaux
    window.WebVTT.processCues(window, cues, this.el_) ;

    // ajouter une classe unique à chaque langue & ajouter des paramètres de style si nécessaire
    for (let i = 0 ; i < tracks.length ; ++i) {
      const track = tracks[i] ;

      for (let j = 0 ; j < track.activeCues.length ; ++j) {
        const cueEl = track.activeCues[j].displayState ;

        Dom.addClass(cueEl, 'vjs-text-track-cue') ;
        Dom.addClass(cueEl, 'vjs-text-track-cue-' + ((track.language) ? track.language : i)) ;
        if (track.language) {
          Dom.setAttribute(cueEl, 'lang', track.language) ;
        }
      }
      if (this.player_.textTrackSettings) {
        this.updateDisplayState(track) ;
      }
    }
  }

}

Component.registerComponent('TextTrackDisplay', TextTrackDisplay) ;
exporter l'affichage par défaut de TextTrackDisplay ;