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

import be.iminds.ilabt.jfed.experimenter_gui.preferences.AbstractPreferencesSubPane;
import be.iminds.ilabt.jfed.preferences.GuiPreferenceKey;
import be.iminds.ilabt.jfed.preferences.JFedGuiPreferences;
import be.iminds.ilabt.jfed.ssh_terminal_tool.unix.TerminalFinder;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import be.iminds.ilabt.jfed.util.OSDetector;
import be.iminds.ilabt.jfed.util.tmp_file_helpers.TmpContentFile;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

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

    @FXML protected TextArea terminalCommandTextArea;
    @FXML private Label terminalCommandLabel;
    @FXML
    private BorderPane root;

    private final JFedGuiPreferences jFedPreferences;

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

    @Inject
    TerminalCommandPane(JFedGuiPreferences jFedPreferences) {
        super("External Terminal Command", false);
        this.jFedPreferences = jFedPreferences;
    }

    @FXML
    private void initialize() {
        if (OSDetector.findOS() == OSDetector.OS.MAC) {
            terminalCommandLabel.setText("AppleScript commands:");
        }


        if (jFedPreferences.getString(GuiPreferenceKey.PREF_COMMAND_UNIX_TERMINAL) != null)
            terminalCommandTextArea.setText(jFedPreferences.getString(GuiPreferenceKey.PREF_COMMAND_UNIX_TERMINAL));
    }

    @FXML
    private void onAutoDetectTerminalButtonClick() {
        String command = TerminalFinder.findLinuxTerminalCommand();
        if (command != null) {
            if (!Objects.equals(command, terminalCommandTextArea.getText()))
                terminalCommandTextArea.setText(command);
            else
                JFDialogs.create()
                        .owner(this.getRoot().getScene().getWindow())
                        .message("Auto detected terminal command is the same as the shown command.")
                        .showInformation();
        } else
            JFDialogs.create()
                    .owner(this.getRoot().getScene().getWindow())
                    .message("Could not auto detect the terminal command.")
                    .showError();
    }

    @FXML
    private void onTestTerminalButtonClick() {
        String terminalCommand = terminalCommandTextArea.getText();

        if (!terminalCommand.contains("%")) {
            JFDialogs.create()
                    .owner(this.getRoot().getScene().getWindow())
                    .message("Your terminal command must contain '%' at the location on which the SSH-command can be included")
                    .showError();
            return;
        }

        String testCommandForTerminal =
                "echo Testing if terminal command works correctly...\nsleep 2\necho If you see this and the terminal does not close 'automatically,' the terminal command works correctly.\n\necho You can close the terminal manually now.";
        final TmpContentFile tmpCommandFile = new TmpContentFile("test-terminal", "sh", "#!/bin/bash \n\n" +
//                "set -v\n"+
                testCommandForTerminal + "\n/bin/bash");
        final String tmpCommandFilename = tmpCommandFile.getFilename();

        String callScriptCommand = "bash " + tmpCommandFilename;

        final List<String> commandList = new ArrayList<>();
        if (terminalCommand.contains("'%'") || terminalCommand.contains("\"%\"")) {
            terminalCommand =
                    terminalCommand.replaceFirst(Pattern.quote("'%'"), "%").replaceFirst(Pattern.quote("\"%\""), "%");

            String[] finalCommandSplit = terminalCommand.split(" ");
            for (String aFinalCommandSplit : finalCommandSplit)
                if (Objects.equals(aFinalCommandSplit, "%"))
                    commandList.add(callScriptCommand);
                else
                    commandList.add(aFinalCommandSplit);
        } else {
            terminalCommand = terminalCommand.replaceFirst(Pattern.quote("%"), callScriptCommand);
            String[] finalCommandSplit = terminalCommand.split(" ");
            Collections.addAll(commandList, finalCommandSplit);
        }

        LOG.debug("Executing command to launch terminal: " + terminalCommand);
        LOG.debug("   testCommandForTerminal: " + testCommandForTerminal);

        LOG.info("launchLinuxTerminal: commandList=" + commandList);

        Runnable processOutputMonitor = () -> {
            try {
                LOG.debug("Starting terminal using command (list): " + commandList);

                ProcessBuilder pb = new ProcessBuilder(commandList);
                pb.redirectErrorStream();

                Process p = pb.start();
                BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));

                String line;
                while ((line = input.readLine()) != null) {
                    LOG.debug("External Terminal stdout+stderr: " + line);
                }
                //note: input might close before terminal stops.
                input.close();
                try {
                    p.waitFor();
                } catch (InterruptedException e) {
                    LOG.warn("processOutputMonitor thread interrupted while waiting for terminal to stop", e);
                }
                LOG.info("Terminal command exited with exit value: " + p.exitValue());
            } catch (Exception | AssertionError e) {
                LOG.error("Exception while trying to launch external terminal: " + e.getMessage(), e);
            }

            //some terminal commands exit immediately and start terminal in background
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                //ignore
            }

            tmpCommandFile.delete();
        };
        try {
            tmpCommandFile.store();

            Thread t = new Thread(processOutputMonitor);
            t.setName("ExternalTerminal");
            t.setDaemon(true); //=> this application may exit before terminal stops
            t.start();
        } catch (Exception | AssertionError e) {
            LOG.error("Exception: " + e.getMessage(), e);
            tmpCommandFile.delete();
        }
    }


    @Override
    public boolean check() {
        if (!terminalCommandTextArea.getText().contains("%")) {
            JFDialogs.create()
                    .owner(root)
                    .message("Your terminal command must contain '%' at the location on which the SSH-command can be included")
                    .showError();
            return false;
        }

        return true;
    }
    @Override
    public boolean save() {
        jFedPreferences.setString(GuiPreferenceKey.PREF_COMMAND_UNIX_TERMINAL, terminalCommandTextArea.getText());

        return true;
    }
}
