package com.faforever.client.mod;
import com.faforever.client.fx.JavaFxUtil;
import com.faforever.client.fx.WindowController;
import com.faforever.client.i18n.I18n;
import com.faforever.client.mod.event.ModUploadedEvent;
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.event.ActionEvent;
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 ModVaultController {
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 recommendedUiModsPane;
@FXML
Pane newestModsPane;
@FXML
Pane popularModsPane;
@FXML
Pane mostLikedModsPane;
@FXML
Pane modVaultRoot;
@Resource
ModService modService;
@Resource
ApplicationContext applicationContext;
@Resource
ModDetailController modDetailController;
@Resource
I18n i18n;
@Resource
PreferencesService preferencesService;
@Resource
EventBus eventBus;
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 modDetailRoot = modDetailController.getRoot();
modVaultRoot.getChildren().add(modDetailRoot);
AnchorPane.setTopAnchor(modDetailRoot, 0d);
AnchorPane.setRightAnchor(modDetailRoot, 0d);
AnchorPane.setBottomAnchor(modDetailRoot, 0d);
AnchorPane.setLeftAnchor(modDetailRoot, 0d);
modDetailRoot.setVisible(false);
eventBus.register(this);
}
public void setUpIfNecessary() {
if (initialized) {
return;
}
initialized = true;
displayShowroomMods();
JavaFxUtil.makeSuggestionField(searchTextField, this::createModSuggestions, aVoid -> onSearchModButtonClicked());
}
private void displayShowroomMods() {
enterLoadingState();
modService.getMostDownloadedMods(TOP_ELEMENT_COUNT).thenAccept(modInfoBeans -> populateMods(modInfoBeans, popularModsPane))
.thenCompose(aVoid -> modService.getMostLikedMods(TOP_ELEMENT_COUNT)).thenAccept(modInfoBeans -> populateMods(modInfoBeans, mostLikedModsPane))
.thenCompose(aVoid -> modService.getNewestMods(TOP_ELEMENT_COUNT)).thenAccept(modInfoBeans -> populateMods(modInfoBeans, newestModsPane))
.thenCompose(aVoid -> modService.getMostLikedUiMods(TOP_ELEMENT_COUNT)).thenAccept(modInfoBeans -> populateMods(modInfoBeans, recommendedUiModsPane))
.thenRun(this::enterShowroomState)
.exceptionally(throwable -> {
logger.warn("Could not populate mods", throwable);
return null;
});
}
@FXML
void onSearchModButtonClicked() {
if (searchTextField.getText().isEmpty()) {
onResetButtonClicked();
return;
}
enterSearchResultState();
modService.lookupMod(searchTextField.getText(), 100)
.thenAccept(this::displaySearchResult);
}
private void enterLoadingState() {
showroomGroup.setVisible(false);
searchResultGroup.setVisible(false);
loadingPane.setVisible(true);
}
private void populateMods(List<ModInfoBean> modInfoBeans, Pane pane) {
ObservableList<Node> children = pane.getChildren();
Platform.runLater(() -> {
children.clear();
for (ModInfoBean mod : modInfoBeans) {
ModTileController controller = applicationContext.getBean(ModTileController.class);
controller.setMod(mod);
controller.setOnOpenDetailListener(this::onShowModDetail);
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>> createModSuggestions(String string) {
return modService.lookupMod(string, MAX_SUGGESTIONS)
.thenApply(new Function<List<ModInfoBean>, Set<Label>>() {
@Override
public Set<Label> apply(List<ModInfoBean> modInfoBeans) {
return modInfoBeans.stream()
.map(result -> {
String name = result.getName();
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 onShowModDetail(ModInfoBean mod) {
modDetailController.setMod(mod);
modDetailController.getRoot().setVisible(true);
modDetailController.getRoot().requestFocus();
}
private void displaySearchResult(List<ModInfoBean> modInfoBeans) {
showroomGroup.setVisible(false);
searchResultGroup.setVisible(true);
populateMods(modInfoBeans, searchResultPane);
}
public void onUploadModButtonClicked(ActionEvent actionEvent) {
Platform.runLater(() -> {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setInitialDirectory(preferencesService.getPreferences().getForgedAlliance().getModsDirectory().toFile());
directoryChooser.setTitle(i18n.get("modVault.upload.chooseDirectory"));
File result = directoryChooser.showDialog(getRoot().getScene().getWindow());
if (result == null) {
return;
}
openModUploadWindow(result.toPath());
});
}
public Node getRoot() {
return modVaultRoot;
}
private void openModUploadWindow(Path path) {
ModUploadController modUploadController = applicationContext.getBean(ModUploadController.class);
modUploadController.setModPath(path);
Stage modUploadWindow = new Stage(StageStyle.TRANSPARENT);
modUploadWindow.initModality(Modality.NONE);
modUploadWindow.initOwner(getRoot().getScene().getWindow());
WindowController windowController = applicationContext.getBean(WindowController.class);
windowController.configure(modUploadWindow, modUploadController.getRoot(), true, CLOSE);
modUploadWindow.show();
}
public void onRefreshClicked() {
modService.evictModsCache();
displayShowroomMods();
}
@Subscribe
public void onModUploaded(ModUploadedEvent event) {
onRefreshClicked();
}
}