package com.codeborne.selenide.impl;

import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.proxy.FileDownloadFilter;
import com.codeborne.selenide.proxy.SelenideProxyServer;
import com.google.common.base.Predicate;
import org.openqa.selenium.NoSuchWindowException;
import org.openqa.selenium.WebElement;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import static com.codeborne.selenide.WebDriverRunner.getWebDriver;

public class DownloadFileWithProxyServer {
  private static final Logger log = Logger.getLogger(DownloadFileWithProxyServer.class.getName());

  private final long timeout;

  Waiter waiter = new Waiter();

  /**
   * Create new object instance with default download timeout.
   */
  public DownloadFileWithProxyServer() {
    this.timeout = Configuration.timeout;
  }

  /**
   * Create new object instance with custom download timeout.
   *
   * @param timeout download file timeout.
   */
  public DownloadFileWithProxyServer(long timeout) {
    this.timeout = timeout;
  }

  public File download(WebElementSource anyClickableElement,
                       WebElement clickable, SelenideProxyServer proxyServer) throws FileNotFoundException {
    return clickAndInterceptFileByProxyServer(anyClickableElement, clickable, proxyServer);
  }

  private File clickAndInterceptFileByProxyServer(WebElementSource anyClickableElement, WebElement clickable,
                                          SelenideProxyServer proxyServer) throws FileNotFoundException {
    String currentWindowHandle = getWebDriver().getWindowHandle();
    Set<String> currentWindows = getWebDriver().getWindowHandles();

    FileDownloadFilter filter = proxyServer.responseFilter("download");
    filter.activate();
    try {
      clickable.click();

      waiter.wait(filter, new HasDownloads(), timeout, Configuration.pollingInterval);
      return firstDownloadedFile(anyClickableElement, filter);
    }
    finally {
      filter.deactivate();
      closeNewWindows(currentWindowHandle, currentWindows);
    }
  }

  private void closeNewWindows(String currentWindowHandle, Set<String> currentWindows) {
    Set<String> windowHandles = getWebDriver().getWindowHandles();
    if (windowHandles.size() != currentWindows.size()) {
      Set<String> newWindows = new HashSet<>(windowHandles);
      newWindows.removeAll(currentWindows);

      log.info("File has been opened in a new window, let's close " + newWindows.size() + " new windows");

      for (String newWindow : newWindows) {
        log.info("  Let's close " + newWindow);
        try {
          getWebDriver().switchTo().window(newWindow);
          getWebDriver().close();
        }
        catch (NoSuchWindowException windowHasBeenClosedMeanwhile) {
          log.info("  Failed to close " + newWindow + ": " + Cleanup.of.webdriverExceptionMessage(windowHasBeenClosedMeanwhile));
        }
        catch (Exception e) {
          log.warning("  Failed to close " + newWindow + ": " + e);
        }
      }
      getWebDriver().switchTo().window(currentWindowHandle);
    }
  }

  private static class HasDownloads implements Predicate<FileDownloadFilter> {
    @Override
    public boolean apply(FileDownloadFilter filter) {
      return !filter.getDownloadedFiles().isEmpty();
    }
  }

  private File firstDownloadedFile(WebElementSource anyClickableElement,
                                   FileDownloadFilter filter) throws FileNotFoundException {
    List<File> files = filter.getDownloadedFiles();
    if (files.isEmpty()) {
      throw new FileNotFoundException("Failed to download file " + anyClickableElement +
           + timeout + " ms." + filter.getResponses());
    }

    log.info("Downloaded file: " + files.get(0).getAbsolutePath());
    log.info("Just in case, all intercepted responses: " + filter.getResponses());
    return files.get(0);
  }
}

@@ -3733,13 +3733,8 @@
     
-    +
  tim

Fix code style.