/**
 * @file 中間件.js
 * @module 中間件
 */
從'../utils/obj.js'導入{分配};
從 '../utils/string-cases.js' 導入 {toTitleCase};

const 中間件 = {};
常量中間件實例 = {};

導出常量終止符 = {};

/**
 * 中間件對像是一個普通的 JavaScript 對象,它有一些方法
 * 匹配允許列表中的 {@link Tech} 方法
 * {@link 模塊:中間件。允許 | 吸氣器},
 * {@link 模塊:中間件. 允許的設置器 | 設置器} 和
 * {@link 模塊:中間件。允許調解器 | 調解器}。
 *
 * @typedef {Object} 中間件對象
 */

/**
 * 一個中間件工廠函數,應該返回一個
 * {@link 模塊:中間件〜中間件對象 | 中間件對象}。
 *
 * 這個工廠會在需要的時候為每個玩家調用,和玩家一起
 * 作為參數傳入。
 *
 * @callback 中間件工廠
 * @param {Player} 播放器
 * Video.js 播放器。
 */

/**
 * 定義播放器應通過工廠函數使用的中間件
 * 返回一個中間件對象。
 *
 * @param {string} 類型
 * 要匹配的 MIME 類型或所有 MIME 類型的“*”。
 *
 * @param {MiddlewareFactory} 中間件
 * 將執行的中間件工廠函數
 * 匹配類型。
 */
導出函數使用(類型,中間件){
  中間件[類型] = 中間件[類型] || [];
  中間件[類型].push(中間件);
}

/**
 * 按類型(或所有中間件)獲取中間件。
 *
 * @param {string} 類型
 * 要匹配的 MIME 類型或所有 MIME 類型的“*”。
 *
 * @return {函數[]|未定義}
 * 一組中間件,如果不存在則為 `undefined`。
 */
導出函數 getMiddleware(type) {
  如果(類型){
    返回中間件[類型];
  }

  返回中間件;
}

/**
 *通過遞歸任何中間件異步設置源
 * 匹配中間件並在每個中間件上調用 `setSource`,傳遞
 * 以前每次返回的值。
 *
 * @param {Player} 播放器
 * {@link Player} 實例。
 *
 * @param {Tech~SourceObject} 源
 * 源對象。
 *
 * @param {函數}
 * 下一個要運行的中間件。
 */
導出函數 setSource(player, src, next) {
  player.setTimeout(() => setSourceHelper(src, middlewares[src.type], next, player), 1);
}

/**
 * 設置技術後,將技術傳遞給每個中間件的 setTech 方法。
 *
 * @param {Object[]} 中間件
 * 一組中間件實例。
 *
 * @param {技術}技術
 * Video.js 技術。
 */
導出函數 setTech(中間件,技術){
  middleware.forEach((mw) => mw.setTech && mw.setTech(tech));
}

/**
 * 首先通過每個中間件調用技術上的吸氣劑
 * 從右到左給玩家。
 *
 * @param {Object[]} 中間件
 * 一組中間件實例。
 *
 * @param {技術} 技術
 * 目前的技術。
 *
 * @param {string} 方法
 * 一個方法名。
 *
 * @return {混合}
 * 中間件攔截後技術的最終值。
 */
導出函數 get(中間件、技術、方法){
  return middleware.reduceRight(middlewareIterator(method), tech[method]());
}

/**
 * 獲取給玩家的參數並在每個參數上調用 setter 方法
 * 從左到右到技術的中間件。
 *
 * @param {Object[]} 中間件
 * 一組中間件實例。
 *
 * @param {技術}技術
 * 目前的技術。
 *
 * @param {string} 方法
 * 一個方法名。
 *
 * @param {混合} 參數
 * 在技術上設置的值。
 *
 * @return {混合}
 * `tech` 的`method` 的返回值。
 */
導出函數集(中間件、技術、方法、參數){
  返回技術[方法](middleware.reduce(middlewareIterator(method), arg));
}

/**
 * 獲取給玩家的參數並調用
 * 每個中間件上的方法從左到右。
 *
 * 然後,在tech上調用傳入的方法,返回結果不變
 * 返回給播放器,通過中間件,這次是從右到左。
 *
 * @param {Object[]} 中間件
 * 一組中間件實例。
 *
 * @param {技術}技術
 * 目前的技術。
 *
 * @param {string} 方法
 * 一個方法名。
 *
 * @param {混合} 參數
 * 在技術上設置的值。
 *
 * @return {混合}
 * `tech` 的`method` 的返回值,不管
 * 中間件的返回值。
 */
導出函數調解(中間件、技術、方法、arg = null){
  const callMethod = 'call' + toTitleCase(方法);
  const middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
  const terminated = middlewareValue === TERMINATOR;
  // 棄用。`null` 返回值應該返回 TERMINATOR 到
  // 如果 techs 方法實際上返回 null,則防止混淆。
  const returnValue = 終止?空:技術[方法](中間件值);

  executeRight(中間件,方法,returnValue,終止);

  返回返回值;
}

/**
 * 枚舉允許的 getter,其中鍵是方法名稱。
 *
 * @type {對象}
 */
export const allowedGetters = {
  緩衝的:1、
  當前時間:1、
  持續時間:1、
  靜音:1、
  玩過:1、
  暫停:1、
  可尋:1、
  體積:1、
  結束:1
};

/**
 * 允許的 setter 的枚舉,其中鍵是方法名稱。
 *
 * @type {對象}
 */
導出常量 allowedSetters = {
  設置當前時間:1、
  設置靜音:1、
  設置音量:1
};

/**
 * 枚舉允許的調解器,其中鍵是方法名稱。
 *
 * @type {對象}
 */
export const allowedMediators = {
  玩:1、
  暫停:1
};

函數中間件迭代器(方法){
  返回(值,mw)=> {
    // 如果前一個中間件終止,傳遞終止
    如果(值===終止符){
      返回終結者;
    }

    如果(mw[方法]){
      返回 mw[方法](值);
    }

    返回值;
  };
}

函數 executeRight(mws,方法,值,終止){
  對於(讓 i = mws.length - 1; i >= 0; i--){
    const mw = mws[i];

    如果(mw[方法]){
      mw[方法](終止,價值);
    }
  }
}

/**
 * 清除播放器的中間件緩存。
 *
 * @param {Player} 播放器
 * {@link Player} 實例。
 */
導出函數 clearCacheForPlayer(player) {
  middlewareInstances[player.id()] = null;
}

/**
 * {
 * [播放器 ID]:[[MW 工廠,微型實例],...]
 * }
 *
 * @私人的
 */
函數 getOrCreateFactory(播放器,mwFactory){
  const mws = middlewareInstances[player.id()];
  讓 mw = null;

  如果(mws === 未定義 || mws === null){
    mw = mwFactory(玩家);
    middlewareInstances[player.id()] = [[mwFactory, mw]];
    返回兆瓦;
  }

  為 (讓我 = 0; 一世 <  . 長度; 我 ++) {
    const [mwf, mwi] = mws[i];

    如果(MWF!== 米工廠){
      繼續;
    }

    分子量 = 分子量;
  }

  如果(mw === null){
    mw = mwFactory(玩家);
    mws.push([mwFactory, mw]);
  }

  返回兆瓦;
}

function setSourceHelper(src = {}, middleware = [], next, player, acc = [], lastRun = false) {
  常量 [MW 工廠,... 其他] = 中間件;

  // 如果 mwFactory 是一個字符串,那麼我們正處於岔路口
  如果(類型 mwFactory === '字符串'){
    setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);

  // 如果我們有一個 mwFactory,用播放器調用它來獲取 mw,
  // 然後調用mw的setSource方法
  } 否則如果(mwFactory){
    const mw = getOrCreateFactory(播放器, mwFactory);

    // 如果 setSource 不存在,隱式選擇這個中間件
    如果(!mw.setSource){
      acc.push(mw);
      返回 setSourceHelper(src, mwrest, next, player, acc, lastRun);
    }

    mw.setSource(assign({}, src), function(err, _src) {

      // 發生了一些事情,嘗試當前級別的下一個中間件
      // 確保使用舊的 src
      如果(錯誤){
        返回 setSourceHelper(src, mwrest, next, player, acc, lastRun);
      }

      // 我們已經成功了,現在我們需要更深入
      acc.push(mw);

      // 如果是同一類型,則繼續沿當前鏈向下
      // 否則,我們要沿著新鏈向下
      setSourceHelper(
        _src,
        類型 === _src 類型?其中一種:中間件 [_src.type],
        下一個,
        播放器,
        累積,
        最後一次
      );
    });

  } else if (mwrest.length) {
    setSourceHelper(src, mwrest, next, player, acc, lastRun);
  } else if (lastRun) {
    下一個(源,acc);
  }其他{
    setSourceHelper(src, middlewares['*'], next, player, acc, true);
  }
}