package be.iminds.ilabt.jfed.experimenter_gui.preferences.subsections;

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedExperimenterGuiConfigProvider;
import be.iminds.ilabt.jfed.experimenter_gui.preferences.AbstractPreferencesSubPane;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.TextFieldWithStatus;
import be.iminds.ilabt.jfed.preferences.CorePreferenceKey;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import be.iminds.ilabt.jfed.util.IOUtils;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * EditorPane: config of JFedExperimenterGuiConfigProvider
 */
public class EditorPane extends AbstractPreferencesSubPane {
    private final static Logger LOG = LoggerFactory.getLogger(EditorPane.class);

    @FXML
    private TextFieldWithStatus guiConfigsXmlUrlTextField;

    @FXML
    private HBox guiConfigsXmlUrlTextBox;
    @FXML
    private HBox guiConfigsXmlLocalFileBox;
    @FXML
    private TextFieldWithStatus guiConfigsXmlLocalFileTextField;
    @FXML
    private Button guiConfigsXmlLocalFileBrowseButton;

    @FXML
    private RadioButton defaultGuiConfigsXml;
    @FXML
    private RadioButton onlineGuiConfigXml;
    @FXML
    private RadioButton localGuiConfigXml;
    @FXML
    private RadioButton builtinGuiConfigXml;


    @FXML
    private BorderPane root;

    @FXML
    private Label extraGuiConfigsLabel;

    @FXML
    private Label extraGuiConfigsFlagsLabel;
    @FXML
    private TextField extraGuiConfigsFlagsField;

    @Override
    public Node getRoot() {
        return root;
    }

    private final JFedGuiPreferences jFedPreferences;

    @Inject
    EditorPane(JFedGuiPreferences jFedPreferences) {
        super("GUI Editor", true/*advanced*/);
        this.jFedPreferences = jFedPreferences;
    }

    @FXML
    private void initialize() {

        guiConfigsXmlLocalFileBox.managedProperty().bind(guiConfigsXmlLocalFileBox.visibleProperty());
        guiConfigsXmlLocalFileBox.visibleProperty().bind(localGuiConfigXml.selectedProperty());

        guiConfigsXmlUrlTextBox.managedProperty().bind(guiConfigsXmlUrlTextBox.visibleProperty());
        guiConfigsXmlUrlTextBox.visibleProperty().bind(onlineGuiConfigXml.selectedProperty());

        guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
        guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);

        guiConfigsXmlLocalFileTextField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue) {
                guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.NONE);
            } else {
                testGuiConfigsXmlLocalFile();
            }
        });

        guiConfigsXmlUrlTextField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue) {
                guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.NONE);
            } else {
                testGuiConfigsXmlUrl();
            }
        });

        //load settings
        String guiConfigsSourcePref = jFedPreferences.getString(CorePreferenceKey.PREF_GUICONFIG_SOURCE);
        if (guiConfigsSourcePref.equalsIgnoreCase("AUTO") || guiConfigsSourcePref.equalsIgnoreCase("DEFAULT")) {
            defaultGuiConfigsXml.setSelected(true);
        } else {
            if (guiConfigsSourcePref.equalsIgnoreCase("BUILTIN")) {
                builtinGuiConfigXml.setSelected(true);
            } else {
                if (guiConfigsSourcePref.startsWith("file:")) {
                    localGuiConfigXml.setSelected(true);
                    guiConfigsXmlLocalFileTextField.setText(guiConfigsSourcePref.substring(5));
                    testGuiConfigsXmlLocalFile();
                } else {
                    //must be URL
                    onlineGuiConfigXml.setSelected(true);
                    guiConfigsXmlUrlTextField.setText(guiConfigsSourcePref);
                    testGuiConfigsXmlUrl();
                }
            }
        }

        extraGuiConfigsFlagsField.setText(
                jFedPreferences.getStringList(CorePreferenceKey.PREF_GUICONFIG_EXTRA_FLAGS).stream()
                        .collect(Collectors.joining(","))
        );

        updateExtraGuiConfigsLabels();
    }

    private void updateExtraGuiConfigsLabels() {
        List<File> extraFiles = JFedExperimenterGuiConfigProvider.getExtraGuiConfigFiles();
        List<File> overwriteFiles = JFedExperimenterGuiConfigProvider.getOverwriteGuiConfigFiles();
        extraGuiConfigsLabel.setText(
                "The files below will be scanned for additional GUI editor config information:" +
                        (extraFiles.isEmpty() ? "" : "\n  -  ") +
                        extraFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining("\n   - ")) +
                        (overwriteFiles.isEmpty() ? "" : "\n  - (*) ") +
                        overwriteFiles.stream().map(File::getAbsolutePath).collect(Collectors.joining("\n   - (*) ")) +
                        (overwriteFiles.isEmpty() ? ""
                                : "\n\nThe files marked with a (*) will overwrite information from other sources, the other files will never do this.")
        );
        extraGuiConfigsLabel.setVisible(!extraFiles.isEmpty() || !overwriteFiles.isEmpty());
    }

    @FXML
    private void onGuiConfigsXmlLocalFileBrowseButtonAction(ActionEvent actionEvent) {
        FileChooser dc = new FileChooser();
        dc.setTitle("Select the local Gui Editor Config Information File");

        File selectedFile = dc.showOpenDialog(guiConfigsXmlLocalFileTextField.getScene().getWindow());

        if (selectedFile != null) {
            guiConfigsXmlLocalFileTextField.setText(selectedFile.getAbsolutePath());
            testGuiConfigsXmlLocalFile();
        }
    }

    private boolean testGuiConfigsXmlUrl() {
        URL url;
        try {
            url = new URL(guiConfigsXmlUrlTextField.getText());
        } catch (MalformedURLException e) {
            guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            guiConfigsXmlUrlTextField.setStatusDetailString("Not a valid URL");
            return false;
        }

        try {
            String content = IOUtils.streamToString(url.openStream(), "UTF-8");

            if (testGuiConfigsXmlContent(content)) {
                guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.OK);
                guiConfigsXmlUrlTextField.setStatusDetailString("");
            } else {
                guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
                guiConfigsXmlUrlTextField.setStatusDetailString("Content at URL is invalid");
                LOG.warn("Invalid content in remote gui config \"" + url + "\"");
            }
            return true;
        } catch (IOException e) {
            guiConfigsXmlUrlTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            guiConfigsXmlUrlTextField.setStatusDetailString("IOException opening file: " + e.getMessage());
            LOG.warn("Failed to open user remote gui config \"" + url + "\"", e);
            return false;
        }
    }

    private boolean testGuiConfigsXmlLocalFile() {
        File file = new File(guiConfigsXmlLocalFileTextField.getText());

        if (!file.exists()) {
            guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            guiConfigsXmlLocalFileTextField.setStatusDetailString("File does not exist");
            LOG.warn("local gui config \"" + file.getAbsolutePath() + "\" does not exist");
            return false;
        }

        try {
            String content = IOUtils.fileToString(file);

            if (testGuiConfigsXmlContent(content)) {
                guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.OK);
                guiConfigsXmlLocalFileTextField.setStatusDetailString("");
            } else {
                guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
                guiConfigsXmlLocalFileTextField.setStatusDetailString("Invalid content in file");
                LOG.warn("Invalid content in user local gui config \"" + file.getAbsolutePath() + "\"");
            }
            return true;
        } catch (IOException e) {
            guiConfigsXmlLocalFileTextField.setStatus(TextFieldWithStatus.Status.ERROR);
            guiConfigsXmlLocalFileTextField.setStatusDetailString("IOException opening file: " + e.getMessage());
            LOG.warn("Failed to open user local gui config \"" + file.getAbsolutePath() + "\"", e);
            return false;
        }
    }

    private boolean testGuiConfigsXmlContent(String content) {
        //TODO add a better check!
        return content.contains("<authorities");
    }

    @Override
    public boolean check() {
        if (localGuiConfigXml.isSelected() && guiConfigsXmlLocalFileTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid Gui Editor Config Information File before saving.")
                    .showError();
            return false;
        }

        if (onlineGuiConfigXml.isSelected() && guiConfigsXmlUrlTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid Gui Editor Config Information URL before saving.")
                    .showError();
            return false;
        }

        assert extraGuiConfigsFlagsField.getText() != null;
        if (extraGuiConfigsFlagsField.getText().trim().matches(".*[^,] [^,].*")) {
            JFDialogs.create()
                    .owner(root)
                    .message("Extra fields should not contain spaces. It should be a comma seperated list.")
                    .showError();
            return false;
        }

        return true;
    }

    public boolean save() {
        if (!check()) return false;

        if (defaultGuiConfigsXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_GUICONFIG_SOURCE, "AUTO");
        if (builtinGuiConfigXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_GUICONFIG_SOURCE, "BUILTIN");
        if (localGuiConfigXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_GUICONFIG_SOURCE, "file:" + guiConfigsXmlLocalFileTextField.getText());
        if (onlineGuiConfigXml.isSelected())
            jFedPreferences.setString(CorePreferenceKey.PREF_GUICONFIG_SOURCE, guiConfigsXmlUrlTextField.getText());
        List<String> extraFlags =
                Arrays.asList(extraGuiConfigsFlagsField.getText().trim().split(","))
                        .stream().map(String::trim).filter((s) -> !s.isEmpty()).collect(Collectors.toList());
        if (!extraFlags.isEmpty()) {
            jFedPreferences.setStringList(CorePreferenceKey.PREF_GUICONFIG_EXTRA_FLAGS, extraFlags);
        } else {
            jFedPreferences.clear(CorePreferenceKey.PREF_GUICONFIG_EXTRA_FLAGS);
        }

        return true;
    }
}
