/**
 * @file create-logger.js
 * @module create-logger
 */
import window from 'global/window' ;

// Il s'agit de la variable de suivi privée pour l'historique de l'enregistrement.
let history = [] ;

/**
 * Enregistrer les messages dans la console et l'historique en fonction du type de message
 *
 * @private
 * @param {string} type
 *         Le nom de la méthode de console à utiliser.
 *
 * @param {Array} args
 *         Les arguments à transmettre à la méthode de la console correspondante.
 */
const LogByTypeFactory = (name, log) => (type, level, args) => {
  const lvl = log.levels[level] ;
  const lvlRegExp = new RegExp(`^(${lvl})$`) ;

  if (type !== 'log') {

    // Ajouter le type au début du message lorsqu'il ne s'agit pas de "log".
    args.unshift(type.toUpperCase() + ':') ;
  }

  // Ajouter le préfixe de la console après l'ajout à l'historique.
  args.unshift(name + ':') ;

  // Ajouter un clone des args à ce point dans l'historique.
  if (history) {
    history.push([].concat(args)) ;

    // ne stocke que 1000 entrées d'historique
    const splice = history.length - 1000 ;

    history.splice(0, splice > 0 ? splice : 0) ;
  }

  // S'il n'y a pas de console, n'essayez pas d'envoyer des messages, mais ils le feront
  // sont encore inscrits dans l'histoire.
  if (!window.console) {
    retour ;
  }

  // Ces paramètres étaient réglés une fois en dehors de cette fonction, mais ils ont été conservés
  // dans la fonction permet de tester plus facilement les cas où la console n'existe pas
  // lorsque le module est exécuté.
  let fn = window.console[type] ;

  if (!fn && type === 'debug') {
    // Certains navigateurs ne prennent pas en charge console.debug. Pour ces derniers, nous
    // devrait prendre par défaut le journal comparable le plus proche.
    fn = window.console.info || window.console.log ;
  }

  // S'en sortir s'il n'y a pas de console ou si ce type n'est pas autorisé par le système de gestion de l'information
  // niveau de journalisation actuel.
  if (!fn || !lvl || !lvlRegExp.test(type)) {
    retour ;
  }

  fn[Array.isArray(args) ? 'apply' : 'call'](window.console, args) ;
};

export default function createLogger(name) {
  // Il s'agit de la variable de suivi privée pour le niveau de journalisation.
  let level = 'info' ;

  // le logByType lié au journal et à l'historique spécifiques
  let logByType ;

  /**
   * Enregistre les messages de débogage simples. Similaire à `console.log`.
   *
   * 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
   * ##### *args
   * Mixte[]
   *
   * Toute combinaison de valeurs pouvant être passées à `console.log()`.
   *
   * #### Valeur de retour
   *
   * `undefined`
   *
   * @namespace
   * @param {Mixed[]} args
   *           Un ou plusieurs messages ou objets qui doivent être enregistrés.
   */
  const log = function(...args) {
    logByType('log', level, args) ;
  };

  // Il s'agit de l'aide logByType utilisée par les méthodes de journalisation ci-dessous
  logByType = LogByTypeFactory(name, log) ;

  /**
   * Créer un nouveau sous-blogger qui enchaîne l'ancien nom au nouveau nom.
   *
   * Par exemple, en faisant `videojs.log.createLogger('player')` et en utilisant ce logger, on obtiendra le résultat suivant :
   * ``js
   *  mylogger('foo') ;
   *  // > VIDEOJS : player : foo
   * ```
   *
   * @param {string} name
   *        Le nom à ajouter pour appeler le nouvel enregistreur
   * @return {Object}
   */
  log.createLogger = (subname) => createLogger(name + ' : ' + subname) ;

  /**
   * Enumération des niveaux de journalisation disponibles, les clés étant les noms des niveaux
   * et les valeurs sont des chaînes séparées par `|` contenant les méthodes de journalisation autorisées
   * dans ce niveau de journalisation. Ces chaînes sont utilisées pour créer une expression régulière
   * correspondant au nom de la fonction appelée.
   *
   * Les niveaux fournis par Video.js sont les suivants :
   *
   * - `off` : Ne correspond à aucun appel. Toute valeur qui peut être transformée en `false` aura une valeur de
   *   cet effet. Le plus restrictif.
   * - `all` : Ne correspond qu'aux fonctions fournies par Video.js (`debug`, `log`,
   *   `log.warn`, et `log.error`).
   * - `debug` : Correspond aux appels `log.debug`, `log`, `log.warn`, et `log.error`.
   * - `info` (par défaut) : Correspond aux appels `log`, `log.warn` et `log.error`.
   * - `warn` : Correspond aux appels `log.warn` et `log.error`.
   * - `error` : Ne prend en compte que les appels à `log.error`.
   *
   * @type {Objet}
   */
  log.levels = {
    tous : 'debug|log|warn|error',
    off : '',
    debug : 'debug|log|warn|error',
    info : 'log|warn|error',
    warn : 'warn|error',
    erreur : "erreur",
    DEFAUT : niveau
  };

  /**
   * Permet d'obtenir ou de définir le niveau de journalisation actuel.
   *
   * Si une chaîne correspondant à une clé de {@link module:log.levels} est fournie, acts
   * en tant que passeur.
   *
   * @param {string} [lvl]
   *         Passez un niveau valide pour définir un nouveau niveau de journalisation.
   *
   * @return {string}
   *         Le niveau de journalisation actuel.
   */
  log.level = (lvl) => {
    if (typeof lvl === 'string') {
      if (!log.levels.hasOwnProperty(lvl)) {
        throw new Error(`"${lvl}" in not a valid log level`) ;
      }
      niveau = lvl ;
    }
    le niveau de retour ;
  };

  /**
   * Renvoie un tableau contenant tout ce qui a été enregistré dans l'historique.
   *
   * Ce tableau est un clone peu profond de l'historique interne. Toutefois, son
   * le contenu n'est _pas_ cloné ; ainsi, la mutation d'objets à l'intérieur de ce tableau sera
   * les faire muter dans l'histoire.
   *
   * @return {Array}
   */
  log.history = () => history ? [].concat(history) : [] ;

  /**
   * Permet de filtrer l'historique en fonction du nom de l'enregistreur donné
   *
   * @param {string} fname
   *        Le nom à filtrer par
   *
   * @return {Array}
   *         La liste filtrée à retourner
   */
  log.history.filter = (fname) => {
    return (history || []).filter((historyItem) => {
      // si le premier élément de chaque historyItem contient `fname`, alors il y a correspondance
      return new RegExp(`.*${fname}.*`).test(historyItem[0]) ;
    }) ;
  };

  /**
   * Efface le suivi interne de l'historique, mais n'empêche pas la poursuite de l'historique
   * le suivi.
   */
  log.history.clear = () => {
    if (history) {
      histoire.longueur = 0 ;
    }
  };

  /**
   * Désactiver le suivi de l'historique s'il est actuellement activé.
   */
  log.history.disable = () => {
    if (history !== null) {
      histoire.longueur = 0 ;
      histoire = null ;
    }
  };

  /**
   * Activer le suivi de l'historique s'il est actuellement désactivé.
   */
  log.history.enable = () => {
    if (history === null) {
      histoire = [] ;
    }
  };

  /**
   * Enregistre les messages d'erreur. Similaire à `console.error`.
   *
   * @param {Mixed[]} args
   *        Un ou plusieurs messages ou objets qui doivent être consignés en tant qu'erreur
   */
  log.error = (...args) => logByType('error', level, args) ;

  /**
   * Enregistre les messages d'avertissement. Similaire à `console.warn`.
   *
   * @param {Mixed[]} args
   *        Un ou plusieurs messages ou objets qui doivent être consignés en tant qu'avertissement.
   */
  log.warn = (...args) => logByType('warn', level, args) ;

  /**
   * Enregistre les messages de débogage. Similaire à `console.debug`, mais peut aussi agir comme une fonction comparable à `console.debug`
   * log si `console.debug` n'est pas disponible
   *
   * @param {Mixed[]} args
   *        Un ou plusieurs messages ou objets qui doivent être consignés en tant que débogage.
   */
  log.debug = (...args) => logByType('debug', level, args) ;

  retourner le journal ;
}