gt:       assert window == null;
context: package games.strategy.engine.framework.map.download;
  
  import static com.google.common.base.Preconditions.checkArgument;
  import static com.google.common.base.Preconditions.checkNotNull;
  import static com.google.common.base.Preconditions.checkState;
  import static com.google.common.base.Predicates.not;
  
  import games.strategy.engine.ClientContext;
  import games.strategy.engine.framework.GameRunner;
  import games.strategy.engine.framework.lookandfeel.LookAndFeelSwingFrameListener;
  import games.strategy.engine.framework.map.download.DownloadFile.DownloadState;
  import java.awt.BorderLayout;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.Rectangle;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Optional;
  import java.util.Set;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  import javax.swing.Box;
  import javax.swing.DefaultListModel;
  import javax.swing.JButton;
  import javax.swing.JEditorPane;
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JList;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.JSplitPane;
  import javax.swing.JTabbedPane;
  import javax.swing.SwingUtilities;
  import javax.swing.event.ListSelectionListener;
  import lombok.extern.java.Log;
  import org.triplea.java.Interruptibles;
  import org.triplea.java.OptionalUtils;
  import org.triplea.swing.JButtonBuilder;
  import org.triplea.swing.JFrameBuilder;
  import org.triplea.swing.SwingComponents;
  import org.triplea.swing.jpanel.JPanelBuilder;
  
  /** Window that allows for map downloads and removal. */
  @Log
  public class DownloadMapsWindow extends JFrame {
    private enum MapAction {
      INSTALL,
      UPDATE,
      REMOVE
    }
  
    private static final long serialVersionUID = -1542210716764178580L;
    private static final int WINDOW_WIDTH = 1200;
    private static final int WINDOW_HEIGHT = 700;
    private static final int DIVIDER_POSITION = WINDOW_HEIGHT - 150;
    private static final String MULTIPLE_SELECT_MSG =
        "You can select multiple maps by holding control or shift while clicking map names.";
    private static final SingletonManager SINGLETON_MANAGER = new SingletonManager();
  
    private final MapDownloadProgressPanel progressPanel;
  
    private DownloadMapsWindow(
        final Collection<String> pendingDownloadMapNames,
        final List<DownloadFileDescription> allDownloads) {
      super("Download Maps");
  
      setDefaultCloseOperation(DISPOSE_ON_CLOSE);
      setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
      setLocationRelativeTo(null);
      setMinimumSize(new Dimension(200, 200));
  
      setIconImage(JFrameBuilder.getGameIcon());
      progressPanel = new MapDownloadProgressPanel();
  
      final Set<DownloadFileDescription> pendingDownloads = new HashSet<>();
      final Collection<String> unknownMapNames = new ArrayList<>();
      for (final String mapName : pendingDownloadMapNames) {
        OptionalUtils.ifPresentOrElse(
            findMap(mapName, allDownloads),
            pendingDownloads::add,
            () -> unknownMapNames.add(mapName));
      }
      final Collection<String> installedMapNames = removeInstalledDownloads(pendingDownloads);
  
      if (!pendingDownloads.isEmpty()) {
        progressPanel.download(pendingDownloads);
      }
  
      pendingDownloads.addAll(
          ClientContext.downloadCoordinator().getDownloads().stream()
              .filter(download -> download.getDownloadState() != DownloadState.CANCELLED)
              .map(DownloadFile::getDownload)
              .collect(Collectors.toList()));
  
      if (!unknownMapNames.isEmpty() || !installedMapNames.isEmpty()) {
        SwingComponents.newMessageDialog(
            formatIgnoredPendingMapsMessage(unknownMapNames, installedMapNames));
      }
  
      final Optional<String> selectedMapName = pendingDownloadMapNames.stream().findFirst();
  
      SwingComponents.addWindowClosingListener(this, progressPanel::cancel);
  
      final JTabbedPane outerTabs = new JTabbedPane();
  
      final List<DownloadFileDescription> maps =
          filterMaps(allDownloads, DownloadFileDescription::isMap);
      outerTabs.add("Maps", newTabbedPanelForMaps(maps, pendingDownloads));
  
      final List<DownloadFileDescription> skins =
          filterMaps(allDownloads, DownloadFileDescription::isMapSkin);
      outerTabs.add(
          "Skins", newAvailableInstalledTabbedPanel(selectedMapName, skins, pendingDownloads));
  
      final List<DownloadFileDescription> tools =
          filterMaps(allDownloads, DownloadFileDescription::isMapTool);
      outerTabs.add(
          "Tools", newAvailableInstalledTabbedPanel(selectedMapName, tools, pendingDownloads));
  
      final JSplitPane splitPane =
          new JSplitPane(
              JSplitPane.VERTICAL_SPLIT, outerTabs, SwingComponents.newJScrollPane(progressPanel));
      splitPane.setDividerLocation(DIVIDER_POSITION);
      add(splitPane);
    }
  
    /**
     * Shows the Download Maps window.
     *
     * @throws IllegalStateException If this method is not called from the EDT.
     */
    public static void showDownloadMapsWindow() {
      checkState(SwingUtilities.isEventDispatchThread());
  
      showDownloadMapsWindowAndDownload(Collections.emptyList());
    }
  
    /**
     * Shows the Download Maps window and immediately begins downloading the specified map in the
     * background.
     *
     * <p>The user will be notified if the specified map is unknown.
     *
     * @param mapName The name of the map to download; must not be {@code null}.
     * @throws IllegalStateException If this method is not called from the EDT.
     */
    public static void showDownloadMapsWindowAndDownload(final String mapName) {
      checkState(SwingUtilities.isEventDispatchThread());
      checkNotNull(mapName);
  
      showDownloadMapsWindowAndDownload(Collections.singletonList(mapName));
    }
  
    /**
     * Shows the Download Maps window and immediately begins downloading the specified maps in the
     * background.
     *
     * <p>The user will be notified if any of the specified maps are unknown.
     *
     * @param mapNames The collection containing the names of the maps to download; must not be {@code
     *     null}.
     * @throws IllegalStateException If this method is not called from the EDT.
     */
    public static void showDownloadMapsWindowAndDownload(final Collection<String> mapNames) {
      checkState(SwingUtilities.isEventDispatchThread());
      checkNotNull(mapNames);
  
      SINGLETON_MANAGER.showAndDownload(mapNames);
    }
  
    private static final class SingletonManager {
      private enum State {
        UNINITIALIZED,
        INITIALIZING,
        INITIALIZED
      }
  
      private State state;
      private DownloadMapsWindow window;
  
      SingletonManager() {
        uninitialize();
      }
  
      private void uninitialize() {
        assert SwingUtilities.isEventDispatchThread();
  
        state = State.UNINITIALIZED;
        window = null;
      }
  
      void showAndDownload(final Collection<String> mapNames) {
        assert SwingUtilities.isEventDispatchThread();
  
        switch (state) {
          case UNINITIALIZED:
            initialize(mapNames);
            break;
  
          case INITIALIZING:
            logMapDownloadRequestIgnored(mapNames);
            // do nothing; window will be shown when initialization is complete
            break;
  
          case INITIALIZED:
            logMapDownloadRequestIgnored(mapNames);
            show();
            break;
  
          default:
            throw new AssertionError("unknown state");
        }
      }
  
      private void initialize(final Collection<String> mapNames) {
        assert SwingUtilities.isEventDispatchThread();
        assert state == State.UNINITIALIZED;
  
        Interruptibles.awaitResult(SingletonManager::getMapDownloadListInBackground)
            .result
            .filter(not(Collection::isEmpty))
            .ifPresent(
                downloads -> {
                  state = State.INITIALIZING;
                  createAndShow(mapNames, downloads);
                });
      }
  
      private static List<DownloadFileDescription> getMapDownloadListInBackground()
          throws InterruptedException {
        return GameRunner.newBackgroundTaskRunner()
            .runInBackgroundAndReturn(
                "Downloading list of available maps...", ClientContext::getMapDownloadList);
      }
  
      private void createAndShow(
          final Collection<String> mapNames, final List<DownloadFileDescription> downloads) {
        assert SwingUtilities.isEventDispatchThread();
        assert state == State.INITIALIZING;
  