package ui;

import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import controller.Controller;
import controller.Export;
import controller.SettingsController;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Mesh;
import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import model.Settings;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;

public class Main extends Application {

    //Save the last x/y value of the mouse, used for rotating the object in the javaFX panel
    private int lastX = 0;
    private int lastY = 0;

    //Which view is currently used for the javaFX panel
    private static final int VIEW_ALL = 0;
    private static final int VIEW_CONDUCTIVE = 1;
    private static final int VIEW_NON_CONDUCTIVE = 2;
    private static final int VIEW_CROSS_SECTION = 3;

    //If creating the STLs is finished. If yes, change the javaFX view
    private static boolean finished = false;
    //If the openSCAD installation got detected. If not, show a message.
    private static boolean detected = true;

    private CheckBox accelerationCheckBox;
	private CheckBox weightCheckBox;
	private CheckBox tempCheckBox;
	private CheckBox tiltingCheckBox;
	private CheckBox pressureCheckBox;
	private Button createObjectButton;
    private ComboBox<String> comboBox;
    private Pane fxPanel;
    private Group rootGroup;
    private StackPane glassPane;
	private Parent root;
	private Spinner<String> accelerationSpinner;
	private Spinner<String> loadSpinner;

	@Override
    public void start(Stage primaryStage) throws Exception{

        root = FXMLLoader.load(getClass().getResource("MainWindow.fxml"));
        primaryStage.setTitle("Off-Line Interactor Printing");

        primaryStage.setOnCloseRequest(e -> {
			try {
				Platform.exit();
			}
			catch (Exception e1) {
				e1.printStackTrace();
			}
		});

        Node nodeAccel = root.lookup("#accelerationPane");
        TitledPane pane = (TitledPane) nodeAccel;
        BorderPane borderPane = new BorderPane();
        accelerationCheckBox = new CheckBox();
        borderPane.setLeft(accelerationCheckBox);
        BorderPane.setAlignment(accelerationCheckBox, Pos.CENTER_LEFT);
		Label accelerationLabel = new Label(" Acceleration");

        accelerationLabel.setOnMouseClicked(event -> {
			accelerationCheckBox.setSelected(!accelerationCheckBox.isSelected());
			accelerationClicked();
		});
        Image imgAccelSymbol = new Image(getClass().getResource("/pictures/acceleration.png").toString());
		ImageView imViewAccelSymbol = new ImageView(imgAccelSymbol);
		imViewAccelSymbol.setFitWidth(30);
		imViewAccelSymbol.setFitHeight(30);
        accelerationLabel.setGraphic(imViewAccelSymbol);
        borderPane.setCenter(accelerationLabel);
        pane.setGraphic(borderPane);
        accelerationCheckBox.setOnAction(event -> {
			accelerationClicked();
			event.consume();
		});

        Node nodeWeight = root.lookup("#weightPane");
        TitledPane paneWeight = (TitledPane) nodeWeight;
        BorderPane borderPaneWeight = new BorderPane();
        weightCheckBox = new CheckBox();
        borderPaneWeight.setLeft(weightCheckBox);
		BorderPane.setAlignment(weightCheckBox, Pos.CENTER_LEFT);
		Label weightLabel = new Label(" Load");
        weightLabel.setOnMouseClicked(event -> {
			weightCheckBox.setSelected(!weightCheckBox.isSelected());
			weightClicked();
		});
		Image imgWeightSymbol = new Image(getClass().getResource("/pictures/weight.png").toString());
		ImageView imViewWeightSymbol = new ImageView(imgWeightSymbol);
		imViewWeightSymbol.setFitWidth(30);
		imViewWeightSymbol.setFitHeight(30);
		weightLabel.setGraphic(imViewWeightSymbol);
        borderPaneWeight.setCenter(weightLabel);
        paneWeight.setGraphic(borderPaneWeight);
        weightCheckBox.setOnAction(event -> {
			weightClicked();
			event.consume();
		});

        Node nodeTemp = root.lookup("#temperaturePane");
        TitledPane paneTemp = (TitledPane) nodeTemp;
        BorderPane borderPaneTemp = new BorderPane();
        tempCheckBox = new CheckBox();
        borderPaneTemp.setLeft(tempCheckBox);
		BorderPane.setAlignment(tempCheckBox, Pos.CENTER_LEFT);
		Label tempLabel = new Label(" Temperature");
		tempLabel.setOnMouseClicked(event -> {
			tempCheckBox.setSelected(!tempCheckBox.isSelected());
			temperatureClicked();
		});
		Image imgTempSymbol = new Image(getClass().getResource("/pictures/temperature.png").toString());
		ImageView imViewTempSymbol = new ImageView(imgTempSymbol);
		imViewTempSymbol.setFitWidth(14);
		imViewTempSymbol.setFitHeight(30);
		tempLabel.setGraphic(imViewTempSymbol);
        borderPaneTemp.setCenter(tempLabel);
        paneTemp.setGraphic(borderPaneTemp);
        tempCheckBox.setOnAction(event -> {
			temperatureClicked();
			event.consume();
		});




        Node nodeRotation = root.lookup("#rotationPane");
        TitledPane paneRotation = (TitledPane) nodeRotation;
        BorderPane borderPaneRotation = new BorderPane();
        tiltingCheckBox = new CheckBox();
        borderPaneRotation.setLeft(tiltingCheckBox);
		BorderPane.setAlignment(tiltingCheckBox, Pos.CENTER_LEFT);
		Label rotationLabel = new Label(" Tilting");
        rotationLabel.setOnMouseClicked(event -> {
			tiltingCheckBox.setSelected(!tiltingCheckBox.isSelected());
			tiltingClicked();
		});
		Image imgRotationSymbol = new Image(getClass().getResource("/pictures/rotation.png").toString());
		ImageView imViewRotationSymbol = new ImageView(imgRotationSymbol);
		imViewRotationSymbol.setFitWidth(30);
		imViewRotationSymbol.setFitHeight(30);
		rotationLabel.setGraphic(imViewRotationSymbol);
        borderPaneRotation.setCenter(rotationLabel);
        paneRotation.setGraphic(borderPaneRotation);
        tiltingCheckBox.setOnAction(event -> {
			tiltingClicked();
			event.consume();
		});

        Node nodePressure = root.lookup("#pressurePane");
        TitledPane panePressure = (TitledPane) nodePressure;
        BorderPane borderPanePressure = new BorderPane();
        pressureCheckBox = new CheckBox();
        borderPanePressure.setLeft(pressureCheckBox);
		BorderPane.setAlignment(pressureCheckBox, Pos.CENTER_LEFT);
		Label pressureLabel = new Label(" Pressure");
        pressureLabel.setOnMouseClicked(event -> {
			pressureCheckBox.setSelected(!pressureCheckBox.isSelected());
			pressureClicked();
		});
		Image imgPressureSymbol = new Image(getClass().getResource("/pictures/pressure.png").toString());
		ImageView imViewPressureSymbol = new ImageView(imgPressureSymbol);
		imViewPressureSymbol.setFitWidth(30);
		imViewPressureSymbol.setFitHeight(30);
		pressureLabel.setGraphic(imViewPressureSymbol);
        borderPanePressure.setCenter(pressureLabel);
        panePressure.setGraphic(borderPanePressure);
        pressureCheckBox.setOnAction(event -> {
			pressureClicked();
			event.consume();
		});


        primaryStage.setScene(new Scene(root, 1280, 720));
        primaryStage.show();


		accelerationLabel.setMinWidth(((TitledPane) nodeAccel).getWidth() * 0.83);
		weightLabel.setMinWidth(((TitledPane) nodeAccel).getWidth() * 0.83);
		tempLabel.setMinWidth(((TitledPane) nodeAccel).getWidth() * 0.83);
		rotationLabel.setMinWidth(((TitledPane) nodeAccel).getWidth() * 0.83);
		pressureLabel.setMinWidth(((TitledPane) nodeAccel).getWidth() * 0.83);

		ObservableList<String> accelerationEntries = FXCollections.observableArrayList("5.0 m/s²", "12.0 m/s²");
		accelerationSpinner = new Spinner<>();
		SpinnerValueFactory<String> accelerationValueFactory = new SpinnerValueFactory<String>() {
			@Override
			public void decrement(int steps) {
				String current = this.getValue();
				int index = accelerationEntries.indexOf(current);
				this.setValue(accelerationEntries.get((accelerationEntries.size() + index - steps)%accelerationEntries.size()));
				double value = Double.parseDouble(this.getValue().replace(" m/s²", ""));
				Controller.getC().setAcceleration(value);
			}

			@Override
			public void increment(int steps) {
				String current = this.getValue();
				int index = accelerationEntries.indexOf(current);
				this.setValue(accelerationEntries.get((index + steps)%accelerationEntries.size()));
				double value = Double.parseDouble(this.getValue().replace(" m/s²", ""));
				Controller.getC().setAcceleration(value);
			}
		};
		accelerationValueFactory.setValue("5.0 m/s²");
		accelerationSpinner.setValueFactory(accelerationValueFactory);
		accelerationSpinner.setDisable(true);
		accelerationSpinner.setMaxWidth(120);

		HBox accelHBox = ((HBox)root.lookup("#accelerationContent"));
		accelHBox.getChildren().add(accelerationSpinner);
		ObservableList<Node> workingCollection = FXCollections.observableArrayList(accelHBox.getChildren());
		Collections.swap(workingCollection, 1, 3);
		Collections.swap(workingCollection, 2, 3);
		accelHBox.getChildren().setAll(workingCollection);

		ObservableList<String> loadEntries = FXCollections.observableArrayList("5.4 Kg", "10.8 Kg", "16.2 Kg");
		loadSpinner = new Spinner<>();
		SpinnerValueFactory<String> loadValueFactory = new SpinnerValueFactory<String>() {
			@Override
			public void decrement(int steps) {
				String current = this.getValue();
				int index = loadEntries.indexOf(current);
				this.setValue(loadEntries.get((loadEntries.size() + index - steps)%loadEntries.size()));
				double value = Double.parseDouble(this.getValue().replace(" Kg", ""));
				Controller.getC().setWeight((int)(value*10));
			}

			@Override
			public void increment(int steps) {
				String current = this.getValue();
				int index = loadEntries.indexOf(current);
				this.setValue(loadEntries.get((index + steps)%loadEntries.size()));
				double value = Double.parseDouble(this.getValue().replace(" Kg", ""));
				Controller.getC().setWeight((int)(value*10));
			}
		};
		loadValueFactory.setValue("5.4 Kg");
		loadSpinner.setValueFactory(loadValueFactory);
		loadSpinner.setDisable(true);
		loadSpinner.setMaxWidth(120);

		HBox loadHBox = ((HBox)root.lookup("#weightContent"));
		loadHBox.getChildren().add(loadSpinner);
		ObservableList<Node> loadWorkingCollection = FXCollections.observableArrayList(loadHBox.getChildren());
		Collections.swap(loadWorkingCollection, 1, 3);
		Collections.swap(loadWorkingCollection, 2, 3);
		loadHBox.getChildren().setAll(loadWorkingCollection);


        ToggleGroup groupTemp = new ToggleGroup();
        ((RadioButton) root.lookup("#temperatureRadio1")).setToggleGroup(groupTemp);
        ((RadioButton) root.lookup("#temperatureRadio2")).setToggleGroup(groupTemp);

        ((RadioButton) root.lookup("#temperatureRadio1")).selectedProperty().addListener((observable, oldValue, newValue) -> {
			if (newValue)
				Controller.getC().setTemperature(Settings.Temp.OVER0);
		});

        ((RadioButton) root.lookup("#temperatureRadio2")).selectedProperty().addListener((observable, oldValue, newValue) -> {
			if (newValue)
				Controller.getC().setTemperature(Settings.Temp.UNDER0);
		});

        ToggleGroup groupTilt = new ToggleGroup();
        ((RadioButton) root.lookup("#rotationRadio1")).setToggleGroup(groupTilt);
        ((RadioButton) root.lookup("#rotationRadio2")).setToggleGroup(groupTilt);

        ((RadioButton) root.lookup("#rotationRadio1")).selectedProperty().addListener((observable, oldValue, newValue) -> {
			if (newValue)
				Controller.getC().setTilt(Settings.Tilt.TILT);
		});

        ((RadioButton) root.lookup("#rotationRadio2")).selectedProperty().addListener((observable, oldValue, newValue) -> {
			if (newValue)
				Controller.getC().setTilt(Settings.Tilt.FLIP);
		});


        fxPanel = (Pane) root.lookup("#fxPanel");

        SubScene subScene = createScene();
        fxPanel.getChildren().add(subScene);
        subScene.heightProperty().bind(fxPanel.heightProperty());
        subScene.widthProperty().bind(fxPanel.widthProperty());


        fxPanel.setOnMousePressed(event -> {
			lastX = (int) event.getX();
			lastY = (int) event.getY();
		});

        fxPanel.setOnMouseDragged(event -> {
			//Rotation
			Group root1 = rootGroup;//(SubScene) fxPanel.getChildren().get(1);
			Rotate rY = (Rotate) root1.getTransforms().get(0);
			rY.setAngle(rY.getAngle()+ event.getY() - lastY);
			lastY = (int) event.getY();

			Rotate rX = (Rotate) root1.getTransforms().get(1);
			rX.setAngle(rX.getAngle()+ event.getX() - lastX);
			lastX = (int) event.getX();
		});

        fxPanel.setOnScroll(event -> {
			//Zooming
			Group root12 = rootGroup;//(SubScene) fxPanel.getChildren().get(1);
			int direction;
			if (root12.getScaleX() > 0) {
				if (event.getDeltaY() > 0)
					direction = 1;
				else
					direction = -1;
			}
			else {
				if (event.getDeltaY() > 0)
					direction = 1;
				else
					direction = 0;
			}

			root12.setScaleX(root12.getScaleX() + direction);
			root12.setScaleY(root12.getScaleY() + direction);
			root12.setScaleZ(root12.getScaleZ() + direction);
		});

        comboBox = (ComboBox<String>) root.lookup("#comboBox");
        ObservableList<String> options = FXCollections.observableArrayList(
                "Show All Parts",
                "Show Conductive Only",
                "Show Non-Conductive Only",
                "Show Cross-Section"
        );
        comboBox.getItems().addAll(options);
        comboBox.setValue(options.get(3));
        comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(() -> {
        	clearView((SubScene)fxPanel.getChildren().get(0));
			int index = -1;
			switch (newValue) {
				case "Show All Parts": index = 0; break;
				case "Show Conductive Only": index = 1; break;
				case "Show Non-Conductive Only": index = 2; break;
				case "Show Cross-Section": index = 3; break;
			}
			changeObject((SubScene)fxPanel.getChildren().get(0), index);
		}));

        createObjectButton = (Button) root.lookup("#createObjectButton");
        Image imageCreate = new Image(getClass().getResource("/pictures/cog.png").toString());
        ImageView imageViewCreate = new ImageView(imageCreate);
        imageViewCreate.setFitWidth(30);
        imageViewCreate.setFitHeight(30);
        createObjectButton.setGraphic(imageViewCreate);
        createObjectButton.setContentDisplay(ContentDisplay.LEFT);
        createObjectButton.setOnAction(event -> {
			Task task = new Task() {

				@Override
				protected Void call() {
					Export.createSTL();
					return null;
				}
			};
			task.setOnSucceeded( e -> glassPane.setVisible(false));
			task.setOnRunning(e -> {
				glassPane.setVisible(true);
				clearView((SubScene) fxPanel.getChildren().get(0));
				this.root.lookup("#saveObjectButton").setDisable(true);
			});

			new Thread(task).start();
			//Same as above

			comboBox.setDisable(false);
			Task task2 = new Task() {

				@Override
				protected Void call() {
					while (!finished) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					finished = false;
					int index = -1;
					switch (comboBox.getValue()) {
						case "Show All Parts": index = 0; break;
						case "Show Conductive Only": index = 1; break;
						case "Show Non-Conductive Only": index = 2; break;
						case "Show Cross-Section": index = 3; break;
					}
					int finalIndex = index;
					Platform.runLater(() -> changeObject((SubScene) fxPanel.getChildren().get(0), finalIndex));
					return null;
				}
			};
			new Thread(task2).start();
		});
		Button saveObjectButton = ((Button) root.lookup("#saveObjectButton"));
		Image imageSave = new Image(getClass().getResource("/pictures/save.png").toString());
		ImageView imageViewSave = new ImageView(imageSave);
		imageViewSave.setFitWidth(30);
		imageViewSave.setFitHeight(30);
		saveObjectButton.setGraphic(imageViewSave);
		saveObjectButton.setContentDisplay(ContentDisplay.LEFT);
		saveObjectButton.setOnAction(event -> {
			DirectoryChooser chooser = new DirectoryChooser();
			chooser.setTitle("Save to Folder");
			Platform.runLater(() -> {
				File file = chooser.showDialog(primaryStage);

				if (file != null) {
					Export.saveFilesTo(file);
				}
			});
		});

        Image image = new Image(getClass().getResource("/pictures/information-icon-6086.png").toString());
        ImageView imViewAccel = new ImageView(image);
        imViewAccel.setFitWidth(15);
        imViewAccel.setFitHeight(15);
		((Button) root.lookup("#accelerationInfo")).setGraphic(imViewAccel);
		ImageView imViewWeight = new ImageView(image);
		imViewWeight.setFitWidth(15);
		imViewWeight.setFitHeight(15);
		((Button) root.lookup("#weightInfo")).setGraphic(imViewWeight);
		ImageView imViewFreeze = new ImageView(image);
		imViewFreeze.setFitWidth(15);
		imViewFreeze.setFitHeight(15);
		((Button) root.lookup("#freezeInfo")).setGraphic(imViewFreeze);
		ImageView imViewMelt = new ImageView(image);
		imViewMelt.setFitWidth(15);
		imViewMelt.setFitHeight(15);
		((Button) root.lookup("#meltInfo")).setGraphic(imViewMelt);
		ImageView imViewTilt = new ImageView(image);
		imViewTilt.setFitWidth(15);
		imViewTilt.setFitHeight(15);
		((Button) root.lookup("#tiltInfo")).setGraphic(imViewTilt);
		ImageView imViewFlip = new ImageView(image);
		imViewFlip.setFitWidth(15);
		imViewFlip.setFitHeight(15);
		((Button) root.lookup("#flipInfo")).setGraphic(imViewFlip);
		ImageView imViewSqueeze = new ImageView(image);
		imViewSqueeze.setFitWidth(15);
		imViewSqueeze.setFitHeight(15);
		((Button) root.lookup("#squeezeInfo")).setGraphic(imViewSqueeze);

		Image imageSettings = new Image(getClass().getResource("/pictures/cog.png").toString());
		ImageView imageViewSettings = new ImageView(imageSettings);
		imageViewSettings.setFitWidth(20);
		imageViewSettings.setFitHeight(20);
		((Button)root.lookup("#settingsButton")).setGraphic(imageViewSettings);

		Image imgSnowflake = new Image(getClass().getResource("/pictures/snowflake.png").toString());
		ImageView snowflakeIV = new ImageView(imgSnowflake);
		snowflakeIV.setFitWidth(20);
		snowflakeIV.setFitHeight(20);
		Label snowflakeLabel = (Label) root.lookup("#temperatureFallingLabel");
		snowflakeLabel.setGraphic(snowflakeIV);

		Image imgFlame = new Image(getClass().getResource("/pictures/flame.png").toString());
		ImageView flameIV = new ImageView(imgFlame);
		flameIV.setFitWidth(20);
		flameIV.setFitHeight(20);
		Label flameLabel = (Label) root.lookup("#temperatureRisingLabel");
		flameLabel.setGraphic(flameIV);

		Image imgTilt = new Image(getClass().getResource("/pictures/tilt.png").toString());
		ImageView tiltIV = new ImageView(imgTilt);
		tiltIV.setFitWidth(20);
		tiltIV.setFitHeight(20);
		Label tiltLabel = (Label) root.lookup("#tiltLabel");
		tiltLabel.setGraphic(tiltIV);

		Image imgFlip = new Image(getClass().getResource("/pictures/flip.png").toString());
		ImageView flipIV = new ImageView(imgFlip);
		flipIV.setFitWidth(20);
		flipIV.setFitHeight(20);
		Label flipLabel = (Label) root.lookup("#flipLabel");
		flipLabel.setGraphic(flipIV);


        glassPane = new StackPane();
        glassPane.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2);");
        glassPane.setVisible(false);
        Label loadingLabel = new Label("Creating Object...");
        Image loadingImage = new Image(getClass().getResource("/pictures/loadingCircle.gif").toString());
        ImageView imView = new ImageView(loadingImage);
        imView.setFitHeight(16);
        imView.setFitWidth(128);
      //  loadingLabel.setGraphic(new ImageView(image));
		VBox vb = new VBox();

		//ProgressBar pbar = new ProgressBar();
		//pbar.setProgress(0.5);

		vb.setAlignment(Pos.CENTER);
		//vb.getChildren().add(pbar);
		vb.getChildren().add(imView);
		vb.getChildren().add(loadingLabel);
		glassPane.getChildren().add(vb);
		StackPane.setAlignment(vb, Pos.CENTER);
        ((GridPane)root).add(glassPane, 1, 0);

        //If openSCAD could not be found, show a warning. The text depends on the OS.
        if (!detected) {
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.initStyle(StageStyle.UTILITY);
                alert.setTitle("Information");
                alert.setHeaderText("Warning");
                alert.setContentText("Couldn't find openscad.exe.\nMake sure that OpenSCAD is installed and select its folder under Menu -> Settings.");

                alert.showAndWait();
            }
            else if (System.getProperty("os.name").toLowerCase().contains("linux")) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.initStyle(StageStyle.UTILITY);
                alert.setTitle("Information");
                alert.setHeaderText("Warning");
                alert.setContentText("Couldn't find openscad.\nMake sure that OpenSCAD is installed and is visible gloabaly.");

                alert.showAndWait();
            }
            else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.initStyle(StageStyle.UTILITY);
                alert.setTitle("Information");
                alert.setHeaderText("Warning");
                alert.setContentText("Couldn't find openscad.app.\nMake sure that OpenSCAD is installed and select its folder under Menu -> Settings.");

                alert.showAndWait();
            }
        }
    }

    public static String olipPath;

    public static void main(String[] args) {

    	if (System.getProperty("os.name").toLowerCase().contains("windows"))
    		olipPath = System.getenv("AppData") + File.separator + "Olip" + File.separator;
    	else if (System.getProperty("os.name").toLowerCase().contains("mac"))
    		olipPath = System.getProperty("user.home") + "/Library/Application Support/Olip/";
    	else	//Linux
    		olipPath = System.getProperty("user.home") + "/.olip/";

        //Load the settings.ini
		File file = new File(olipPath + "settings.ini");
		if (file.exists())
			SettingsController.loadSettings();
		else {
			//If there isn't a settings.ini file, create a new one and search if OpenSCAD is installed.
			//Windows: exe typically under the given path.
			if (System.getProperty("os.name").toLowerCase().contains("windows")) {
				File openscadexe = new File("C:\\Program Files\\OpenSCAD\\openscad.exe");
				if (openscadexe.exists())
					Controller.getC().setOpenSCADPath(openscadexe.toPath().getParent());
				else {
					notDetected();
				}
			}
			//Linux: OpenSCAD is visible globally, so check via command line
			else if (System.getProperty("os.name").toLowerCase().contains("linux")) {
				try {
					Process p = Runtime.getRuntime().exec("which openscad");
					p.waitFor();
					BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
					String s = in.readLine();
					if (s != null) {
						Controller.getC().setOpenSCADPath(new File(s).toPath());
					}
					else {
						notDetected();
					}
				} catch (IOException | InterruptedException e1) {
					e1.printStackTrace();
				}
			}
			//MacOS: OpenSCAD is installed as an app, so look for in in the application folder
			else if (System.getProperty("os.name").toLowerCase().contains("mac")) {
				File openscadapp = new File("/Applications/OpenSCAD.app");
				if (openscadapp.exists())
					Controller.getC().setOpenSCADPath(new File(openscadapp.toPath().toString() + "/Contents/MacOS/").toPath());
				else
					notDetected();
			}
			SettingsController.saveSettings();
		}

        launch(args);
    }

    private void accelerationClicked() {
		boolean disable = accelerationCheckBox.isSelected();

		createObjectButton.setDisable(!disable);
		disableWeight();
		disableTemperature();
		disableTilting();
		disablePressure();

		root.lookup("#accelerationLabel").setDisable(!disable);
		accelerationSpinner.setDisable(!disable);

		Controller.getC().acceleration(disable);
	}

	private void disableAcceleration() {
		accelerationCheckBox.setSelected(false);
		Controller.getC().acceleration(false);
		root.lookup("#accelerationLabel").setDisable(true);
		accelerationSpinner.setDisable(true);
	}

	private void weightClicked() {
		boolean disable = weightCheckBox.isSelected();

		createObjectButton.setDisable(!disable);
		disableAcceleration();
		disableTemperature();
		disableTilting();
		disablePressure();

		root.lookup("#weightLabel").setDisable(!disable);
		loadSpinner.setDisable(!disable);

		Controller.getC().weight(disable);
	}

	private void disableWeight() {
		weightCheckBox.setSelected(false);
		Controller.getC().weight(false);
		root.lookup("#weightLabel").setDisable(true);
		loadSpinner.setDisable(true);
	}

	private void temperatureClicked() {
		boolean disable = tempCheckBox.isSelected();

		createObjectButton.setDisable(!disable);
		disableWeight();
		disableAcceleration();
		disableTilting();
		disablePressure();

		root.lookup("#temperatureRisingLabel").setDisable(!disable);
		root.lookup("#temperatureFallingLabel").setDisable(!disable);

		root.lookup("#temperatureRadio1").setDisable(!disable);
		root.lookup("#temperatureRadio2").setDisable(!disable);

		Controller.getC().temperature(disable);
	}

	private void disableTemperature() {
		tempCheckBox.setSelected(false);
		Controller.getC().temperature(false);
		root.lookup("#temperatureRadio1").setDisable(true);
		root.lookup("#temperatureRadio2").setDisable(true);
		root.lookup("#temperatureRisingLabel").setDisable(true);
		root.lookup("#temperatureFallingLabel").setDisable(true);
	}

	private void tiltingClicked() {
		boolean disable = tiltingCheckBox.isSelected();

		createObjectButton.setDisable(!disable);
		disableWeight();
		disableTemperature();
		disableAcceleration();
		disablePressure();

		root.lookup("#rotationRadio1").setDisable(!disable);
		root.lookup("#rotationRadio2").setDisable(!disable);

		root.lookup("#tiltLabel").setDisable(!disable);
		root.lookup("#flipLabel").setDisable(!disable);

		Controller.getC().tilt(disable);
	}

	private void disableTilting() {
		tiltingCheckBox.setSelected(false);
		Controller.getC().tilt(false);
		root.lookup("#rotationRadio1").setDisable(true);
		root.lookup("#rotationRadio2").setDisable(true);

		root.lookup("#tiltLabel").setDisable(true);
		root.lookup("#flipLabel").setDisable(true);
	}

	private void pressureClicked() {
		boolean disable = pressureCheckBox.isSelected();

		createObjectButton.setDisable(!disable);
		disableWeight();
		disableTemperature();
		disableTilting();
		disableAcceleration();

		//root.lookup("#pressureContent").setDisable(!disable);

		Controller.getC().squeeze(disable);
	}

	private void disablePressure() {
		pressureCheckBox.setSelected(false);
		Controller.getC().squeeze(false);
	}

    /**
     * Initialize the JavaFX panel
     * @return	initialized panel
     */
    private SubScene createScene() {
        rootGroup  =  new  Group();
        SubScene  scene  =  new  SubScene(rootGroup, 1000, 1000, true, null);
        scene.setFill(new Color(0.9, 0.9, 0.9, 1.0));

        Timeline timer = new Timeline(new KeyFrame(Duration.seconds(0.001667), event -> {
			rootGroup.setTranslateX(fxPanel.getWidth()/2);
			rootGroup.setTranslateY(fxPanel.getHeight()/2);

		}));
        timer.setCycleCount(Timeline.INDEFINITE);
        timer.play();

		rootGroup.setScaleX(Math.min(fxPanel.getWidth()/80, fxPanel.getHeight()/80));
		rootGroup.setScaleY(Math.min(fxPanel.getWidth()/80, fxPanel.getHeight()/80));
		rootGroup.setScaleZ(Math.min(fxPanel.getWidth()/80, fxPanel.getHeight()/80));

        //rootGroup.setTranslateX(400);
        //rootGroup.setTranslateY(400);
        rootGroup.setTranslateZ(0);
        Rotate r = new Rotate();
        r.setAngle(-60);
        r.setAxis(Rotate.Y_AXIS);
        rootGroup.getTransforms().add(r);
        Rotate r2 = new Rotate();
        r.setAxis(Rotate.X_AXIS);
        r2.setAngle(-20);
        rootGroup.getTransforms().add(r2);

        rootGroup.getChildren().add(new AmbientLight(Color.WHITE));

        return scene;
    }

    /**
     * Reload the object (changes if another object got created) or just change the view mode
     * @param scene     area to show the 3d scene
     * @param viewMode  view mode, dependend on dropdown menu
     */
    private void changeObject(SubScene scene, int viewMode) {

		Label previewlabel = ((Label) this.root.lookup("#previewLabel"));
		Label propertiesLabel = ((Label) this.root.lookup("#propertiesLabel"));
		if (Settings.getSettings().isAcceleration()) {
			previewlabel.setText("Preview for Acceleration");
			propertiesLabel.setText("Acceleration Limit: " + Settings.getSettings().getAcceleration() + " m/s²\n" +
					"Fill Hole " + (Controller.getC().getFill()?"enabled":"disabled")+ "\n" +
					"Touchscreen Dot Size " + Controller.getC().getSizeScreen() + " mm\n" +
					"Finger Dot Size " + Controller.getC().getSizeFinger() + " mm");
		} else if (Settings.getSettings().isWeight()) {
			previewlabel.setText("Preview for Load");
			propertiesLabel.setText("Weight Limit: " + Settings.getSettings().getWeight()/10.0 + " Kg\n" +
					"Fill Hole " + (Controller.getC().getFill()?"enabled":"disabled")+ "\n" +
					"Touchscreen Dot Size " + Controller.getC().getSizeScreen() + " mm\n" +
					"Finger Dot Size " + Controller.getC().getSizeFinger() + " mm");
		} else if (Settings.getSettings().isTemperature()) {
			previewlabel.setText("Preview for Temperature");
			propertiesLabel.setText((Settings.getSettings().getTemperature()== Settings.Temp.OVER0?"Rising":"Falling") + " Temperature\n" +
					"Fill Hole " + (Controller.getC().getFill()?"enabled":"disabled") + "\n" +
					"Touchscreen Dot Size " + Controller.getC().getSizeScreen() + " mm\n" +
					"Finger Dot Size " + Controller.getC().getSizeFinger() + " mm");
		} else if (Settings.getSettings().isTilt()) {
			previewlabel.setText("Preview for Rotation");
			propertiesLabel.setText("Rotation by " + (Settings.getSettings().getTilting()== Settings.Tilt.TILT?"90°":"180°") + "\n" +
					"Fill Hole " + (Controller.getC().getFill()?"enabled":"disabled")+ "\n" +
					"Touchscreen Dot Size " + Controller.getC().getSizeScreen() + " mm\n" +
					"Finger Dot Size " + Controller.getC().getSizeFinger() + " mm");
		} else if (Settings.getSettings().isSqueeze()) {
			previewlabel.setText("Preview for Pressure");
			propertiesLabel.setText("Fill Hole " + (Controller.getC().getFill()?"enabled":"disabled")+ "\n" +
					"Touchscreen Dot Size " + Controller.getC().getSizeScreen() + " mm\n" +
					"Finger Dot Size " + Controller.getC().getSizeFinger() + " mm");
		} else {
			previewlabel.setText("Preview");
			propertiesLabel.setText("");
		}

        Group root = (Group) scene.getRoot();

        StlMeshImporter stlimport = new StlMeshImporter();
        String fileName;

        switch (viewMode) {
            case VIEW_ALL: fileName = Main.olipPath + "output.stl"; break;
            case VIEW_CONDUCTIVE: fileName = Main.olipPath + "output_conductive.stl"; break;
            case VIEW_NON_CONDUCTIVE: fileName = Main.olipPath + "output.stl"; break;
            case VIEW_CROSS_SECTION: fileName = Main.olipPath + "output_crosssection.stl"; break;
            default: fileName = Main.olipPath + "output.stl";
        }

        stlimport.read(new File(fileName));
        Mesh mesh = stlimport.getImport();
        MeshView meshView = new MeshView(mesh);



        PhongMaterial mat = new PhongMaterial();
        mat.setSpecularColor(Color.WHITE);
        if (viewMode == VIEW_CONDUCTIVE)
            mat.setDiffuseColor(Color.RED);
        else
            mat.setDiffuseColor(Color.LIGHTGRAY);
        meshView.setMaterial(mat);

        root.getChildren().add(meshView);

        if (viewMode == VIEW_ALL || viewMode == VIEW_CROSS_SECTION) {
            String fileName2;
            if (viewMode == VIEW_ALL)
                fileName2 = Main.olipPath + "output_conductive.stl";
            else
                fileName2 = Main.olipPath + "output_crosssection_conductive.stl";
            stlimport.read(new File(fileName2));
            Mesh mesh2 = stlimport.getImport();
            MeshView meshView2 = new MeshView(mesh2);

            PhongMaterial mat2 = new PhongMaterial();
            mat2.setSpecularColor(Color.WHITE);
            mat2.setDiffuseColor(Color.RED);
            meshView2.setMaterial(mat2);

            root.getChildren().add(meshView2);
        }
		this.root.lookup("#saveObjectButton").setDisable(false);
    }

    private void clearView(SubScene scene) {
		Group root = (Group) scene.getRoot();
		while (root.getChildren().size() > 0)
			root.getChildren().remove(0);
	}


    /**
     * Tell this frame that the creation of the STLs is finished
     */
    public static void finished() {
        finished = true;
    }

    /**
     * Tell this frame that openSCAD could not be found
     */
    private static void notDetected() {
        detected = false;
    }

}
