class PatchInstallerFactory extends AbstractInstallerFactory {

  @Override
  protected boolean canHandlePackage(@NotNull RepoPackage p, @NotNull RepoManager manager, @NotNull FileOp fop) {
    ProgressIndicator progress = new StudioLoggerProgressIndicator(PatchInstallerFactory.class);
    if (p instanceof LocalPackage) {
      // Uninstall case. Only useful on windows, since it locks in-use files.
      if (fop.isWindows()) {
        try {
          if (FileUtilKt.recursiveSize(fop.toPath(((LocalPackage)p).getLocation())) >= PSEUDO_PATCH_CUTOFF) {
            // Don't pseudo-patch if the file is too big.
            return false;
          }
        }
        catch (IOException e) {
          // ignore
        }
        // Any patcher will do: just see if we have any patcher available.
        LocalPackage latestPatcher = PatchInstallerUtil.getLatestPatcher(manager);
        // don't try to use the patcher to uninstall itself
        return latestPatcher != null && !latestPatcher.equals(p);
      }
      else {
        // Don't use patcher on non-windows.
        return false;
      }
    }

    LocalPackage local = manager.getPackages().getLocalPackages().get(p.getPath());
    RemotePackage remote = (RemotePackage)p;
    if (local == null || (!fop.isWindows() && !hasPatch(local, remote))) {
      // If this isn't an update, or if we're not on windows and there's no patch, there's no reason to use the patcher.
      return false;
    }

    if (hasPatch(local, remote)) {
      // If a patch is available, make sure we can get the patcher itself
      LocalPackage patcher = PatchInstallerUtil.getDependantPatcher((RemotePackage)p, manager);
      if (patcher != null && myPatchRunnerFactory.getPatchRunner(patcher, progress, fop) != null) {
        return true;
      }

      // Maybe it's not installed yet, but is being installed right now as part of the same operation.
      if (PatchInstallerUtil.getInProgressDependantPatcherInstall((RemotePackage)p, manager) != null) {
        return true;
      }

      // We don't have the right patcher. Give up unless we're on Windows.
      if (!fop.isWindows()) {
        return false;
      }
    }

    // At this point we must be on Windows.
    if (((RemotePackage)p).getArchive().getComplete().getSize() >= PSEUDO_PATCH_CUTOFF) {
      // Don't pseudo-patch if the file is too big.
      return false;
    }
    // There's no patch available, but if a patch installer is installed and better than KNOWN_GOOD_VERSION we can still use it.
    LocalPackage patcher = PatchInstallerUtil.getLatestPatcher(manager);
    return patcher != null && PatchInstallerUtil.comparePatcherPaths(patcher.getPath(), KNOWN_GOOD_VERSION) >= 0;
  }

  public  PatchInstallerFactory();
  @VisibleForTesting  PatchInstallerFactory(@NotNull PatchRunner.Factory runnerFactory);

}