package org.broad.igv.prefs;

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.broad.igv.DirectoryManager;
import org.broad.igv.Globals;
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.util.FileDialogUtils;
import org.broad.igv.ui.util.UIUtilities;

import javax.swing.*;
import java.awt.*;
import java.beans.EventHandler;
import java.io.*;
import java.util.*;
import java.util.List;

public class PreferenceEditorFX {

    public static void main(String[] args) throws IOException {

        open(null);

    }

    public static void open(Frame parent) throws IOException {
        List<PreferencesManager.PreferenceGroup> preferenceGroups = PreferencesManager.loadPreferenceList();

        SwingUtilities.invokeLater(() -> {
            JDialog frame = new JDialog(parent, "Preferences", true);

            final JFXPanel fxPanel = new JFXPanel();
            Platform.runLater(() -> initFX(frame, fxPanel, preferenceGroups));
            frame.add(fxPanel);
            frame.pack();
            frame.setSize(800, 600);
            frame.setLocationRelativeTo(parent);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        });
    }

    private static void initFX(final JDialog parent, JFXPanel fxPanel, List<PreferencesManager.PreferenceGroup> preferenceGroups) {

        // final Map<String, String> updatedPrefs = new HashMap<>();

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(tabPane);
        Scene scene = new Scene(borderPane);

        Map<String, Map<String, String>> updatedPreferencesMap = new HashMap<>();
        for (PreferencesManager.PreferenceGroup entry : preferenceGroups) {

            if (entry.tabLabel.equals("Hidden")) continue;

            final IGVPreferences preferences = PreferencesManager.getPreferences(entry.category);

            final Map<String, String> updatedPrefs =
                    updatedPreferencesMap.containsKey(entry.category) ? updatedPreferencesMap.get(entry.category) :
                            new HashMap<>();
            updatedPreferencesMap.put(entry.category, updatedPrefs);


            final String tabLabel = entry.tabLabel;
            Tab tab = new Tab(tabLabel);
            tab.setClosable(false);

            tabPane.getTabs().add(tab);

            ScrollPane scrollPane = new ScrollPane();
            tab.setContent(scrollPane);

            VBox vBox = new VBox();
            vBox.setFillWidth(true);
            //   vBox.prefWidthProperty().bind(pane.widthProperty());
            scrollPane.setContent(vBox);

            GridPane gridPane = new GridPane();
            gridPane.setHgap(5);
            gridPane.setVgap(5);
            vBox.getChildren().add(gridPane);

            String currentGroup = null;

            int row = 1;
            for (PreferencesManager.Preference pref : entry.preferences) {

                try {
                    Tooltip tooltip = pref.getComment() == null ? null : new Tooltip(pref.getComment());

                    if (pref.getKey().equals(PreferencesManager.SEPARATOR_KEY)) {
                        Separator sep = new Separator();
                        GridPane.setColumnSpan(sep, 4);
                        gridPane.add(sep, 1, row);
                        row++;
                        continue;
                    }

                    if (pref.getKey().equals(PreferencesManager.INFO_KEY)) {
                        row++;
                        Label label = new Label(pref.getLabel());
                        label.setStyle("-fx-font-size:16");
                        label.setStyle("-fx-font-weight: bold");
                        GridPane.setColumnSpan(label, 4);
                        gridPane.add(label, 1, row);
                        row += 2;
                        continue;
                    }

                    if (pref.group != null && !pref.group.equals(currentGroup)) {
                        // Start a new group
                        row = 0;

                        currentGroup = pref.group;
                        gridPane = new GridPane();
                        gridPane.setHgap(5);
                        gridPane.setVgap(5);

                        vBox.getChildren().add(gridPane);
                        TitledPane tp = new TitledPane(currentGroup, gridPane);
                        tp.setCollapsible(false);

                        vBox.getChildren().add(tp);
                        vBox.setMargin(tp, new Insets(10, 0, 0, 0));

                    }

                    if (pref.getType().equals("boolean")) {
                        CheckBox cb = new CheckBox(pref.getLabel());
                        cb.setSelected(preferences.getAsBoolean(pref.getKey()));
                        cb.setOnAction(event -> {
                            updatedPrefs.put(pref.getKey(), Boolean.toString(cb.isSelected()));
                            System.out.println("Set " + pref.getLabel() + ": " + cb.isSelected());
                        });
                        GridPane.setColumnSpan(cb, 2);
                        gridPane.add(cb, 1, row);

                        if (tooltip != null) {
                            cb.setTooltip(tooltip);
                        }

                    } else if (pref.getType().startsWith("select")) {
                        Label label = new Label(pref.getLabel());
                        String[] selections = Globals.whitespacePattern.split(pref.getType())[1].split("\\|");
                        final ComboBox comboBox = new ComboBox(FXCollections.observableArrayList(Arrays.asList(selections)));
                        comboBox.valueProperty().setValue(pref.getDefaultValue());
                        comboBox.valueProperty().addListener((ov, t, t1) -> {
                            System.out.println("Set " + pref.getLabel() + " " + comboBox.valueProperty().toString());
                        });
                        gridPane.add(label, 1, row);
                        GridPane.setColumnSpan(comboBox, 3);
                        gridPane.add(comboBox, 2, row);

                        if (tooltip != null) {
                            label.setTooltip(tooltip);
                            comboBox.setTooltip(tooltip);
                        }

                    } else {
                        Label label = new Label(pref.getLabel());
                        TextField field = new TextField(preferences.get(pref.getKey()));
                        field.setPrefWidth(500);
                        field.setOnAction(event -> {
                            final String text = field.getText();
                            if (validate(text, pref.getType())) {
                                updatedPrefs.put(pref.getKey(), text);
                            } else {
                                field.setText(preferences.get(pref.getKey()));
                            }
                        });
                        field.focusedProperty().addListener((observable, oldValue, newValue) -> {
                            if (newValue == false) {
                                final String text = field.getText();
                                if (validate(text, pref.getType())) {
                                    updatedPrefs.put(pref.getKey(), text);
                                } else {
                                    field.setText(preferences.get(pref.getKey()));
                                }
                            }
                        });

                        gridPane.add(label, 1, row);
                        gridPane.add(field, 2, row);


                        if (tooltip != null) {
                            label.setTooltip(tooltip);
                            field.setTooltip(tooltip);
                        }
                    }


                    row++;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            if (tabLabel.equalsIgnoreCase("Cram")) {
                // Add Cram cache directory management at the end.  This is a special case

                String currentDirectory = DirectoryManager.getFastaCacheDirectory().getAbsolutePath();
                final Label currentDirectoryLabel = new Label("Cache directory: " + currentDirectory);
                final Button moveButton = new Button("Move...");
                row++;
                gridPane.add(currentDirectoryLabel, 1, row);
                GridPane.setHalignment(moveButton, HPos.LEFT);
                gridPane.add(moveButton, 2, row);

                moveButton.setOnAction(event -> {
                    // Do this on the Swing thread until we port to javafx file dialog
                    UIUtilities.invokeOnEventThread(() -> {
                        final File directory = DirectoryManager.getFastaCacheDirectory();
                        final File newDirectory = FileDialogUtils.chooseDirectory("Select cache directory", DirectoryManager.getUserDirectory());
                        if (newDirectory != null && !newDirectory.equals(directory)) {
                            DirectoryManager.moveDirectoryContents(directory, newDirectory);
                            Platform.runLater(() -> currentDirectoryLabel.setText(newDirectory.getAbsolutePath()));
                        }
                    });
                });

            }


            if (tabLabel.equalsIgnoreCase("Advanced")) {
                // Add IGV directory management at the end.  This is a special case
                String currentDirectory = DirectoryManager.getIgvDirectory().getAbsolutePath();
                final Label currentDirectoryLabel = new Label("IGV Directory: " + currentDirectory);
                final Button moveButton = new Button("Move...");
                row++;
                gridPane.add(currentDirectoryLabel, 1, row);
                GridPane.setHalignment(moveButton, HPos.RIGHT);
                gridPane.add(moveButton, 2, row);

                moveButton.setOnAction(event -> {
                    // Do this on the Swing thread until we port to javafx file dialog
                    UIUtilities.invokeOnEventThread(() -> {
                        final File igvDirectory = DirectoryManager.getIgvDirectory();
                        final File newDirectory = FileDialogUtils.chooseDirectory("Select IGV directory", DirectoryManager.getUserDirectory());
                        if (newDirectory != null && !newDirectory.equals(igvDirectory)) {
                            DirectoryManager.moveIGVDirectory(newDirectory);
                            Platform.runLater(() -> currentDirectoryLabel.setText(newDirectory.getAbsolutePath()));
                        }
                    });
                });

            }
        }

        HBox hbox = new HBox();
        hbox.setAlignment(Pos.CENTER_RIGHT);
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(5);
        hbox.setStyle("-fx-background-color: #336699;");

        Button cancelBUtton = new Button("Cancel");
        cancelBUtton.setPrefSize(100, 20);
        cancelBUtton.setOnAction((event) -> {
            SwingUtilities.invokeLater(() -> parent.setVisible(false));
        });

        Button saveButton = new Button("Save");
        saveButton.setPrefSize(100, 20);
        saveButton.setDefaultButton(true);
        saveButton.setOnAction((event) -> {
            PreferencesManager.updateAll(updatedPreferencesMap);
            SwingUtilities.invokeLater(() -> parent.setVisible(false));
            if (IGV.hasInstance()) {
                IGV.getInstance().doRefresh();
            }
        });
        hbox.getChildren().addAll(cancelBUtton, saveButton);

        borderPane.setBottom(hbox);

        fxPanel.setScene(scene);
    }


    private static boolean validate(String text, String type) {

        if (type.equals("integer")) {
            try {
                Integer.parseInt(text);
            } catch (NumberFormatException e) {
                return false;
            }
        } else if (type.equals("float")) {
            try {
                Double.parseDouble(text);
            } catch (NumberFormatException e) {
                return false;
            }
        }

        return true;
    }


}
