/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

export var WindowsInstallsInfo = {
  /**
   * Retrieve install paths of this app, based on the values in the TaskBarIDs registry key.
   *
   * Installs from unarchived packages do not have a TaskBarID registry key and
   * therefore won't appear in the result.
   *
   * @param {number} [limit] Optional, maximum number of installation paths to count.
            Defaults to 1024.
   * @param {Set} [exclude] Optional, an Set of paths to exclude from the count.
   * @returns {Set} Set of install paths, lower cased.
   */
  getInstallPaths(limit = 1024, exclude = new Set()) {
    // This is somewhat more complicated than just collecting all values because
    // the same install can be listed in both HKCU and HKLM.  The strategy is to
    // add all paths to a Set to deduplicate.

    const lcExclude = new Set();
    exclude.forEach(p => lcExclude.add(p.toLowerCase()));

    // Add the names of the values under `rootKey\subKey` to `set`.
    // All strings are lower cased first, as Windows paths are not case-sensitive.
    function collectValues(rootKey, wowFlag, subKey, set) {
      const key = Cc["@mozilla.org/windows-registry-key;1"].createInstance(
        Ci.nsIWindowsRegKey
      );

      try {
        key.open(rootKey, subKey, key.ACCESS_READ | wowFlag);
      } catch (_e) {
        // The key may not exist, ignore.
        // (nsWindowsRegKey::Open doesn't provide detailed error codes)
        return;
      }
      const valueCount = key.valueCount;

      try {
        for (let i = 0; i < valueCount; ++i) {
          const path = key.getValueName(i).toLowerCase();
          if (!lcExclude.has(path)) {
            set.add(path);
          }
          if (set.size >= limit) {
            break;
          }
        }
      } finally {
        key.close();
      }
    }

    const subKeyName = `Software\\Mozilla\\${Services.appinfo.name}\\TaskBarIDs`;

    const paths = new Set();

    // First collect from HKLM for both 32-bit and 64-bit installs regardless of the architecture
    // of the current application.
    for (const wowFlag of [
      Ci.nsIWindowsRegKey.WOW64_32,
      Ci.nsIWindowsRegKey.WOW64_64,
    ]) {
      collectValues(
        Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
        wowFlag,
        subKeyName,
        paths
      );
      if (paths.size >= limit) {
        break;
      }
    }

    if (paths.size < limit) {
      // Then collect from HKCU.
      // HKCU\Software is shared between 32 and 64 so nothing special is needed for WOW64,
      // ref https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys
      collectValues(
        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
        0 /* wowFlag */,
        subKeyName,
        paths
      );
    }

    return paths;
  },
};
