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

import be.iminds.ilabt.jfed.experimenter_gui.preferences.AbstractPreferencesSubPane;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.LabelWithStatus;
import be.iminds.ilabt.jfed.experimenter_gui.util.ui.TextFieldWithStatus;
import be.iminds.ilabt.jfed.lowlevel.GeniUser;
import be.iminds.ilabt.jfed.lowlevel.GeniUserProvider;
import be.iminds.ilabt.jfed.lowlevel.ssh_key_info.PuTTYFilesKeyInfo;
import be.iminds.ilabt.jfed.lowlevel.ssh_key_info.SshKeyInfoFactory;
import be.iminds.ilabt.jfed.preferences.CorePreferenceKey;
import be.iminds.ilabt.jfed.preferences.GuiPreferenceKey;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.ssh_terminal_tool.putty.PageantHelper;
import be.iminds.ilabt.jfed.ssh_terminal_tool.putty.SshKeyInfoUnlocker;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.File;
import java.util.HashSet;
import java.util.Set;

/**
 * PuttyPane
 */
public class PuttyPane extends AbstractPreferencesSubPane {
    private final static Logger LOG = LoggerFactory.getLogger(PuttyPane.class);

    private static final FileChooser.ExtensionFilter PPK_FILE_EXTENSION =
            new FileChooser.ExtensionFilter("PuTTY Private Key (*.ppk)", "*.ppk");


    @FXML
    private TextFieldWithStatus puttyDirTextField;
    @FXML
    private Button puttyDirBrowseButton;

    @FXML
    protected HBox puttyDirScanResult;
    @FXML
    protected LabelWithStatus puttyDirScanPuttyLabel;
    @FXML
    protected LabelWithStatus puttyDirScanPageantLabel;
    @FXML
    protected LabelWithStatus puttyDirScanPlinkLabel;

    @FXML
    protected CheckBox pageantCheckBox;
    @FXML
    protected CheckBox x11ForwardCheckBox;
    @FXML
    protected CheckBox agentForwardCheckBox;
    @FXML
    private BorderPane root;

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

    private final GeniUserProvider geniUserProvider;
    private final JFedGuiPreferences jFedPreferences;
    private final PageantHelper pageantHelper;
    private final SshKeyInfoUnlocker sshKeyInfoUnlocker;

    @Inject
    PuttyPane(GeniUserProvider geniUserProvider, JFedGuiPreferences jFedPreferences,
              PageantHelper pageantHelper, SshKeyInfoUnlocker sshKeyInfoUnlocker) {
        super("PuTTY", false);
        this.geniUserProvider = geniUserProvider;
        this.jFedPreferences = jFedPreferences;
        this.pageantHelper = pageantHelper;
        this.sshKeyInfoUnlocker = sshKeyInfoUnlocker;
    }

    @FXML
    private void initialize() {
        puttyDirTextField.focusedProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue) {
                puttyDirTextField.setStatus(TextFieldWithStatus.Status.NONE);
            } else {
                testPuttyDir();
            }
        });

        //load settings

        if (jFedPreferences.getFile(GuiPreferenceKey.PREF_PUTTY_DIRECTORY) != null) {
            puttyDirTextField.setText(jFedPreferences.getFile(GuiPreferenceKey.PREF_PUTTY_DIRECTORY).getPath());
        } else {
            //try to detect the location of PuTTY
            String puttyDir = JFedGuiPreferences.findPuTTYDirectory();

            if (puttyDir != null) {
                puttyDirTextField.setText(puttyDir);
            }
        }

        pageantCheckBox.setSelected(jFedPreferences.getBoolean(GuiPreferenceKey.PREF_SSHAGENT_USE, true));
        x11ForwardCheckBox.setSelected(jFedPreferences.getBoolean(GuiPreferenceKey.PREF_PUTTY_X11_FORWARD, true));
        agentForwardCheckBox.setSelected(jFedPreferences.getBoolean(GuiPreferenceKey.PREF_PUTTY_AGENT_FORWARD, true));

        //evaluate inputs
        testPuttyDir();

        //set focus
        Platform.runLater(() -> {
            if (puttyDirTextField.getText().length() == 0)
                puttyDirBrowseButton.requestFocus();
            else
                puttyDirBrowseButton.requestFocus();
        });

    }

    private void testPuttyDir() {
        if (puttyDirTextField.getText().length() == 0) {
            puttyDirTextField.setStatus(TextFieldWithStatus.Status.NONE);
            return;
        }

        puttyDirTextField.setStatus(validatePuTTYDirectory(puttyDirTextField.getText()) ?
                TextFieldWithStatus.Status.OK :
                TextFieldWithStatus.Status.ERROR);

        puttyDirScanResult.setVisible(true);
        puttyDirScanResult.setManaged(true);

        boolean puttyOk;
        boolean pageantOk;
        boolean plinkOk;

        File puttyDir = new File(puttyDirTextField.getText());
        if (!puttyDir.exists() || !puttyDir.isDirectory()) {
            puttyOk = false;
            pageantOk = false;
            plinkOk = false;
        } else {
            puttyOk = new File(puttyDir, "putty.exe").exists();
            pageantOk = new File(puttyDir, "pageant.exe").exists();
            plinkOk = new File(puttyDir, "plink.exe").exists();
        }

        if (puttyOk) {
            puttyDirScanPuttyLabel.setText("putty.exe is present: can open SSH terminals");
            puttyDirScanPuttyLabel.setStatus(LabelWithStatus.Status.OK);
        } else {
            puttyDirScanPuttyLabel.setText("putty.exe is missing: cannot open SSH terminals");
            puttyDirScanPuttyLabel.setStatus(LabelWithStatus.Status.ERROR);
        }
        if (pageantOk) {
            puttyDirScanPageantLabel.setText("pageant.exe is present");
            puttyDirScanPageantLabel.setStatus(LabelWithStatus.Status.OK);
        } else {
            puttyDirScanPageantLabel.setText("pageant.exe is missing");
            puttyDirScanPageantLabel.setStatus(LabelWithStatus.Status.ERROR);
        }
        if (plinkOk) {
            puttyDirScanPlinkLabel.setText("plink.exe is present: can use SSH proxy");
            puttyDirScanPlinkLabel.setStatus(LabelWithStatus.Status.OK);
        } else {
            puttyDirScanPlinkLabel.setText("plink.exe is missing: cannot use SSH proxy");
            puttyDirScanPlinkLabel.setStatus(LabelWithStatus.Status.ERROR);
        }

        pageantCheckBox.setDisable(!pageantOk);
        if (!pageantOk)
            pageantCheckBox.setSelected(false);
    }

    private boolean validatePuTTYDirectory(String dir) {
        return JFedGuiPreferences.isValidPuttyDir(new File(dir));
    }


    @FXML
    private void onPuttyDirBrowseButtonAction(ActionEvent actionEvent) {

        DirectoryChooser dc = new DirectoryChooser();
        dc.setTitle("Select the PuTTY Installation Directory");

        File selectedDir = dc.showDialog(puttyDirTextField.getScene().getWindow());

        if (selectedDir != null) {
            puttyDirTextField.setText(selectedDir.getAbsolutePath());
            testPuttyDir();
        }
    }

    @Override
    public boolean check() {
        if (puttyDirTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid PuTTY installation directory before saving. This directory needs to contain: putty.exe puttygen.exe and plink.exe")
                    .showError();
            return false;
        }

        return true;
    }

    public boolean save() {
        if (puttyDirTextField.getStatus() != TextFieldWithStatus.Status.OK) {
            JFDialogs.create()
                    .owner(root)
                    .message("Please provide a valid PuTTY installation directory before saving. This directory needs to contain: putty.exe puttygen.exe and plink.exe")
                    .showError();
            return false;
        }

        jFedPreferences.setFile(GuiPreferenceKey.PREF_PUTTY_DIRECTORY, new File(puttyDirTextField.getText()));

        jFedPreferences.setBoolean(GuiPreferenceKey.PREF_SSHAGENT_USE, pageantCheckBox.isSelected());

        jFedPreferences.setBoolean(GuiPreferenceKey.PREF_PUTTY_X11_FORWARD, x11ForwardCheckBox.isSelected());

        jFedPreferences.setBoolean(GuiPreferenceKey.PREF_PUTTY_AGENT_FORWARD, agentForwardCheckBox.isSelected());

        //Ok, finally, register keys with pageant
        if (jFedPreferences.getBoolean(GuiPreferenceKey.PREF_SSHAGENT_USE)) {
            Set<PuTTYFilesKeyInfo> keys = new HashSet<>();

            assert geniUserProvider.isUserLoggedIn();

            PuTTYFilesKeyInfo sshKeyInfo = jFedPreferences.getCustomSshKeyInfo();
            if (sshKeyInfo == null ) {
                GeniUser geniUser = geniUserProvider.getLoggedInGeniUser();
                sshKeyInfo = SshKeyInfoFactory.createGeniUserSshKeyInfo(geniUser);
            }
            keys.add(sshKeyInfo);

            if (!jFedPreferences.getString(CorePreferenceKey.PREF_SSHPROXY_USE_FOR_SSH).equalsIgnoreCase("NEVER")) {
                final GeniUser geniUser = geniUserProvider.getLoggedInGeniUser();
                keys.add(SshKeyInfoFactory.createGeniUserSshKeyInfo(geniUser));
            }

            pageantHelper.registerKeys(keys, sshKeyInfoUnlocker);
        }

        return true;
    }
}
