package ch.retorte.intervalmusiccompositor.ui;
import ch.retorte.intervalmusiccompositor.commons.Utf8Control;
import ch.retorte.intervalmusiccompositor.commons.platform.PlatformFactory;
import ch.retorte.intervalmusiccompositor.compilation.CompilationParameters;
import ch.retorte.intervalmusiccompositor.messagebus.DebugMessage;
import ch.retorte.intervalmusiccompositor.messagebus.ErrorMessage;
import ch.retorte.intervalmusiccompositor.spi.*;
import ch.retorte.intervalmusiccompositor.spi.decoder.AudioFileDecoder;
import ch.retorte.intervalmusiccompositor.spi.encoder.AudioFileEncoder;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageSubscriber;
import ch.retorte.intervalmusiccompositor.spi.update.UpdateAvailabilityChecker;
import ch.retorte.intervalmusiccompositor.ui.mainscreen.MainScreenController;
import com.sun.javafx.collections.ImmutableObservableList;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.stage.Stage;
import java.net.URI;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* A ui based on JavaFx.
*/
public class IntervalMusicCompositorUI extends Application implements Ui {
//---- Static
private static final String MAIN_SCREEN_LAYOUT_FILE = "/layouts/MainScreen.fxml";
public static final String CORE_RESOURCE_BUNDLE_NAME = "core_imc";
public static final String UI_RESOURCE_BUNDLE_NAME = "ui_imc";
private static MusicListControl musicListControl;
private static MusicCompilationControl musicCompilationController;
private static ProgramControl programControl;
private static ApplicationData applicationData;
private static UpdateAvailabilityChecker updateAvailabilityChecker;
private static MessageSubscriber messageSubscriber;
private static MessageProducer messageProducer;
private static MainScreenController mainScreenController;
//---- Fields
private CompilationParameters compilationParameters = new CompilationParameters();
private ResourceBundle resourceBundle = ResourceBundle.getBundle(UI_RESOURCE_BUNDLE_NAME, new Utf8Control());
private ch.retorte.intervalmusiccompositor.commons.platform.Platform platform = new PlatformFactory().getPlatform();
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
//---- Constructors
public IntervalMusicCompositorUI() { /* Default constructor needed by JavaFX Application. */ }
public IntervalMusicCompositorUI(final MusicListControl musicListControl,
final MusicCompilationControl musicCompilationController,
final ProgramControl programControl,
ApplicationData applicationData,
UpdateAvailabilityChecker updateAvailabilityChecker,
MessageSubscriber messageSubscriber,
MessageProducer messageProducer) {
IntervalMusicCompositorUI.musicListControl = musicListControl;
IntervalMusicCompositorUI.musicCompilationController = musicCompilationController;
IntervalMusicCompositorUI.programControl = programControl;
IntervalMusicCompositorUI.applicationData = applicationData;
IntervalMusicCompositorUI.updateAvailabilityChecker = updateAvailabilityChecker;
IntervalMusicCompositorUI.messageSubscriber = messageSubscriber;
IntervalMusicCompositorUI.messageProducer = messageProducer;
installUncaughtExceptionHandler();
}
//---- Methods
private void installUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
messageProducer.send(new ErrorMessage(throwable));
messageProducer.send(new DebugMessage(IntervalMusicCompositorUI.this, throwable));
});
}
@Override
public void start(Stage primaryStage) throws Exception {
Platform.setImplicitExit(true);
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(MAIN_SCREEN_LAYOUT_FILE), resourceBundle);
Parent root = fxmlLoader.load();
primaryStage.setTitle(applicationData.getProgramName());
primaryStage.setScene(new Scene(root));
primaryStage.show();
addProgramIconsTo(primaryStage);
mainScreenController = fxmlLoader.getController();
initialize(mainScreenController);
initializeCompilationParameters();
}
catch (Exception e) {
messageProducer.send(new ErrorMessage("UI failed to load due to: " + e.getMessage() + " Quitting."));
messageProducer.send(new DebugMessage(this, e));
quit();
}
}
private void initializeCompilationParameters() {
compilationParameters.setOutputPath(platform.getDesktopPath());
}
@Override
public void quit() {
Platform.exit();
}
@Override
public void stop() throws Exception {
/* Is called right before the platform exits. */
shutdownExecutorService();
programControl.quit();
super.stop();
}
private void shutdownExecutorService() {
try {
executorService.shutdown();
}
catch (Exception e) {
// nop
}
}
@Override
public void launch() {
Application.launch();
Platform.runLater(() -> {
try {
start(new Stage());
} catch (Exception e) {
e.printStackTrace();
}
});
}
private void addProgramIconsTo(Stage stage) {
stage.getIcons().add(new Image("file:images/program_icon.png"));
stage.getIcons().add(new Image("file:images/program_icon_small.png"));
}
@Override
public void setActive() {
mainScreenController.setActive();
}
@Override
public void setInactive() {
mainScreenController.setInactive();
}
@Override
public void setEnvelopeImage(WritableImage envelopeImage) {
mainScreenController.setEnvelopeImage(envelopeImage);
}
@Override
public void openInDesktopBrowser(String url) {
URI uri = getUriFor(url);
if (uri != null) {
messageProducer.send(new DebugMessage(this, "Sending URL to systems Desktop for opening in web browser: " + uri));
getHostServices().showDocument(uri.toASCIIString());
}
}
private URI getUriFor(String string) {
try {
return new URI(string);
}
catch (Exception e) {
messageProducer.send(new ErrorMessage("Not able to convert help website url to URI due to: " + e.getMessage()));
messageProducer.send(new DebugMessage(this, e));
}
return null;
}
private void initialize(MainScreenController mainScreenController) {
mainScreenController.initializeFieldsWith(this, programControl, applicationData, musicListControl, musicCompilationController, compilationParameters, messageSubscriber, messageProducer, updateAvailabilityChecker, executorService);
mainScreenController.updateOutputFileFormatWith(getAudioFileEncoders());
mainScreenController.updateAvailableDecodersWith(getAudioFileDecoderExtensions());
}
private ObservableList<AudioFileEncoder> getAudioFileEncoders() {
return new ImmutableObservableList<>(musicCompilationController.getAvailableEncoders().toArray(new AudioFileEncoder[0]));
}
private List<AudioFileDecoder> getAudioFileDecoderExtensions() {
return musicCompilationController.getAvailableDecoders();
}
}