package eu.gity.jmeter.serveremulator;


import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeModel;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.util.JMeterUtils;

import javax.swing.*;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

import static eu.gity.jmeter.serveremulator.properties.strings.ServerEmulatorPropertiesStrings.SERVER_EMULATOR_ERROR;
import static eu.gity.jmeter.serveremulator.properties.strings.ServerEmulatorPropertiesStrings.SERVER_EMULATOR_ERROR_READING_CONFIG;

/**
 * Server Emulator core class responsible for performing all tasks for managing FTP and HTTP(S) servers.
 *
 * @author Gity a.s.
 */
public class ServerEmulatorCore {

    public static final String HTTP = "http";
    public static final String FTP = "ftp";
    public static final String DNS = "DNS";
    private static final String dnsScriptName = "dns.sh";
    private static final String configRelativePath = "serveremulator/";
    private static final String scriptRelativePath = "serveremulator/";
    private static final String httpScriptName = "apachectlns.sh";
    private static final String ftpScriptName = "vsftpdctlns.sh";
    private final ServerEmulatorGui serverEmulatorGui;
    private final String jmeterHomeDir;
    private final String pathToModules = ServerEmulatorGui.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
    private String apacheMainConfigFile = "";
    private String apachePortsConf = "";
    private String apacheDocumentRoot = "";
    private String seApacheConfigFile;
    private String netnsName = "";
    private String domainName;
    private JMeterTreeNode previouslySelectedNode;
    private boolean sePreviouslyEnabled = true;
    private boolean listenersRegistered;
    private boolean sslEnable = false;

    /**
     * Constructor which also shuts down servers if any of them has been running before initialization of plugin core
     *
     * @param serverEmulatorGui reference to GUI for passing status/error messages and other information
     * @param jmeterHomeDir     path to JMeter installation where also Server Emulator auxiliary files are stored
     */
    ServerEmulatorCore(ServerEmulatorGui serverEmulatorGui, String jmeterHomeDir) throws URISyntaxException, IOException {
        extractResourcesDir(new JarFile(pathToModules), "scripts", new File("/" + FilenameUtils.getPath(pathToModules) + "serveremulator/"));
        extractResourcesDir(new JarFile(pathToModules), "images", new File("/" + FilenameUtils.getPath(pathToModules) + "serveremulator/"));
        extractResourcesDir(new JarFile(pathToModules), "apache_cfg", new File("/" + FilenameUtils.getPath(pathToModules) + "serveremulator/"));
        this.serverEmulatorGui = serverEmulatorGui;
        this.jmeterHomeDir = jmeterHomeDir + "/lib/ext/";
        if (isServerRunning(HTTP)) {
            stopHTTPServer(false);
        }
        if (isServerRunning(FTP)) {
            stopFTPServer(false);
        }
        // copy ports.conf
        apachePortsConf = this.jmeterHomeDir + scriptRelativePath + "/ports.conf";
        Path src = Paths.get(apachePortsConf);
        Path dst = Paths.get("/etc/apache2/ports.conf");
        Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);

        // copy setup for default ssl
        src = Paths.get(this.jmeterHomeDir + scriptRelativePath + "/default-ssl.conf");
        dst = Paths.get("/etc/apache2/sites-available/default-ssl.conf");
        Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
    }

    public static void excuteCommand(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.isFile()) {
            throw new IllegalArgumentException("The file " + filePath + " does not exist");
        }
        if (isLinux()) {
            Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", filePath}, null);
        } else if (isWindows()) {
            Runtime.getRuntime().exec("cmd /c start " + filePath);
        }
    }

    public static boolean isLinux() {
        String os = System.getProperty("os.name");
        return os.toLowerCase().indexOf("linux") >= 0;
    }

    public static boolean isWindows() {
        String os = System.getProperty("os.name");
        return os.toLowerCase().indexOf("windows") >= 0;
    }

    /**
     * Informs whether ActionRouter listeners were registered yet
     *
     * @return true if the listeners were already registered
     */
    boolean isListenersRegistered() {
        return this.listenersRegistered;
    }

    /**
     * Launches reading of Server Emulator config file and sets name of netns to GUI if reading was successful
     */
    private void loadConfig() {
        if ((netnsName = readSeConfig()).length() == 0) {
            JOptionPane.showMessageDialog(null, JMeterUtils.getLocaleString(SERVER_EMULATOR_ERROR_READING_CONFIG), JMeterUtils.getLocaleString(SERVER_EMULATOR_ERROR), JOptionPane.ERROR_MESSAGE);
        } else {
            serverEmulatorGui.setNsName(netnsName);
        }
    }

    /**
     * Reads Server Emulator main config file, parses data from it and checks them for validity
     *
     * @return netns name on success, empty string on error
     */
    String readSeConfig() {
        String seConfigFile = jmeterHomeDir + configRelativePath + "server_emulator.cfg";
        //serverEmulatorGui.state("Reading config file " + seConfigFile);
        try {
            for (String line : Files.readAllLines(Paths.get(seConfigFile), StandardCharsets.UTF_8)) {
                if (!line.startsWith("#") && line.split("=").length == 2) {
                    switch (line.split("=")[0]) {
                        case "netns_name":
                            netnsName = line.split("=")[1];
                            break;
                        case "apache_config":
                            apacheMainConfigFile = line.split("=")[1];
                            break;
                        case "domain_name":
                            domainName = line.split("=")[1];
                            break;
                        case "document_root":
                            apacheDocumentRoot = line.split("=")[1];
                            break;
                    }
                }
            }
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while reading configuration to file " + seConfigFile + ":\n" + e.getStackTrace().toString());
            return "";
        }
        if (netnsName.length() == 0) {
            serverEmulatorGui.state(this, "error", "Network namespace name was not found in the config file " + seConfigFile);
        } else if (!Pattern.matches("[a-zA-Z0-9_]{1,127}", netnsName)) {
            serverEmulatorGui.state(this, "error", "The network namespace name " + netnsName + " found in config file is invalid. It must be up to 127 characters long and must include only" + " letters, numbers and underscore characters.");
            return "";
        }
        if (!Files.exists(Paths.get(apacheMainConfigFile))) {
            serverEmulatorGui.state(this, "error", "The file " + apacheMainConfigFile + " referred to in the config file does not exist");
            return "";
        }
        if (!Files.exists(Paths.get(apachePortsConf))) {
            serverEmulatorGui.state(this, "error", "The file " + apachePortsConf + " referred to in the config file does not exist");
            return "";
        }
        if (domainName != null && domainName.length() > 0) {
            if (!Pattern.matches("^(https?:\\/\\/)?[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]+[a-zA-Z0-9]+(:([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$", domainName)) {
                serverEmulatorGui.state(this, "error", "The domain name " + domainName + " found in config file is invalid. The format should be [scheme://]domain-name|ip-address[:port]");
                return "";
            }
        }
        return netnsName;
    }

    /**
     * Configures network namespace and chosen interface by launching script nsconfig.sh with neccessary input parameters
     *
     * @param nsDevName name of the device which should be locked in the created namespace
     * @param ip        IPv4 address which should be set for the device inside namespace
     * @return true if the script returned 0 as exit code
     */
    boolean configureNs(String nsDevName, String ip) {
        serverEmulatorGui.state("Configuring network namespace...");
        String command = jmeterHomeDir + scriptRelativePath + "nsconfig.sh add " + netnsName + " " + nsDevName + " " + ip;
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
            if (proc.exitValue() == 0) {
                return true;
            } else {
                serverEmulatorGui.state(this, "error", "Failed to create the network namespace");
                return false;
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for network namespace configuration was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for network namespace configuration:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
    }

    /**
     * Deletes network namespace by launching script nsconfig.sh with neccessary input parameters
     *
     * @return true if the script didn't returned 3 as exit code which would mean that namespace was not deleted
     */
    boolean deleteNs(boolean silent) {
        if (!silent) {
            serverEmulatorGui.state("Deleting network namespace...");
        }
        String command = jmeterHomeDir + scriptRelativePath + "nsconfig.sh delete " + netnsName;
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
            if (proc.exitValue() == 3) { //Returns 3 if nents was not deleted
                serverEmulatorGui.state(this, "error", "The network namespace " + netnsName + " was not deleted");
                return false;
            } else {
                return true;
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for network namespace deletion was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for network namespace deletion:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
    }

    /**
     * Configuration method used if advanced MPM configuration is not enabled
     *
     * @param HTTPListenIPs a list of IPs server should listen on
     * @param port          a port server should listen on
     * @param sslEnabled    if SSL should be activated (HTTPS)
     * @return true if configuration was applied successfully
     */
    boolean configHTTPServer(ArrayList<String> HTTPListenIPs, String port, boolean sslEnabled) {
        return configHTTPServer(HTTPListenIPs, port, sslEnabled, null, null, null, null, null);
    }

    /**
     * Configuration method used if advanced MPM configuration is enabled
     *
     * @param HTTPListenIPs     a list of IPs server should listen on
     * @param port              a port server should listen on
     * @param sslEnabled        if SSL should be activated (HTTPS)
     * @param startServers      sets number of server child processes started on server startup
     * @param maxRequestWorkers limits number of simultaneous request that will be served
     * @param threadsPerChild   number of threads created by each child process
     * @param minSpareThreads   minimum number of idle threads to handle request spikes
     * @param maxSpareThreads   maximum number of idle threads
     * @return true if configuration was applied successfully
     */
    boolean configHTTPServer(ArrayList<String> HTTPListenIPs, String port, boolean sslEnabled, String startServers, String maxRequestWorkers, String threadsPerChild, String minSpareThreads, String maxSpareThreads) {
        this.sslEnable = sslEnabled;
        if (!Files.isWritable(Paths.get(apacheMainConfigFile))) {
            if (!Files.exists(Paths.get(apacheMainConfigFile))) {
                serverEmulatorGui.state(this, "error", "The file " + apacheMainConfigFile + " does not exist");
            } else {
                serverEmulatorGui.state(this, "error", "Can not write to the Apache main config file " + apacheMainConfigFile);
            }
            apacheMainConfigFile = "";
            return false;
        }

        //Writting Server Emulator Apache config file which will be then linked to main Apache config file
        seApacheConfigFile = jmeterHomeDir + configRelativePath + "server_emulator_apache.conf";
        serverEmulatorGui.state("Generating server configuration...");

        List<String> newLines = new ArrayList<>();

        newLines.add("# ServerName directive sets a domain identification. If it was not configured in Server Emulator" + " config file, IP address and port will be used.");
        if (domainName == null || domainName.equals("")) {
            domainName = HTTPListenIPs.get(0) + ":" + port;
        }
        newLines.add("ServerName " + domainName + "\n");

        newLines.add("# Listen directive binds Apache to specific IP(s) and port");
        for (String ip : HTTPListenIPs) {
            newLines.add("Listen " + ip + ":" + port);
        }
        newLines.add("");

        //Multi processing module configuration (mpm_event is intended to be used - enabling of this module is mentioned in installation instruction)
        if (startServers != null) {//Means if advanced config is enabled on GUI
            newLines.add("#Advanced MPM settings");
            newLines.add("<IfModule mpm_event_module>");
            newLines.add("\tStartServers " + startServers);
            newLines.add("\tMaxRequestWorkers " + maxRequestWorkers);
            newLines.add("\tThreadsPerChild " + threadsPerChild);
            newLines.add("\tMinSpareThreads " + minSpareThreads);
            newLines.add("\tMaxSpareThreads " + maxSpareThreads);
            newLines.add("</IfModule>\n");
        }

        try {
            Files.write(Paths.get(seApacheConfigFile), newLines, StandardCharsets.UTF_8);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while writting configuration to file " + seApacheConfigFile + ":\n" + e.getStackTrace().toString());
            return false;
        }

        // setup ssl on specific port not default 443
        if (sslEnabled) {
            try {
                String content = FileUtils.readFileToString(new File("/etc/apache2/sites-available/default-ssl.conf"), "UTF-8");
                content = content.replaceAll("<VirtualHost _default_:\\d+>", "<VirtualHost _default_:" + port + ">");
                File tempFile = new File("/etc/apache2/sites-available/default-ssl.conf");
                FileUtils.writeStringToFile(tempFile, content, "UTF-8");
            } catch (IOException e) {
                //Simple exception handling, replace with what's necessary for your use case!
                throw new RuntimeException("Generating file failed", e);
            }
        }

        removeLinkFromApacheConfig();

        serverEmulatorGui.state("Linking server configuration to main Apache configuration file...");
        BufferedWriter oFile;
        String s = System.lineSeparator();
        try {
            //Opens the file in append mode
            oFile = new BufferedWriter(new FileWriter(apacheMainConfigFile, true));
            oFile.append("# Includes configuration applied by Server Emulator JMeter plugin used for load testing" + s);
            oFile.append("Include " + seApacheConfigFile + s);
            oFile.close();
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while writting to main Apache configuration file " + apacheMainConfigFile + ":\n" + e.getStackTrace().toString());
            return false;
        }
        return true;
    }

    /**
     * Removes reference to custom configuration file generated by Server Emulator from main Apache configuration file
     */
    void removeLinkFromApacheConfig() {
        if (seApacheConfigFile != null) {
            List<String> newLines = new ArrayList<String>();
            try {
                for (String line : Files.readAllLines(Paths.get(apacheMainConfigFile), StandardCharsets.UTF_8)) {
                    if (!line.contains("Server Emulator JMeter plugin") && !line.contains(seApacheConfigFile)) {
                        newLines.add(line);
                    }
                }
                Files.write(Paths.get(apacheMainConfigFile), newLines, StandardCharsets.UTF_8);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while writting to main Apache configuration file " + apacheMainConfigFile + ":\n" + e.getStackTrace().toString());
            }
        }
    }

    /**
     * Used to terminate a server process in case normal shutdown has not succeeded
     *
     * @param command a script with parameters for termination of corresponding process
     */
    private void killServerProcess(String command) {
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for killing the server process was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for killing the server process:\n" + command + " \n" + e.getStackTrace().toString());
            }
        }
    }

    /**
     * Starts Apache HTTP server by launching script with necessary parameters, redirecting both standard output and error output to GUI.
     *
     * @return true if server was started successfully
     */
    boolean startHTTPServer(boolean nsConfigured) {
        String commandPrefix = jmeterHomeDir + scriptRelativePath + httpScriptName;
        String command;
        if (nsConfigured) {
            serverEmulatorGui.state("Starting HTTP server as a daemon process in network namespace " + netnsName + "...");
            command = commandPrefix + " startnetns " + netnsName + " " + sslEnable;
        } else {
            serverEmulatorGui.state("Starting HTTP server as a system service...");
            command = commandPrefix + " start" + " " + sslEnable;
        }

        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
            if (proc.exitValue() == 0 && isServerRunning(HTTP)) {
                serverEmulatorGui.enableHTTPOptions(false);
                serverEmulatorGui.state("HTTP server started successfuly");
                return true;
            } else {
                serverEmulatorGui.state("Failed to start HTTP server");
                serverEmulatorGui.state(proc + " " + command);
                stopHTTPServer(nsConfigured);
                return false;
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for starting Apache was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for starting HTTP server:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
    }

    /**
     * Stops Apache HTTP server by launching script with necessary parameters, redirecting both standard output and error output to GUI.
     *
     * @return true if server was stopped successfully
     */
    boolean stopHTTPServer(boolean nsConfigured) {
        String commandPrefix = jmeterHomeDir + scriptRelativePath + httpScriptName;
        String command;
        if (nsConfigured) {
            serverEmulatorGui.state("Stopping HTTP server as a process running in network namespace " + netnsName + "...");
            command = commandPrefix + " stopnetns " + netnsName + " " + sslEnable;
        } else {
            serverEmulatorGui.state("Stopping HTTP server running as a system service...");
            command = commandPrefix + " stop" + " " + sslEnable;
        }

        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
            if (proc.exitValue() == 0 && !isServerRunning(HTTP)) {
                serverEmulatorGui.state("HTTP server stopped successfuly");
                return true;
            } else {
                serverEmulatorGui.state("Failed to stop HTTP server. Trying to terminate the process...");
                killServerProcess(commandPrefix + " termnetns");
                if (!isServerRunning(HTTP)) {
                    serverEmulatorGui.state("HTTP server process successfuly terminated");
                    return true;
                }
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for stopping Apache was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for stopping HTTP server:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        } finally {
            serverEmulatorGui.enableHTTPOptions(true);
            removeLinkFromApacheConfig();
        }
        serverEmulatorGui.state("Failed to stop HTTP server");
        return false;
    }

    /**
     * Starts vsftpd FTP server by launching script with necessary parameters, redirecting both standard output and error output to GUI.
     *
     * @return true if server was started successfully
     */
    boolean startFTPServer(boolean nsConfigured) {
        String commandPrefix = jmeterHomeDir + scriptRelativePath + ftpScriptName;
        String command;
        if (nsConfigured) {
            serverEmulatorGui.state("Starting FTP server as a process in network namespace " + netnsName + "...");
            command = commandPrefix + " startnetns " + netnsName;
        } else {
            serverEmulatorGui.state("Starting FTP server as a system service...");
            command = commandPrefix + " start";
        }

        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            redirectOutput(proc, command);
            if (proc.exitValue() == 0 && isServerRunning(FTP)) {
                serverEmulatorGui.state("FTP server started successfuly");
                return true;
            } else {
                serverEmulatorGui.state("Failed to start FTP server");
                return false;
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for starting vsftpd was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for starting vsftpd:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
    }

    /**
     * Stops vsftpd FTP server by launching script with necessary parameters, redirecting both standard output and error output to GUI.
     *
     * @return true if server was stopped successfully
     */

    boolean stopFTPServer(boolean nsConfigured) {
        String commandPrefix = jmeterHomeDir + scriptRelativePath + ftpScriptName;
        String command;
        if (nsConfigured) {
            serverEmulatorGui.state("Stoppinging FTP server as a process running in network namespace " + netnsName + "...");
            command = commandPrefix + " stopnetns " + netnsName;
        } else {
            serverEmulatorGui.state("Stopping FTP server running as a system service...");
            command = commandPrefix + " stop";
        }

        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
            proc.waitFor();
            if (proc.exitValue() == 0 && !isServerRunning(FTP)) {
                proc.waitFor();
                serverEmulatorGui.state("FTP server stopped successfuly");
                return true;
            } else {
                serverEmulatorGui.state("Failed to stop FTP server. Trying to terminate the process...");
                killServerProcess(commandPrefix + " stopnetns");
                if (!isServerRunning(FTP)) {
                    serverEmulatorGui.state("FTP server process successfuly terminated");
                    return true;
                }
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for stopping vsftpd was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for stopping vsftpd:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
        serverEmulatorGui.state("Failed to stop FTP server");
        return false;
    }

    /**
     * Finds out if a server process is running by launching script with neccessary parameters.
     *
     * @param server type of server whose process should be checked (may be http or ftp)
     * @return true if the server is running
     */

    // Common funkce

    void CommonShowInterface(boolean nsConfigured, String CommonInterface){
        String command ="sudo ip addres show " + CommonInterface;
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }

    void CommonAddInterface(boolean nsConfigured, String FullCommonIP, String CommonInterface, String mask){
        serverEmulatorGui.state("IP "+FullCommonIP+" was succesfully added to interface" + CommonInterface);
        String command ="sudo ip addr add " + FullCommonIP +"/"+mask+ " dev " + CommonInterface;
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
        CommonShowInterface(nsConfigured, CommonInterface);
    }

    void CommonDelInterface(boolean nsConfigured, String FullCommonIP, String CommonInterface, String mask){
        serverEmulatorGui.state("IP "+FullCommonIP+" was succesfully deleted from interface" + CommonInterface);
        String command ="sudo ip addr del " + FullCommonIP +"/"+mask+ " dev " + CommonInterface;
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
        CommonShowInterface(nsConfigured, CommonInterface);
    }


    // DNS funkce
    void startDNSServer(boolean nsConfigured) {
        serverEmulatorGui.state("DNS server started successfully");
        serverEmulatorGui.state("New configuration file created successfully");
            String command = jmeterHomeDir + scriptRelativePath + "dns.sh start";
            String[] args = new String[]{"/bin/bash", "-c", command};
            try {
                Process proc = new ProcessBuilder(args).start();
                redirectOutput(proc, command);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
            }
    }

    void stopDNSServer(boolean nsConfigured) {
        serverEmulatorGui.state("DNS server stopped successfully");
        serverEmulatorGui.state("Configuration restored successfully");
            String command = jmeterHomeDir + scriptRelativePath + "dns.sh stop";
            String[] args = new String[]{"/bin/bash", "-c", command};
            try {
                Process proc = new ProcessBuilder(args).start();
                redirectOutput(proc, command);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
            }
    }

    void restartDNSServer(boolean nsConfigured) {
        serverEmulatorGui.state("DNS server restarted successfully");
        String command = jmeterHomeDir + scriptRelativePath + "dns.sh restart";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }

    void getDNSServerStatus(boolean nsConfigured) {
        serverEmulatorGui.state("Getting status of the DNS server...");
            String command = jmeterHomeDir + scriptRelativePath + "dns.sh status";
            String[] args = new String[]{"/bin/bash", "-c", command};
            try {
                Process proc = new ProcessBuilder(args).start();
                redirectOutput(proc, command);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
            }

    }

    void DNSRestoreConfiguration() throws IOException {
        // specify the file path of the text file to delete
        String filePath = "/etc/bind/named.conf.options";

        // create a file object representing the text file
        File file = new File(filePath);

        // check if the file exists and can be deleted
        if (file.exists() && file.canWrite()) {
            // delete the file
            file.delete();
            serverEmulatorGui.state("Configuration file has been deleted.");
        } else {
            serverEmulatorGui.state("Unable to delete configuration file.");
        }

        File oldFile = new File("/etc/bind/named.conf.optionsPuvodni");
        File newFile = new File("/etc/bind/named.conf.options");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DNS configuration file renamed successfully.");
        } else {
            serverEmulatorGui.state("Configuration file renaming failed.");
        }



    }

     void DNSCreateConfiguration(String port, String IPv4, String IPv6) throws IOException {
        File oldFile = new File("/etc/bind/named.conf.options");
        File newFile = new File("/etc/bind/named.conf.optionsPuvodni");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DNS file named.conf.options renamed successfully.");
        } else {
            serverEmulatorGui.state("DNS file named.conf.options renaming failed.");
        }

        if(serverEmulatorGui.dnsSEC.isSelected()){
            String fileName = "/etc/bind/named.conf.options";
            String fileContent = "options {\n" +
                    "\tdirectory \"/var/cache/bind\";\n" +
                    "\tforwarders {\n" +
                    "\t\t1.1.1.1;\n" +
                    "\t\t8.8.8.8;\n" +
                    "\t};\n" +
                    "\tdnssec-validation auto;\n" +
                    "\t" + "listen-on port "+port+" { " + IPv4 +"; };\n" +
                    "\t" + "listen-on-v6 port " + port + " { " + IPv6 + "; };\n" +
                    "};";


            // Create file object
            File file = new File(fileName);

            // Create file writer object
            FileWriter writer = new FileWriter(file);

            // Write content to file
            writer.write(fileContent);

            // Close writer
            writer.close();

            serverEmulatorGui.state("DNS file named.conf.options created and content written successfully.");
        }else {
            String fileName = "/etc/bind/named.conf.options";
            String fileContent = "options {\n" +
                    "\tdirectory \"/var/cache/bind\";\n" +
                    "\tforwarders {\n" +
                    "\t\t1.1.1.1;\n" +
                    "\t\t8.8.8.8;\n" +
                    "\t};\n" +
                    "\tdnssec-validation no;\n" +
                    "\t" + "listen-on port "+port+" { " + IPv4 +"; };\n" +
                    "\t" + "listen-on-v6 port " + port + " { " + IPv6 + "; };\n" +
                    "};";

            // Create file object
            File file = new File(fileName);

            // Create file writer object
            FileWriter writer = new FileWriter(file);

            // Write content to file
            writer.write(fileContent);

            // Close writer
            writer.close();

            serverEmulatorGui.state("DNS file named.conf.options file created and content written successfully.");
        }



    }
    //DNS Zone
    void DNSCreateZoneDBlocal(String IPv4, String IPv6) throws IOException {

            String fileName = "/etc/bind/db.ubuntu.local";
            String fileContent = "$TTL\t604800\n" +
                    "@\tIN\tSOA\tubuntu.local. root.localhost. (\n" +
                    "\t\t\t      1\t\t; Serial\n" +
                    "\t\t\t 604800\t\t; Refresh\n" +
                    "\t\t\t  86400\t\t; Retry\n" +
                    "\t\t\t2419200\t\t; Expire\n" +
                    "\t\t\t  86400 )\t; Negative Cache TTL\n" +
                    ";\n" +
                    "\tIN\tA\t"+IPv4+"\n" +
                    "\tIN\tAAAA\t"+IPv6+"\n" +
                    "@\tIN\tNS\tlocalhost.\n";


            // Create file object
            File file = new File(fileName);

            // Create file writer object
            FileWriter writer = new FileWriter(file);

            // Write content to file
            writer.write(fileContent);

            // Close writer
            writer.close();

    }

    void DNSCreateZoneConfLocal() throws IOException {

        File oldFile = new File("/etc/bind/named.conf.local");
        File newFile = new File("/etc/bind/named.conf.localPuvodni");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DNS file named.conf.local renamed successfully.");
        } else {
            serverEmulatorGui.state("DNS file named.conf.local renaming failed.");
        }

        String fileName = "/etc/bind/named.conf.local";
        String fileContent = "zone \"ubuntu.local\" {\n" +
                "\ttype master;\n" +
                "\tfile \"/etc/bind/db.ubuntu.local\";\n" +
                "};\n";


        // Create file object
        File file = new File(fileName);

        // Create file writer object
        FileWriter writer = new FileWriter(file);

        // Write content to file
        writer.write(fileContent);

        // Close writer
        writer.close();

    }

    void DNSDeleteZoneFiles() throws IOException {
        File oldFile = new File("/etc/bind/named.conf.localPuvodni");
        File newFile = new File("/etc/bind/named.conf.local");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DNS file named.conf.localPuvodni renamed successfully.");
        } else {
            serverEmulatorGui.state("DNS file named.conf.localPuvodni renaming failed.");
        }

        String filePath = "/etc/bind/db.ubuntu.local";

        // create a file object representing the text file
        File file = new File(filePath);

        // check if the file exists and can be deleted
        if (file.exists() && file.canWrite()) {
            // delete the file
            file.delete();
            serverEmulatorGui.state("Configuration file has been deleted.");
        } else {
            serverEmulatorGui.state("Unable to delete configuration file.");
        }

    }

    //DHCP funkce
    void getDHCPServerStatus(boolean nsConfigured) {
        serverEmulatorGui.state("Getting status of the DHCP server...");
        String command = jmeterHomeDir + scriptRelativePath + "dhcp.sh status";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

    }

    void startDHCPserver(boolean nsConfigured) {
        serverEmulatorGui.state("DHCP server started succesfully");
        String command = jmeterHomeDir + scriptRelativePath + "dhcp.sh start";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

    }

    void stopDHCPserver(boolean nsConfigured) {
        serverEmulatorGui.state("DHCP server stopped succesfully");
        String command = jmeterHomeDir + scriptRelativePath + "dhcp.sh stop";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

    }

    void restartDHCPServer(boolean nsConfigured) {
        serverEmulatorGui.state("DHCP server restarted successfully");
        String command = jmeterHomeDir + scriptRelativePath + "dhcp.sh restart";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }

    void DHCPstartFunction(String IPv4interfaces, String IPv6interfaces) throws IOException {
        File oldFile = new File("/etc/default/isc-dhcp-server");
        File newFile = new File("/etc/default/isc-dhcp-serverPuvodni");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DHCP file isc-dhcp-server renamed successfully.");
        } else {
            serverEmulatorGui.state("DHCP file isc-dhcp-server renaming failed.");
        }


        String fileName = "/etc/default/isc-dhcp-server";
        String fileContent = "# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)\n" +
                "\n" +
                "# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).\n" +
                "#DHCPDv4_CONF=/etc/dhcp/dhcpd.conf\n" +
                "#DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf\n" +
                "\n" +
                "# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).\n" +
                "#DHCPDv4_PID=/var/run/dhcpd.pid\n" +
                "#DHCPDv6_PID=/var/run/dhcpd6.pid\n" +
                "\n" +
                "# Additional options to start dhcpd with.\n" +
                "#       Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead\n" +
                "#OPTIONS=\"\"\n" +
                "\n" +
                "# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?\n" +
                "#       Separate multiple interfaces with spaces, e.g. \"eth0 eth1\".\n" +
                "INTERFACESv4=\"" + IPv4interfaces + "\"\n" +
                "INTERFACESv6=\"" + IPv6interfaces + "\"\n";


        // Create file object
        File file = new File(fileName);

        // Create file writer object
        FileWriter writer = new FileWriter(file);

        // Write content to file
        writer.write(fileContent);

        // Close writer
        writer.close();

        serverEmulatorGui.state("File isc-dhcp-server created and content written successfully.");
    }

    void DHCPstopFunction() throws IOException {
        // specify the file path of the text file to delete
        String filePath = "/etc/default/isc-dhcp-server";

        // create a file object representing the text file
        File file = new File(filePath);

        // check if the file exists and can be deleted
        if (file.exists() && file.canWrite()) {
            // delete the file
            file.delete();
            serverEmulatorGui.state("Configuration file has been deleted.");
        } else {
            serverEmulatorGui.state("Unable to delete configuration file.");
        }

        File oldFile = new File("/etc/default/isc-dhcp-serverPuvodni");
        File newFile = new File("/etc/default/isc-dhcp-server");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DHCP file isc-dhcp-serverPuvodni renamed successfully.");
        } else {
            serverEmulatorGui.state("DHCP file isc-dhcp-serverPuvodni renaming failed.");
        }



    }

    void DisplayDHCPports(boolean nsConfigured) {
        String command = "sudo lsof -i -P -n | grep dhcpd";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }

    void AddDHCPport(boolean nsConfigured, String port) {

        String command1 = "sudo chmod 777 /var/lib/dhcp/dhcpd.leases";
        String[] args1 = new String[]{"/bin/bash", "-c", command1};
        try {
            Process proc = new ProcessBuilder(args1).start();
            redirectOutput(proc, command1);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }


        String command2 = "sudo dhcpd -p " + port;
        String[] args2 = new String[]{"/bin/bash", "-c", command2};
        try {
            Process proc = new ProcessBuilder(args2).start();
            redirectOutput(proc, command2);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

        serverEmulatorGui.state("DHCP port added successfully");
    }

    void DeleteDHCPport(boolean nsConfigured, String port) {

        String command1 = "sudo chmod 777 /var/lib/dhcp/dhcpd.leases";
        String[] args1 = new String[]{"/bin/bash", "-c", command1};
        try {
            Process proc = new ProcessBuilder(args1).start();
            redirectOutput(proc, command1);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }


        String command2 = "sudo kill " + port;
        String[] args2 = new String[]{"/bin/bash", "-c", command2};
        try {
            Process proc = new ProcessBuilder(args2).start();
            redirectOutput(proc, command2);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

        serverEmulatorGui.state("DHCP port Deleted successfully");
    }

    void DisplayDHCPattachedIP(boolean nsConfigured) {
        String command = "sudo dhcp-lease-list";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }










    void DHCPCreateCommonConfig() throws IOException {
        File oldFile = new File("/etc/dhcp/dhcpd.conf");
        File newFile = new File("/etc/dhcp/dhcpd.confPuvodni");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DHCP configuration file dhcpd.conf renamed successfully.");
        } else {
            serverEmulatorGui.state("DHCP configuration  file dhcpd.conf renaming failed.");
        }


        String fileName = "/etc/dhcp/dhcpd.conf";
        String fileContent = "option domain-name \"example.org\";\n" +
                "option domain-name-servers server.example.org;\n" +
                "\n" +
                "default-lease-time 600;\n" +
                "max-lease-time 7200;\n" +
                "ddns-update-style none;\n" +
                "subnet 192.168.56.0 netmask 255.255.255.0 {\n" +
                "  range 192.168.56.20 192.168.56.25;\n" +
                "  option domain-name-servers server.example.org;\n" +
                "  option domain-name \"example.org\";\n" +
                "  option subnet-mask 255.255.255.0;\n" +
                "  option routers 192.168.56.1;\n" +
                "  option broadcast-address 192.168.56.255;\n" +
                "  default-lease-time 600;\n" +
                "  max-lease-time 7200;\n" +
                "}";

        // Create file object
        File file = new File(fileName);

        // Create file writer object
        FileWriter writer = new FileWriter(file);

        // Write content to file
        writer.write(fileContent);

        // Close writer
        writer.close();

        serverEmulatorGui.state("File dhcpd.conf created and content written successfully.");
    }

    void DHCPDeleteCommonConfig() throws IOException {
        // specify the file path of the text file to delete
        String filePath = "/etc/dhcp/dhcpd.conf";

        // create a file object representing the text file
        File file = new File(filePath);

        // check if the file exists and can be deleted
        if (file.exists() && file.canWrite()) {
            // delete the file
            file.delete();
            serverEmulatorGui.state("Configuration file has been deleted.");
        } else {
            serverEmulatorGui.state("Unable to delete configuration file.");
        }

        File oldFile = new File("/etc/dhcp/dhcpd.confPuvodni");
        File newFile = new File("/etc/dhcp/dhcpd.conf");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("DHCP configuration file dhcpd.confPuvodni renamed successfully.");
        } else {
            serverEmulatorGui.state("DHCP configuration file dhcpd.confPuvodni renaming failed.");
        }



    }





























    // NTP funkce
    void startNTPServer(boolean nsConfigured) {
        serverEmulatorGui.state("NTP server started successfully");
        serverEmulatorGui.state("New configuration file created successfully");
        String command = "sudo service ntp start";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

        String command2 = "sudo chmod 777 /etc/ntp.conf";
        String[] args2 = new String[]{"/bin/bash", "-c", command2};
        try {
            Process proc = new ProcessBuilder(args2).start();
            redirectOutput(proc, command2);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

    }
    void stopNTPServer(boolean nsConfigured) {
        serverEmulatorGui.state("NTP server stopped successfully");
        serverEmulatorGui.state("Configuration restored successfully");
        String command = "sudo service ntp stop";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

    }
    void restartNTPServer (boolean nsConfigured){
        serverEmulatorGui.state("NTP server restarted successfully");
        String command = "sudo service ntp restart";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }

        String command1 = "sudo service ntp-systemd-netif restart";
        String[] args1 = new String[]{"/bin/bash", "-c", command1};
        try {
            Process proc = new ProcessBuilder(args1).start();
            redirectOutput(proc, command1);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }
    void getNTPServerStatus(boolean nsConfigured){
        serverEmulatorGui.state("Getting status of the NTP server...");
        String command = "sudo service ntp status";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            redirectOutput(proc, command);
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while executing bash script for getting DNS server status: \n" + e.getStackTrace().toString());
        }
    }
    void NTPCreateConfiguration(String NTPinterface, String IP, String mask) throws IOException {
        File oldFile = new File("/etc/ntp.conf");
        File newFile = new File("/etc/ntp.confPuvodni");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("File ntp.conf renamed successfully.");
        } else {
            serverEmulatorGui.state("File ntp.conf renaming failed.");
        }

        if(NTPinterface.equals("")){
            String fileName = "/etc/ntp.conf";
            String fileContent =
                    " #/etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help\n" +
                            "\n" +
                            "driftfile /var/lib/ntp/ntp.drift\n" +
                            "\n" +
                            "# Leap seconds definition provided by tzdata\n" +
                            "leapfile /usr/share/zoneinfo/leap-seconds.list\n" +
                            "\n" +
                            "# Enable this if you want statistics to be logged.\n" +
                            "#statsdir /var/log/ntpstats/\n" +
                            "\n" +
                            "statistics loopstats peerstats clockstats\n" +
                            "filegen loopstats file loopstats type day enable\n" +
                            "filegen peerstats file peerstats type day enable\n" +
                            "filegen clockstats file clockstats type day enable\n" +
                            "\n" +
                            "# Specify one or more NTP servers.\n" +
                            "\n" +
                            "# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board\n" +
                            "# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for\n" +
                            "# more information.\n" +
                            "pool 0.ubuntu.pool.ntp.org iburst\n" +
                            "pool 1.ubuntu.pool.ntp.org iburst\n" +
                            "pool 2.ubuntu.pool.ntp.org iburst\n" +
                            "pool 3.ubuntu.pool.ntp.org iburst\n" +
                            "\n" +
                            "# Use Ubuntu's ntp server as a fallback.\n" +
                            "pool ntp.ubuntu.com\n" +
                            "\n" +
                            "# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for\n" +
                            "# details.  The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>\n" +
                            "# might also be helpful.\n" +
                            "#\n" +
                            "# Note that \"restrict\" applies to both servers and clients, so a configuration\n" +
                            "# that might be intended to block requests from certain clients could also end\n" +
                            "# up blocking replies from your own upstream servers.\n" +
                            "\n" +
                            "# By default, exchange time with everybody, but don't allow configuration.\n" +
                            "restrict -4 default kod notrap nomodify nopeer noquery limited\n" +
                            "restrict -6 default kod notrap nomodify nopeer noquery limited\n" +
                            "\n" +
                            "# Local users may interrogate the ntp server more closely.\n" +
                            "restrict 127.0.0.1\n" +
                            "restrict ::1\n" +
                            "\n" +
                            "# Needed for adding pool entries\n" +
                            "restrict source notrap nomodify noquery\n" +
                            "\n" +
                            "# Clients from this (example!) subnet have unlimited access, but only if\n" +
                            "# cryptographically authenticated.\n" +
                            "#restrict 192.168.123.0 mask 255.255.255.0 notrust\n" +
                            "\n" +
                            "\n" +
                            "# If you want to provide time to your local subnet, change the next line.\n" +
                            "# (Again, the address is an example only.)\n" +
                            "#broadcast 192.168.123.255\n" +
                            "\n" +
                            "# If you want to listen to time broadcasts on your local subnet, de-comment the\n" +
                            "# next lines.  Please do this only if you trust everybody on the network!\n" +
                            "#disable auth\n" +
                            "#broadcastclient\n" +
                            "logfile /var/log/ntp.log\n" +
                            "logconfig =all\n" +
                            "\n" +
                            "interface listen "+IP+"/"+mask+"\n";

            // Create file object
            File file = new File(fileName);

            // Create file writer object
            FileWriter writer = new FileWriter(file);

            // Write content to file
            writer.write(fileContent);

            // Close writer
            writer.close();

            serverEmulatorGui.state("File created and content written successfully.");
        }
        else {
            String fileName = "/etc/ntp.conf";
            String fileContent =
                    " #/etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help\n" +
                            "\n" +
                            "driftfile /var/lib/ntp/ntp.drift\n" +
                            "\n" +
                            "# Leap seconds definition provided by tzdata\n" +
                            "leapfile /usr/share/zoneinfo/leap-seconds.list\n" +
                            "\n" +
                            "# Enable this if you want statistics to be logged.\n" +
                            "#statsdir /var/log/ntpstats/\n" +
                            "\n" +
                            "statistics loopstats peerstats clockstats\n" +
                            "filegen loopstats file loopstats type day enable\n" +
                            "filegen peerstats file peerstats type day enable\n" +
                            "filegen clockstats file clockstats type day enable\n" +
                            "\n" +
                            "# Specify one or more NTP servers.\n" +
                            "\n" +
                            "# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board\n" +
                            "# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for\n" +
                            "# more information.\n" +
                            "pool 0.ubuntu.pool.ntp.org iburst\n" +
                            "pool 1.ubuntu.pool.ntp.org iburst\n" +
                            "pool 2.ubuntu.pool.ntp.org iburst\n" +
                            "pool 3.ubuntu.pool.ntp.org iburst\n" +
                            "\n" +
                            "# Use Ubuntu's ntp server as a fallback.\n" +
                            "pool ntp.ubuntu.com\n" +
                            "\n" +
                            "# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for\n" +
                            "# details.  The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>\n" +
                            "# might also be helpful.\n" +
                            "#\n" +
                            "# Note that \"restrict\" applies to both servers and clients, so a configuration\n" +
                            "# that might be intended to block requests from certain clients could also end\n" +
                            "# up blocking replies from your own upstream servers.\n" +
                            "\n" +
                            "# By default, exchange time with everybody, but don't allow configuration.\n" +
                            "restrict -4 default kod notrap nomodify nopeer noquery limited\n" +
                            "restrict -6 default kod notrap nomodify nopeer noquery limited\n" +
                            "\n" +
                            "# Local users may interrogate the ntp server more closely.\n" +
                            "restrict 127.0.0.1\n" +
                            "restrict ::1\n" +
                            "\n" +
                            "# Needed for adding pool entries\n" +
                            "restrict source notrap nomodify noquery\n" +
                            "\n" +
                            "# Clients from this (example!) subnet have unlimited access, but only if\n" +
                            "# cryptographically authenticated.\n" +
                            "#restrict 192.168.123.0 mask 255.255.255.0 notrust\n" +
                            "\n" +
                            "\n" +
                            "# If you want to provide time to your local subnet, change the next line.\n" +
                            "# (Again, the address is an example only.)\n" +
                            "#broadcast 192.168.123.255\n" +
                            "\n" +
                            "# If you want to listen to time broadcasts on your local subnet, de-comment the\n" +
                            "# next lines.  Please do this only if you trust everybody on the network!\n" +
                            "#disable auth\n" +
                            "#broadcastclient\n" +
                            "logfile /var/log/ntp.log\n" +
                            "logconfig =all\n" +
                            "\n" +
                            "interface listen "+NTPinterface+"\n";

            // Create file object
            File file = new File(fileName);

            // Create file writer object
            FileWriter writer = new FileWriter(file);

            // Write content to file
            writer.write(fileContent);

            // Close writer
            writer.close();

            serverEmulatorGui.state("File created and content written successfully.");
        }



    }
    void NTPRestoreConfiguration() throws IOException {
        // specify the file path of the text file to delete
        String filePath = "/etc/ntp.conf";

        // create a file object representing the text file
        File file = new File(filePath);

        // check if the file exists and can be deleted
        if (file.exists() && file.canWrite()) {
            // delete the file
            file.delete();
            serverEmulatorGui.state("Configuration file ntp.conf has been deleted.");
        } else {
            serverEmulatorGui.state("Unable to delete configuration file ntp.conf.");
        }

        File oldFile = new File("/etc/ntp.confPuvodni");
        File newFile = new File("/etc/ntp.conf");

        boolean success = oldFile.renameTo(newFile);

        if (success) {
            serverEmulatorGui.state("Configuration file ntp.confPuvodni renamed successfully.");
        } else {
            serverEmulatorGui.state("Configuration file ntp.confPuvodni renaming failed.");
        }



    }
    boolean isServerRunning(String server) {
        String scriptName;
        switch (server) {
            case HTTP:
                scriptName = httpScriptName;
                break;
            case FTP:
                scriptName = ftpScriptName;
                break;
            case DNS:
                scriptName = dnsScriptName;
                break;
            default:
                return false;
        }

        String command = jmeterHomeDir + scriptRelativePath + scriptName + " statusnetns";
        String[] args = new String[]{"/bin/bash", "-c", command};
        try {
            Process proc = new ProcessBuilder(args).start();
            proc.waitFor();
            String line;
            //Checking for any errors and eventually redirecting them to GUI
            BufferedReader errorStream = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            List<String> errorList = new ArrayList<String>();
            line = null;
            while ((line = errorStream.readLine()) != null) {
                errorList.add(line);
            }
            if (errorList.size() > 0) {
                serverEmulatorGui.state(this, "error", "Error while executing command:\n" + command + "\n" + errorList);
            }

            BufferedReader outputStream = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            line = null;
            boolean running = false;

            while ((line = outputStream.readLine()) != null) {
                //serverEmulatorGui.state("DEBUG - " + server + " - line length=" + line.length() + " line: " + line);
                if (line.length() > 0) {
                    running = true;
                }
            }

            if (proc.exitValue() == 0) {
                return running;
            } else {
                serverEmulatorGui.state("Failed to get status of the server. Script " + command + " has exited with value " + proc.exitValue());
                return false;
            }
        } catch (IOException | InterruptedException e) {
            if (e.getClass().equals(InterruptedException.class)) {
                serverEmulatorGui.state(this, "error", "Following bash script for getting server status was interrupted:\n" + command + "\n" + e.getStackTrace().toString());
            } else {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting server status:\n" + command + " \n" + e.getStackTrace().toString());
            }
            return false;
        }
    }

    /**
     * Displays Apache HTTP server status on GUI.
     */
    void getHTTPServerStatus(boolean nsConfigured) {
        serverEmulatorGui.state("Getting status of the HTTP server...");
        if (nsConfigured) {
            String status = isServerRunning(HTTP) ? "running" : "not running";
            serverEmulatorGui.state("HTTP server is " + status);
        } else {
            String command = jmeterHomeDir + scriptRelativePath + "apachectlns.sh status";
            String[] args = new String[]{"/bin/bash", "-c", command};
            try {
                Process proc = new ProcessBuilder(args).start();
                redirectOutput(proc, command);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting HTTP server status: \n" + e.getStackTrace().toString());
            }
        }
    }

    /**
     * Displays vsftpd FTP server status on GUI.
     */
    void getFTPServerStatus(boolean nsConfigured) {
        serverEmulatorGui.state("Getting status of the FTP server...");
        if (nsConfigured) {
            String status = isServerRunning(FTP) ? "running" : "not running";
            serverEmulatorGui.state("FTP server is " + status);
        } else {
            String command = jmeterHomeDir + scriptRelativePath + "vsftpdctlns.sh status";
            String[] args = new String[]{"/bin/bash", "-c", command};
            try {
                Process proc = new ProcessBuilder(args).start();
                redirectOutput(proc, command);
            } catch (IOException e) {
                serverEmulatorGui.state(this, "error", "Error while executing bash script for getting FTP server status: \n" + e.getStackTrace().toString());
            }
        }
    }



    /**
     * Writes an index.html file containing basic HTML structure and blocks of '*' characters to increase it's size.
     * The file will be used by Apache HTTP server controlled by Server Emulator to serve any requesting samplers.
     *
     * @return true if generation of the sample page was successful
     */
    boolean generateHtmlPage(double htmlSize) {
        serverEmulatorGui.state("Reading path where to store the web page from config file " + apacheDocumentRoot);
        String documentRoot = "";
        try {
            for (String line : Files.readAllLines(Paths.get(apacheDocumentRoot), StandardCharsets.UTF_8)) {
                if (line.contains("DocumentRoot") && line.split(" ").length == 2) {
                    documentRoot = (line.split(" ")[1]);
                    documentRoot = documentRoot.replace("\"", "");
                }
            }
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while reading configuration to file " + apacheDocumentRoot + ":\n" + e.getStackTrace().toString());
        }

        Path path = Paths.get(documentRoot);
        if (!Files.isWritable(path)) {
            serverEmulatorGui.state(this, "error", "Cannot write to the DocumentRoot path read from main Apache config file " + documentRoot);
            return false;
        }

        FileWriter htmlWriter = null;
        serverEmulatorGui.state("Generating " + htmlSize / 1024 + " kiB sample HTML web page " + documentRoot + "/index.html...");
        try {
            htmlWriter = new FileWriter(documentRoot + "/index.html");
            htmlWriter.write("<!doctype html>\n");
            htmlWriter.write("<html>\n");
            htmlWriter.write("<body>\n");
            htmlWriter.write("<h1>This is a sample web page generated by Server Emulator plugin for a purpose of load testing in JMeter.</h1>\n");
            htmlWriter.write("<p>The page is a simple HTML file which is except of this text filled with padding of " + "'*' chars to increase it's size to a value set by user. </p>\n");
            htmlWriter.write("</body>\n");
            htmlWriter.write("</html>\n");

            //Writes data of a size (htmlSize - 300 bytes of constant data written above) to the HTML file
            for (int i = 1; i <= (htmlSize / 100) - 3; i++) {
                htmlWriter.write("**************************************************"); //Writes a 50 characters
                htmlWriter.write("**************************************************"); //Writes a 50 characters
            }
            // When size is xx.yy where yy is decimal place this add more precision to size of generated web page
            double htmlSizeRounded = Math.round(htmlSize * Math.pow(10, 3)) / Math.pow(10, 3);
            for (int i = 0; i < (((htmlSizeRounded / 100) - (int) (htmlSizeRounded / 100)) * 100); i += 3) {
                htmlWriter.write("**");
            }
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while genrating sample html page " + documentRoot + "index.html for testing:\n" + e.getStackTrace().toString());
            return false;
        } finally {
            if (htmlWriter != null) {
                try {
                    htmlWriter.close();
                } catch (IOException e) {
                    serverEmulatorGui.state(this, "error", "Error while closing the generated html page: \n" + e.getStackTrace().toString());
                    return false;
                }
            }
        }
        serverEmulatorGui.state("Genration finished.");
        return true;
    }

    /**
     * Method for redirecting standard output and standard error output to GUI
     * with help of method serverEmulatorGui.state(String stateString)
     *
     * @param proc    a process whose output should be redirected
     * @param command the command which was passed to bash
     * @return true if no error occurred
     */
    private void redirectOutput(Process proc, String command) {
        String line;
        //Checking for any errors and eventually redirecting them to GUI
        try {
            BufferedReader errorStream = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            List<String> errorList = new ArrayList<String>();
            line = null;
            while ((line = errorStream.readLine()) != null) {
                errorList.add(line);
            }
            if (errorList.size() > 0) {
                serverEmulatorGui.state(this, "error", "Error while executing command:\n" + command + "\n" + errorList);

            }

            //Redirecting standard output to GUI
            BufferedReader outputStream = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            StringBuilder outputBuilder = new StringBuilder();
            line = null;
            while ((line = outputStream.readLine()) != null) {
                outputBuilder.append(line);
                outputBuilder.append(System.getProperty("line.separator"));
            }
            if (outputBuilder.length() > 0) {
                serverEmulatorGui.state(outputBuilder.toString());
            }
        } catch (IOException e) {
            serverEmulatorGui.state(this, "error", "Error while redirecting standard output of the script to user interface: \n" + e.getStackTrace().toString());
        }
    }

    /**
     * Attaches necessary listeners to JMeter ActionRouter to initialize reading of Server Emulator configuration file
     */
    void addActionRouterListeners() {
        listenersRegistered = true;

        //Reacts on selection event
        GuiPackage.getInstance().getMainFrame().getTree().addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                JMeterTreeNode currentNode = (JMeterTreeNode) e.getPath().getLastPathComponent();
                if (currentNode.getUserObject() instanceof ServerEmulator && !currentNode.equals(previouslySelectedNode) && currentNode.isEnabled()) {
                    loadConfig();
                }
                previouslySelectedNode = currentNode;
            }
        });

        //Reacts on enable event
        JMeterTreeModel jmeterTreeModel = (JMeterTreeModel) GuiPackage.getInstance().getMainFrame().getTree().getModel();
        jmeterTreeModel.addTreeModelListener(new TreeModelListener() {

            @Override
            public void treeNodesChanged(TreeModelEvent e) {

                if (e.getSource() instanceof JMeterTreeModel) {
                    JMeterTreeNode currentNode = (JMeterTreeNode) e.getChildren()[0];
                    if (currentNode.getUserObject() instanceof ServerEmulator) {

                        if (currentNode.isEnabled() && !sePreviouslyEnabled) {
                            loadConfig();
                        }
                        sePreviouslyEnabled = currentNode.isEnabled();
                    }
                }
            }

            @Override
            public void treeNodesInserted(TreeModelEvent e) {
            }

            @Override
            public void treeNodesRemoved(TreeModelEvent e) {
            }

            @Override
            public void treeStructureChanged(TreeModelEvent e) {
            }
        });
    }

    /**
     * Copies a resource directory from inside a JAR file to a target directory.
     *
     * @param source The JAR file
     * @param path   The path to the directory inside the JAR file
     * @param target The target directory
     * @throws IOException If there is an I/O error
     */
    public void extractResourcesDir(final JarFile source, final String path, final File target) throws IOException {
        final Enumeration<JarEntry> entries = source.entries();
        final String newpath = String.format("%s/", path);
        while (entries.hasMoreElements()) {
            final JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith(newpath) && !entry.isDirectory()) {
                final File dest = new File(target, entry.getName().substring(newpath.length()));
                if (dest.getName().contains(".sh")) {
                    dest.setExecutable(true, false);
                }
                final File parent = dest.getParentFile();
                if (parent != null) {
                    parent.mkdirs();
                }
                this.writeToFile(source.getInputStream(entry), dest);
            }
        }
    }

    /**
     * Writes an input stream to a file.
     *
     * @param input  The input stream
     * @param target The target file
     * @throws IOException If there is an I/O error
     */
    private void writeToFile(final InputStream input, final File target) throws IOException {
        final OutputStream output = Files.newOutputStream(target.toPath());
        final byte[] buffer = new byte[8 * 1024];
        int length = input.read(buffer);
        while (length > 0) {
            output.write(buffer, 0, length);
            length = input.read(buffer);
        }
        input.close();
        output.close();
    }



}
