/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 *
 * @format
 */
"use strict";

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

const AssetPaths = require("./lib/AssetPaths");

const MapWithDefaults = require("./lib/MapWithDefaults");

/**
 * Lazily build an index of assets for the directories in which we're looking
 * for specific assets. For example if we're looking for `foo.png` in a `bar`
 * directory, we'll look at all the files there and identify all the assets
 * related to `foo.png`, for example `foo@2x.png` and `foo.ios.png`.
 */
class AssetResolutionCache {
  constructor(options) {
    _defineProperty(this, "_findAssets", dirPath => {
      const results = new Map();

      const fileNames = this._opts.getDirFiles(dirPath);

      for (let i = 0; i < fileNames.length; ++i) {
        const fileName = fileNames[i];
        const assetData = AssetPaths.tryParse(fileName, this._opts.platforms);

        if (assetData == null || !this._isValidAsset(assetData)) {
          continue;
        }

        getWithDefaultArray(results, assetData.assetName).push({
          fileName,
          platform: assetData.platform
        });

        if (assetData.platform) {
          const assetNameWithPlatform = `${assetData.name}.${
            assetData.platform
          }.${assetData.type}`;
          getWithDefaultArray(results, assetNameWithPlatform).push({
            fileName,
            platform: null
          });
        }
      }

      return results;
    });

    this._assetsByDirPath = new MapWithDefaults(this._findAssets);
    this._opts = options;
  }
  /**
   * The cache needs to be emptied if any file changes. This could be made more
   * selective if performance demands it: for example, we could clear
   * exclusively the directories in which files have changed. But that'd be
   * more error-prone.
   */

  clear() {
    this._assetsByDirPath.clear();
  }
  /**
   * Get the file paths of all the variants (resolutions, platforms, etc.) of a
   * particular asset name, only looking at a specific directory. If needed this
   * function could be changed to return pre-parsed information about the assets
   * such as the resolution.
   */

  resolve(dirPath, assetName, platform) {
    const results = this._assetsByDirPath.get(dirPath);

    const assets = results.get(assetName);

    if (assets == null) {
      return null;
    }

    return assets
      .filter(asset => asset.platform == null || asset.platform === platform)
      .map(asset => asset.fileName);
  }
  /**
   * Build an index of assets for a particular directory. Several file can
   * fulfill a single asset name, for example the different resolutions or
   * platforms: ex. `foo.png` could contain `foo@2x.png`, `foo.ios.js`, etc.
   */

  _isValidAsset(assetData) {
    return this._opts.assetExtensions.has(assetData.type);
  }
}
/**
 * Used instead of `MapWithDefaults` so that we don't create empty arrays
 * anymore once the index is built.
 */

function getWithDefaultArray(map, key) {
  let el = map.get(key);

  if (el != null) {
    return el;
  }

  el = [];
  map.set(key, el);
  return el;
}

module.exports = AssetResolutionCache;