/**
 * @file video.js
 * @module videojs
 */
import {version} de '../../package.json' ;
import window from 'global/window' ;
import {
  crochets_,
  crochets,
  crochet,
  hookOnce,
  Retirer le crochet
} from './utils/hooks' ;
import * as setup from './setup' ;
import * as stylesheet from './utils/stylesheet.js' ;
import Component from './component' ;
import EventTarget from './event-target' ;
import * as Events from './utils/events.js' ;
import Player from './player' ;
import Plugin from './plugin' ;
import mergeOptions from './utils/merge-options.js' ;
import * as Fn from './utils/fn.js' ;
import TextTrack de './tracks/text-track.js' ;
import AudioTrack from './tracks/audio-track.js' ;
import VideoTrack from './tracks/video-track.js' ;

import { createTimeRanges } from './utils/time-ranges.js' ;
import formatTime, { setFormatTime, resetFormatTime } from './utils/format-time.js' ;
import log, { createLogger } from './utils/log.js' ;
import * as Dom from './utils/dom.js' ;
import * as browser from './utils/browser.js' ;
import * as Url from './utils/url.js' ;
import {isObject} de './utils/obj' ;
import computedStyle de './utils/computed-style.js' ;
import extend from './extend.js' ;
import xhr de '@videojs/xhr' ;

// Inclure les techniciens intégrés
import Tech from './tech/tech.js' ;
import { use as middlewareUse, TERMINATOR } from './tech/middleware.js' ;
import defineLazyProperty de './utils/define-lazy-property.js' ;

/**
 * Normaliser une valeur `id` en enlevant le `#` de tête
 *
 * @private
 * @param {string} id
 *          Une chaîne de caractères, éventuellement précédée d'un `#`.
 *
 * @return {string}
 *          La chaîne de caractères, sans `#` en tête.
 */
const normalizeId = (id) => id.indexOf('#') === 0 ? id.slice(1) : id ;

/**
 * La fonction `videojs()` est également la fonction principale permettant aux utilisateurs de créer une vidéo
 * {@link Player} ainsi que l'espace de noms de la bibliothèque principale.
 *
 * Il peut également être utilisé comme un getter pour une instance {@link Player} préexistante.
 * Cependant, nous recommandons fortement d'utiliser `videojs.getPlayer()` pour cela
 * car elle permet d'éviter tout risque d'initialisation involontaire.
 *
 * En raison de [limitations] (https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
 * de notre modèle JSDoc, nous ne pouvons pas documenter correctement cette fonction comme étant à la fois une fonction
 * et un espace de noms, de sorte que la signature de sa fonction est documentée ici.
 *
 * #### Arguments
 * ##### id
 * chaîne|élément, **obligatoire**
 *
 * Élément vidéo ou ID de l'élément vidéo.
 *
 * ##### options
 * Objet, optionnel
 *
 * Objet d'options permettant de définir des paramètres.
 * Voir : [Guide des options] (https://docs.videojs.com/tutorial-options.html).
 *
 * ##### prêt
 * {@link Component~ReadyCallback}, optionnel
 *
 * Une fonction à appeler lorsque le {@link Player} et le {@link Tech} sont prêts.
 *
 * #### Valeur de retour
 *
 * La fonction `videojs()` renvoie une instance {@link Player}.
 *
 * @namespace
 *
 * @borrows AudioTrack as AudioTrack
 * @borrows Component.getComponent as getComponent
 * @borrows module:computed-style~computedStyle as computedStyle
 * @borrows module:events.on as on
 * @borrows module:events.one as one
 * @borrows module:events.off as off
 * @borrows module:events.trigger as trigger
 * @borrows EventTarget as EventTarget
 * @borrows module:extend~extend as extend
 * @borrows module:fn.bind as bind
 * @borrows module:format-time.formatTime as formatTime
 * @borrows module:format-time.resetFormatTime as resetFormatTime
 * @borrows module:format-time.setFormatTime as setFormatTime
 * @borrows module:merge-options.mergeOptions as mergeOptions
 * @borrows module:middleware.use comme use
 * @borrows Player.players as players
 * @borrows Plugin.registerPlugin as registerPlugin
 * @borrows Plugin.deregisterPlugin as deregisterPlugin
 * @borrows Plugin.getPlugins as getPlugins
 * @borrows Plugin.getPlugin as getPlugin
 * @borrows Plugin.getPluginVersion as getPluginVersion
 * @borrows Tech.getTech as getTech
 * @borrows Tech.registerTech as registerTech
 * @borrows TextTrack as TextTrack
 * @borrows module:time-ranges.createTimeRanges as createTimeRange
 * @borrows module:time-ranges.createTimeRanges as createTimeRanges
 * @borrows module:url.isCrossOrigin as isCrossOrigin
 * @borrows module:url.parseUrl as parseUrl
 * @borrows VideoTrack as VideoTrack
 *
 * @param {string|Element} id
 *         Élément vidéo ou ID de l'élément vidéo.
 *
 * @param {Objet} [options]
 *         Objet d'options permettant de définir des paramètres.
 *         Voir : [Guide des options] (https://docs.videojs.com/tutorial-options.html).
 *
 * @param {Component~ReadyCallback} [ready]
 *         Une fonction à appeler lorsque les {@link Player} et {@link Tech} sont
 *         prêt.
 *
 * @return {Player}
 *         La fonction `videojs()` renvoie une instance {@link Player|Player}.
 */
function videojs(id, options, ready) {
  let player = videojs.getPlayer(id) ;

  if (player) {
    if (options) {
      log.warn(`Le joueur "${id}" est déjà initialisé. Les options ne seront pas appliquées.`) ;
    }
    if (ready) {
      player.ready(ready) ;
    }
    joueur de retour ;
  }

  const el = (typeof id === 'string') ? Dom.$('#' + normalizeId(id)) : id ;

  if (!Dom.isEl(el)) {
    throw new TypeError('L'élément ou l'ID fourni n'est pas valide. (videojs)') ;
  }

  // document.body.contains(el) ne vérifiera que si el est contenu dans ce seul document.
  // Cela pose des problèmes pour les éléments dans les iframes.
  // Au lieu de cela, utilisez le document propriétaire de l'élément au lieu du document global.
  // Cela permet de s'assurer que l'élément se trouve bien dans le dom de ce document.
  // En outre, vérifiez que le document en question dispose d'une vue par défaut.
  // Si le document n'est plus attaché au dom, la vue par défaut du document sera nulle.
  if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
    log.warn('L'élément fourni n'est pas inclus dans le DOM') ;
  }

  options = options || {} ;

  // Stocker une copie de l'el avant modification, s'il doit être restauré dans destroy()
  // En cas d'ingestion de div, stocker le div parent
  if (options.restoreEl === true) {
    options.restoreEl = (el.parentNode && el.parentNode.hasAttribute('data-vjs-player') ? el.parentNode : el).cloneNode(true) ;
  }

  hooks('beforesetup').forEach((hookFunction) => {
    const opts = hookFunction(el, mergeOptions(options)) ;

    if (!isObject(opts) || Array.isArray(opts)) {
      log.error('please return an object in beforesetup hooks') ;
      retour ;
    }

    options = mergeOptions(options, opts) ;
  }) ;

  // Nous obtenons ici le composant "Player" actuel au cas où une intégration aurait eu lieu
  // l'a remplacé par un lecteur personnalisé.
  const PlayerComponent = Component.getComponent('Player') ;

  player = new PlayerComponent(el, options, ready) ;

  hooks('setup').forEach((hookFunction) => hookFunction(player)) ;

  joueur de retour ;
}

videojs.hooks_ = hooks_ ;
videojs.hooks = hooks ;
videojs.hook = hook ;
videojs.hookOnce = hookOnce ;
videojs.removeHook = removeHook ;

// Ajouter les styles par défaut
if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true && Dom.isReal()) {
  let style = Dom.$('.vjs-styles-defaults') ;

  if (!style) {
    style = stylesheet.createStyleElement('vjs-styles-defaults') ;
    const head = Dom.$('head') ;

    if (head) {
      head.insertBefore(style, head.firstChild) ;
    }
    feuille de style.setTextContent(style, `
      .video-js {
        width: 300px ;
        height: 150px ;
      }

      .vjs-fluid:not(.vjs-audio-only-mode) {
        padding-top : 56,25%
      }
    `) ;
  }
}

// Lancer le chargement automatique des joueurs
// Vous devez attendre au moins une fois au cas où ce script serait chargé après votre
// vidéo dans le DOM (comportement bizarre uniquement avec la version minifiée)
setup.autoSetupTimeout(1, videojs) ;

/**
 * Version actuelle de Video.js. Suit [version sémantique] (https://semver.org/).
 *
 * @type {string}
 */
videojs.VERSION = version ;

/**
 * L'objet des options globales. Voici les paramètres qui s'appliquent
 * si aucune dérogation n'est spécifiée lors de la création du lecteur.
 *
 * @type {Objet}
 */
videojs.options = Player.prototype.options_ ;

/**
 * Obtention d'un objet contenant les joueurs actuellement créés, avec pour clé l'identifiant du joueur
 *
 * @return {Object}
 *         Les acteurs créés
 */
videojs.getPlayers = () => Player.players ;

/**
 * Obtenir un lecteur unique sur la base d'un ID ou d'un élément DOM.
 *
 * Cette fonction est utile si vous souhaitez vérifier si un élément ou un ID est associé à un
 * Video.js, mais n'en crée pas si ce n'est pas le cas.
 *
 * @param {string|Element} id
 *          Un élément HTML - `<video>`, `<audio>`, ou `<video-js>` -
 *          ou une chaîne de caractères correspondant à l'`id` d'un tel élément.
 *
 * @return {Player|undefined}
 *          Une instance de joueur ou `undefined` s'il n'y a pas d'instance de joueur
 *          correspondant à l'argument.
 */
videojs.getPlayer = (id) => {
  const players = Player.players ;
  let tag ;

  if (typeof id === 'string') {
    const nId = normalizeId(id) ;
    const player = players[nId] ;

    if (player) {
      joueur de retour ;
    }

    tag = Dom.$('#' + nId) ;
  } else {
    tag = id ;
  }

  if (Dom.isEl(tag)) {
    const {player, playerId} = tag ;

    // L'élément peut avoir une propriété `player` faisant référence à un élément déjà créé
    // instance de joueur. Si c'est le cas, renvoyez-le.
    if (player || players[playerId]) {
      return player || players[playerId] ;
    }
  }
};

/**
 * Renvoie un tableau de tous les joueurs actuels.
 *
 * @return {Array}
 *         Un tableau de tous les joueurs. Le tableau sera présenté dans l'ordre suivant
 *         `Object.keys`, qui peut potentiellement varier entre
 *         Moteurs JavaScript.
 *
 */
videojs.getAllPlayers = () =>

  // Les joueurs éliminés laissent une clé avec une valeur `null`, nous devons donc nous assurer que
  // nous les filtrons.
  Object.keys(Player.players).map(k => Player.players[k]).filter(Boolean) ;

videojs.players = Player.players ;
videojs.getComponent = Component.getComponent ;

/**
 * Enregistrer un composant pour qu'il puisse être référencé par son nom. Utilisé lors de l'ajout à d'autres
 * soit par addChild `component.addChild('myComponent')` soit par addChild
 * options par défaut pour les enfants `{ children : ['myComponent'] }`.
 *
 * > NOTE : Vous pouvez également initialiser le composant avant de l'ajouter.
 * `component.addChild(new MyComponent());`
 *
 * @param {string} name
 *        Le nom de la classe du composant
 *
 * @param {Composant} comp
 *        La classe de composants
 *
 * @return {Component}
 *         Le composant nouvellement enregistré
 */
videojs.registerComponent = (name, comp) => {
  if (Tech.isTech(comp)) {
    log.warn(`La technologie ${nom} a été enregistrée en tant que composant. Il doit être enregistré à l'aide de videojs.registerTech(name, tech)`) ;
  }

  Component.registerComponent.call(Component, name, comp) ;
};

videojs.getTech = Tech.getTech ;
videojs.registerTech = Tech.registerTech ;
videojs.use = middlewareUse ;

/**
 * Un objet qui peut être renvoyé par un intergiciel pour signifier
 * que l'intergiciel est en train de se terminer.
 *
 * @type {objet}
 * @property {objet} middleware.TERMINATOR
 */
Object.defineProperty(videojs, 'middleware', {
  valeur : {},
  accessible à l'écriture : false,
  énumérable : true
}) ;

Object.defineProperty(videojs.middleware, 'TERMINATOR', {
  valeur : TERMINATOR,
  accessible à l'écriture : false,
  énumérable : true
}) ;

/**
 * Une référence au {@link module:browser|module utilitaire du navigateur} en tant qu'objet.
 *
 * @type {Objet}
 * @see {@link module:browser|browser}
 */
videojs.browser = browser ;

/**
 * Utilisez plutôt {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED} ; seulement
 * inclus pour la compatibilité ascendante avec la version 4.x.
 *
 * @deprecated Depuis la version 5.0, utilisez {@link module:browser.TOUCH_ENABLED|browser.TOUCH_ENABLED à la place.
 * @type {boolean}
 */
videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED ;

videojs.extend = extend ;
videojs.mergeOptions = mergeOptions ;
videojs.bind = Fn.bind ;
videojs.registerPlugin = Plugin.registerPlugin ;
videojs.deregisterPlugin = Plugin.deregisterPlugin ;

/**
 * Méthode obsolète pour enregistrer un plugin avec Video.js
 *
 * @deprecated videojs.plugin() est obsolète ; utilisez videojs.registerPlugin() à la place
 *
 * @param {string} name
 *        Le nom du plugin
 *
 * @param {Plugin|Function} plugin
 *         La sous-classe ou la fonction du plugin
 */
videojs.plugin = (name, plugin) => {
  log.warn('videojs.plugin() is deprecated ; use videojs.registerPlugin() instead') ;
  return Plugin.registerPlugin(name, plugin) ;
};

videojs.getPlugins = Plugin.getPlugins ;
videojs.getPlugin = Plugin.getPlugin ;
videojs.getPluginVersion = Plugin.getPluginVersion ;

/**
 * Ajout de langues pour qu'elles soient disponibles pour tous les joueurs.
 * Exemple : `videojs.addLanguage('es', { 'Hello' : 'Hola' });`
 *
 * @param {string} code
 *        Le code de la langue ou la propriété du dictionnaire
 *
 * @param {Object} data
 *        Les valeurs de données à traduire
 *
 * @return {Object}
 *         L'objet dictionnaire de langue résultant
 */
videojs.addLanguage = function(code, data) {
  code = ('' + code).toLowerCase() ;

  videojs.options.languages = mergeOptions(
    videojs.options.languages,
    {[code] : données}
  ) ;

  return videojs.options.languages[code] ;
};

/**
 * Une référence au {@link module:log|module utilitaire log} en tant qu'objet.
 *
 * @type {Fonction}
 * @see {@link module:log|log}
 */
videojs.log = log ;
videojs.createLogger = createLogger ;

videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges ;
videojs.formatTime = formatTime ;
videojs.setFormatTime = setFormatTime ;
videojs.resetFormatTime = resetFormatTime ;
videojs.parseUrl = Url.parseUrl ;
videojs.isCrossOrigin = Url.isCrossOrigin ;
videojs.EventTarget = EventTarget ;
videojs.on = Events.on ;
videojs.one = Events.one ;
videojs.off = Events.off ;
videojs.trigger = Events.trigger ;

/**
 * Un wrapper XMLHttpRequest pour tous les navigateurs.
 *
 * @fonction
 * @param {Objet} options
 *           Paramètres de la demande.
 *
 * @return {XMLHttpRequest|XDomainRequest}
 *           L'objet de la demande.
 *
 * voir https://github.com/Raynos/xhr
 */
videojs.xhr = xhr ;

videojs.TextTrack = TextTrack ;
videojs.AudioTrack = AudioTrack ;
videojs.VideoTrack = VideoTrack ;

[
  'isEl',
  'isTextNode',
  'createEl',
  'hasClass',
  'addClass',
  'removeClass',
  'toggleClass',
  'setAttributes',
  'getAttributes',
  emptyEl",
  'appendContent',
  insérerContenu
].forEach(k => {
  videojs[k] = function() {
    log.warn(`videojs.${k}() is deprecated ; use videojs.dom.${k}() instead`) ;
    return Dom[k].apply(null, arguments) ;
  };
}) ;

videojs.computedStyle = computedStyle ;

/**
 * Une référence au {@link module:dom|DOM utility module} en tant qu'objet.
 *
 * @type {Objet}
 * @see {@link module:dom|dom}
 */
videojs.dom = Dom ;

/**
 * Une référence au module utilitaire {@link module:url|URL} en tant qu'objet.
 *
 * @type {Objet}
 * @see {@link module:url|url}
 */
videojs.url = Url ;

videojs.defineLazyProperty = defineLazyProperty ;

// Ajout d'un texte moins ambigu pour le bouton fullscreen.
// Dans une mise à jour majeure, cela pourrait devenir le texte et la clé par défaut.
videojs.addLanguage('en', {'Non-Fullscreen' : 'Exit Fullscreen'}) ;

export default videojs ;