package net.krazyweb.starmodmanager.view;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import net.krazyweb.starmodmanager.ModManager;
import net.krazyweb.starmodmanager.data.LocalizerFactory;
import net.krazyweb.starmodmanager.data.Mod;
import net.krazyweb.starmodmanager.data.ModList;
import net.krazyweb.starmodmanager.data.Observable;
import net.krazyweb.starmodmanager.data.Observer;
import net.krazyweb.starmodmanager.data.SettingsFactory;
import net.krazyweb.starmodmanager.data.SettingsModelInterface;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ModListViewController implements Observer {
@SuppressWarnings("unused")
private static final Logger log = LogManager.getLogger(ModListViewController.class);
private ModList modList;
private List<ModView> modViews;
private ModListView view;
private List<Mod> oldModOrder;
private double y, lastY, mouseY;
private SettingsModelInterface settings;
private boolean expanded, locked;
protected ModListViewController(final ModListView view, final ModList modList) {
settings = new SettingsFactory().getInstance();
expanded = settings.getPropertyBoolean("modviewexpanded");
locked = settings.getPropertyBoolean("modlistlocked");
modList.addObserver(this);
modViews = new ArrayList<>();
this.view = view;
this.modList = modList;
this.view.build();
for (final Mod mod : modList.getMods()) {
ModView m = new ModView(mod, modList);
modViews.add(m);
this.view.addMod(m);
}
/*
* The following essentially pre-renders the view,
* preventing delay when adding it to the main window
* for the first time. I haven't found a better way
* around this problem yet. If this isn't done, it
* takes a noticeable amount of time to load the view
* on the first click, which is jarring and should be
* avoided.
*/
Stage stage = new Stage();
Scene scene = new Scene((VBox) this.view.getContent());
stage.setScene(scene);
stage.setOpacity(0);
stage.initStyle(StageStyle.UTILITY);
stage.show();
ModManager.getPrimaryStage().toFront();
stage.close();
}
protected void addModButtonClicked() {
FileChooser fileChooser = new FileChooser();
Path lastDirectory = settings.getPropertyPath("lastdirectory");
if (lastDirectory != null) {
fileChooser.setInitialDirectory(lastDirectory.toFile());
}
fileChooser.setTitle(new LocalizerFactory().getInstance().getMessage("modlistview.modfilechoosertitle"));
List<Path> paths = new ArrayList<>();
List<File> files = fileChooser.showOpenMultipleDialog(ModManager.getPrimaryStage());
if (files != null && !files.isEmpty()) {
settings.setProperty("lastdirectory", files.get(0).getParent());
for (File file : files) {
paths.add(file.toPath());
}
modList.addMods(paths);
}
}
protected void modViewMousePressed(final ModView modView, final MouseEvent event) {
if (locked) {
return;
}
oldModOrder = modList.getInstalledMods();
y = modView.getContent().getTranslateY();
lastY = y + modView.getContent().getLayoutY();
mouseY = event.getSceneY();
modView.getContent().toFront();
}
protected void modViewMouseDragged(final ModView modView, final MouseEvent event) {
if (locked) {
return;
}
int nodeHeight = (int) modView.getContent().getHeight();
modView.getContent().setTranslateY((int) (y + event.getSceneY() - mouseY));
double position = modView.getContent().getLayoutY() + modView.getContent().getTranslateY();
if (position <= 0) {
modView.getContent().setTranslateY(0 - modView.getContent().getLayoutY());
}
if (position + modView.getContent().getHeight() >= view.getModsBox().getHeight()) {
modView.getContent().setTranslateY(view.getModsBox().getHeight() - modView.getContent().getLayoutY() - modView.getContent().getHeight());
}
if (modList.getMods().indexOf(modView.getMod()) > 0 && position < lastY) {
final ModView otherModView = getModViewByMod(modList.getMods().get(modList.getMods().indexOf(modView.getMod()) - 1));
double otherModViewPos = otherModView.getContent().getTranslateY() + otherModView.getContent().getLayoutY();
int otherModViewHeight = (int) otherModView.getContent().getHeight();
int otherModViewHalfHeight = otherModViewHeight / 2;
if (!otherModView.moving && otherModViewPos > position - otherModViewHalfHeight) {
otherModView.moving = true;
modList.moveMod(modView.getMod(), 1);
view.animate(otherModView, nodeHeight + 10);
}
}
if (modList.getMods().indexOf(modView.getMod()) < modList.getMods().size() - 1 && position > lastY) {
final ModView otherModView = getModViewByMod(modList.getMods().get(modList.getMods().indexOf(modView.getMod()) + 1));
double otherModViewPos = otherModView.getContent().getTranslateY() + otherModView.getContent().getLayoutY();
int otherModViewHeight = (int) otherModView.getContent().getHeight();
int otherModViewHalfHeight = otherModViewHeight / 2;
if (!otherModView.moving && otherModViewPos < position + modView.getContent().getHeight() - otherModViewHalfHeight) {
otherModView.moving = true;
modList.moveMod(modView.getMod(), -1);
view.animate(otherModView, -nodeHeight - 10);
}
}
lastY = position;
}
protected void modViewMouseReleased(final ModView modView, final MouseEvent e) {
if (locked) {
return;
}
modList.reinstallConflictingMods(oldModOrder);
modView.getContent().setTranslateY(modList.getMods().indexOf(modView.getMod()) * 57 - modView.getContent().getLayoutY());
updateListView();
}
private ModView getModViewByMod(final Mod mod) {
for (final ModView modView : modViews) {
if (modView.getMod() == mod) {
return modView;
}
}
return null;
}
private void updateListView() {
/*
* For some stupid reason that I can't figure out, the mod panes will not retain
* their positions on screen unless they're removed and re-added to the
* mod list container.
*
* Additionally, the list must be sorted into the correct load order
* and the layout/translation data set back to 0.
*/
view.clearMods();
Collections.sort(modViews, new Comparator<ModView>(){
@Override
public int compare(ModView mv1, ModView mv2) {
return mv1.getMod().getOrder() - mv2.getMod().getOrder();
}
});
for (ModView mv : modViews) {
mv.getContent().setTranslateY(0);
mv.getContent().setLayoutY(0);
view.addMod(mv);
}
}
protected void toggleExpansion() {
expanded = !expanded;
settings.setProperty("modviewexpanded", expanded);
for (ModView modView : modViews) {
modView.expand(expanded);
}
updateListView();
}
protected void toggleLock() {
locked = !locked;
settings.setProperty("modlistlocked", locked);
}
protected void getNewMods() {
Thread t = new Thread(modList.getNewModsTask());
t.setName("Get Mods Task");
t.setDaemon(true);
t.start();
}
@Override
public void update(final Observable observable, final Object message) {
if (observable instanceof ModList && message instanceof Object[]) {
Object[] args = (Object[]) message;
if (args[0].equals("modadded")) {
ModView newModView = new ModView((Mod) args[1], modList);
modViews.add(newModView);
updateListView();
} else if (args[0].equals("moddeleted") || args[0].equals("modhidden")) {
modViews.remove(getModViewByMod((Mod) args[1]));
updateListView();
}
}
}
}