package com.faforever.client.map;
import com.faforever.client.fx.JavaFxUtil;
import com.faforever.client.fx.WindowController;
import com.faforever.client.i18n.I18n;
import com.faforever.client.map.event.MapUploadedEvent;
import com.faforever.client.preferences.PreferencesService;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.faforever.client.fx.WindowController.WindowButtonType.CLOSE;
public class MapVaultController {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final int TOP_ELEMENT_COUNT = 7;
private static final int MAX_SUGGESTIONS = 10;
@FXML
Pane searchResultGroup;
@FXML
Pane searchResultPane;
@FXML
Pane showroomGroup;
@FXML
Pane loadingPane;
@FXML
TextField searchTextField;
@FXML
Pane newestMapsPane;
@FXML
Pane popularMapsPane;
@FXML
Pane recommendedMapsPane;
@FXML
Pane mapVaultRoot;
@Resource
MapService mapService;
@Resource
ApplicationContext applicationContext;
@Resource
MapDetailController mapDetailController;
@Resource
I18n i18n;
@Resource
EventBus eventBus;
@Resource
PreferencesService preferencesService;
private boolean initialized;
@FXML
void initialize() {
loadingPane.managedProperty().bind(loadingPane.visibleProperty());
showroomGroup.managedProperty().bind(showroomGroup.visibleProperty());
searchResultGroup.managedProperty().bind(searchResultGroup.visibleProperty());
}
@PostConstruct
void postConstruct() {
Node mapDetailRoot = mapDetailController.getRoot();
mapVaultRoot.getChildren().add(mapDetailRoot);
AnchorPane.setTopAnchor(mapDetailRoot, 0d);
AnchorPane.setRightAnchor(mapDetailRoot, 0d);
AnchorPane.setBottomAnchor(mapDetailRoot, 0d);
AnchorPane.setLeftAnchor(mapDetailRoot, 0d);
mapDetailRoot.setVisible(false);
eventBus.register(this);
}
public void setUpIfNecessary() {
if (initialized) {
return;
}
initialized = true;
displayShowroomMaps();
JavaFxUtil.makeSuggestionField(searchTextField, this::createMapSuggestions, aVoid -> onSearchMapButtonClicked());
}
private void displayShowroomMaps() {
enterLoadingState();
mapService.getMostPlayedMaps(TOP_ELEMENT_COUNT).thenAccept(maps -> populateMaps(maps, popularMapsPane))
.thenCompose(aVoid -> mapService.getMostLikedMaps(TOP_ELEMENT_COUNT)).thenAccept(maps -> populateMaps(maps, recommendedMapsPane))
.thenCompose(aVoid -> mapService.getNewestMaps(TOP_ELEMENT_COUNT)).thenAccept(maps -> populateMaps(maps, newestMapsPane))
.thenRun(this::enterShowroomState)
.exceptionally(throwable -> {
logger.warn("Could not populate maps", throwable);
return null;
});
}
@FXML
void onSearchMapButtonClicked() {
if (searchTextField.getText().isEmpty()) {
onResetButtonClicked();
return;
}
enterSearchResultState();
mapService.lookupMap(searchTextField.getText(), 100)
.thenAccept(this::displaySearchResult);
}
private void enterLoadingState() {
showroomGroup.setVisible(false);
searchResultGroup.setVisible(false);
loadingPane.setVisible(true);
}
private void populateMaps(List<MapBean> maps, Pane pane) {
ObservableList<Node> children = pane.getChildren();
Platform.runLater(() -> {
children.clear();
for (MapBean map : maps) {
MapTileController controller = applicationContext.getBean(MapTileController.class);
controller.setMap(map);
controller.setOnOpenDetailListener(this::onShowMapDetail);
children.add(controller.getRoot());
}
});
}
@FXML
void onResetButtonClicked() {
searchTextField.clear();
showroomGroup.setVisible(true);
searchResultGroup.setVisible(false);
}
private void enterSearchResultState() {
showroomGroup.setVisible(false);
searchResultGroup.setVisible(true);
loadingPane.setVisible(false);
}
private CompletionStage<Set<Label>> createMapSuggestions(String string) {
return mapService.lookupMap(string, MAX_SUGGESTIONS)
.thenApply(new Function<List<MapBean>, Set<Label>>() {
@Override
public Set<Label> apply(List<MapBean> mapBeans) {
return mapBeans.stream()
.map(result -> {
String name = result.getDisplayName();
Label item = new Label(name) {
@Override
public int hashCode() {
return getText().hashCode();
}
@Override
public boolean equals(Object obj) {
return obj != null
&& obj.getClass() == getClass()
&& getText().equals(((Label) obj).getText());
}
};
item.setUserData(name);
return item;
})
.collect(Collectors.toSet());
}
});
}
private void enterShowroomState() {
showroomGroup.setVisible(true);
searchResultGroup.setVisible(false);
loadingPane.setVisible(false);
}
@FXML
void onShowMapDetail(MapBean map) {
mapDetailController.setMap(map);
mapDetailController.getRoot().setVisible(true);
mapDetailController.getRoot().requestFocus();
}
@FXML
void onSearchMapsButtonClicked() {
if (searchTextField.getText().isEmpty()) {
onResetButtonClicked();
return;
}
enterSearchResultState();
mapService.lookupMap(searchTextField.getText(), 100)
.thenAccept(this::displaySearchResult);
}
private void displaySearchResult(List<MapBean> maps) {
showroomGroup.setVisible(false);
searchResultGroup.setVisible(true);
populateMaps(maps, searchResultPane);
}
public void onUploadMapButtonClicked() {
Platform.runLater(() -> {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setInitialDirectory(preferencesService.getPreferences().getForgedAlliance().getCustomMapsDirectory().toFile());
directoryChooser.setTitle(i18n.get("mapVault.upload.chooseDirectory"));
File result = directoryChooser.showDialog(getRoot().getScene().getWindow());
if (result == null) {
return;
}
openMapUploadWindow(result.toPath());
});
}
public Node getRoot() {
return mapVaultRoot;
}
private void openMapUploadWindow(Path path) {
MapUploadController mapUploadController = applicationContext.getBean(MapUploadController.class);
mapUploadController.setMapPath(path);
Stage mapUploadWindow = new Stage(StageStyle.TRANSPARENT);
mapUploadWindow.initModality(Modality.NONE);
mapUploadWindow.initOwner(getRoot().getScene().getWindow());
WindowController windowController = applicationContext.getBean(WindowController.class);
windowController.configure(mapUploadWindow, mapUploadController.getRoot(), true, CLOSE);
mapUploadWindow.show();
}
public void onRefreshClicked() {
mapService.evictCache();
displayShowroomMaps();
}
@Subscribe
public void onMapUploaded(MapUploadedEvent event) {
onRefreshClicked();
}
}