package be.iminds.ilabt.jfed.experimenter_gui.editor.link_properties;

import be.iminds.ilabt.jfed.experimenter_gui.editor.ModelRspecEditor;
import be.iminds.ilabt.jfed.experimenter_gui.util.InetAddressUtil;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.rspec.model.RspecInterface;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXLinkSetting;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXModelRspec;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecInterface;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.FXRspecLink;
import be.iminds.ilabt.jfed.ui.javafx.FXMLUtil;
import be.iminds.ilabt.jfed.ui.javafx.dialogs.JFDialogs;
import be.iminds.ilabt.jfed.util.GeniUrn;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
import org.controlsfx.dialog.CommandLinksDialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * This dialog is meant to act upon the FXRspecLink.LinkSetting-variables.
 * In practice however, these settings do not work. Because of that, this dialog is not in use.
 * <p/>
 * User: twalcari
 * Date: 11/28/13
 * Time: 5:03 PM
 */
public class LinkPropertiesDialog extends BorderPane {

    private static final Logger LOG = LoggerFactory.getLogger(LinkPropertiesDialog.class);

    private final FXRspecLink link;
    private final FXModelRspec model;
    private final ModelRspecEditor modelRspecEditor;

    private final String ipv4Subnetwork;
    private final String ipv6Subnetwork;

    private final ObservableList<FXLinkSetting> impairmentList;

    @FXML
    private Button saveButton;
    @FXML
    private Button cancelButton;
    @FXML
    private TextField nameTextField;

    @FXML
    private RadioButton autoConfigRadioButton;
    @FXML
    private RadioButton ipv4RadioButton;
    @FXML
    private RadioButton ipv6RadioButton;


    @FXML
    private ToggleGroup ipToggleGroup;
    @FXML
    private TableView<RspecInterfaceWithIpAddress> addressesTableView;

    @FXML
    private TableColumn<RspecInterfaceWithIpAddress, String> idTableColumn;
    @FXML
    private TableColumn<RspecInterfaceWithIpAddress, String> ipTableColumn;
    @FXML
    private TableColumn<RspecInterfaceWithIpAddress, String> netmaskTableColumn;


    @FXML
    private ToggleGroup impairmentToggleGroup;

    @FXML
    private RadioButton simpleLinkImpairmentRadioButton;

    @FXML
    private TextField capacityTextField;
    @FXML
    private TextField latencyTextField;
    @FXML
    private TextField packetLossTextField;

    private final LongProperty capacity = new SimpleLongProperty();
    private final IntegerProperty latency = new SimpleIntegerProperty();
    private final DoubleProperty packetLoss = new SimpleDoubleProperty();

    @FXML
    private CheckBox capacityCheckBox;
    @FXML
    private CheckBox latencyCheckBox;
    @FXML
    private CheckBox packetLossCheckBox;

    @FXML
    private RadioButton advancedLinkImpairmentRadioButton;
    @FXML
    private TableView<FXLinkSetting> advancedImpairmentTableView;

    @FXML
    private TableColumn<FXLinkSetting, String> fromTableColumn;
    @FXML
    private TableColumn<FXLinkSetting, String> toTableColumn;
    @FXML
    private TableColumn<FXLinkSetting, Number> capacityTableColumn;
    @FXML
    private TableColumn<FXLinkSetting, Number> latencyTableColumn;
    @FXML
    private TableColumn<FXLinkSetting, Number> packetLossTableColumn;

    private final ObservableList<RspecInterfaceWithIpAddress> addressesList;

    @FXML
    private Tab linkTypeTab;
    @FXML
    private ComboBox<String> linkTypeComboBox;
    @FXML
    private CheckBox sharedLanCheckBox;
    @FXML
    private TextField sharedLanTextField;
    @FXML
    private CheckBox createSharedLanCheckBox;
    @FXML
    private TextField createSharedLanTextField;
    @FXML
    private Label createSharedLanInfo;

    @FXML
    private CheckBox vlanTaggingCheckBox;
    @FXML
    private CheckBox noMacLearningCheckBox;
    @FXML
    private CheckBox linkMultiplexingCheckBox;
    @FXML
    private CheckBox bestEffortCheckBox;
    @FXML
    private CheckBox disallowInterswitchCheckBox;

    private final TestbedInfoSource testbedInfoSource;


    LinkPropertiesDialog(final ModelRspecEditor modelRspecEditor, FXRspecLink link, TestbedInfoSource testbedInfoSource) {
        this.modelRspecEditor = modelRspecEditor;
        this.testbedInfoSource = testbedInfoSource;
        this.model = modelRspecEditor.getModelRspec();
        this.link = link;

        String inet4subnet = ModelRspecEditor.getInet4SubnetworkFromLink(link);
        if (inet4subnet == null) {
            inet4subnet = modelRspecEditor.getNextAvailableInet4Subnetwork();
        }
        this.ipv4Subnetwork = inet4subnet;

        String inet6subnet = ModelRspecEditor.getInet6SubnetworkFromLink(link);
        if (inet6subnet == null) {
            inet6subnet = InetAddressUtil.generateInet6Subnetwork();
        }
        this.ipv6Subnetwork = inet6subnet;

        impairmentList = link.getLinkSettings();

        //initialize the Scene
        FXMLUtil.injectFXML(this);

        nameTextField.setText(link.getClientId());

        idTableColumn.setCellValueFactory(p -> p.getValue().getRspecInterface().clientIdProperty());

        ipTableColumn.setCellValueFactory(p -> p.getValue().ipAddressProperty());

        netmaskTableColumn.setCellValueFactory(p -> p.getValue().netmaskProperty());


        addressesList = FXCollections.observableArrayList();
        addressesTableView.setItems(addressesList);

        for (FXRspecInterface iface : link.getInterfaces()) {
            addressesList.add(new RspecInterfaceWithIpAddress(iface));

            if (!iface.getIpAddresses().isEmpty()) {
                RspecInterface.IpAddress ipAddress = iface.getIpAddresses().get(0);

                if (RspecInterface.IpAddress.TYPE_IPV4.equalsIgnoreCase(ipAddress.getType())) {
                    ipv4RadioButton.setSelected(true);
                } else if (RspecInterface.IpAddress.TYPE_IPV6.equalsIgnoreCase(ipAddress.getType())) {
                    ipv6RadioButton.setSelected(true);
                }
            }
        }

        ipToggleGroup.selectedToggleProperty().addListener(observable -> {
            for (RspecInterfaceWithIpAddress iface : addressesList) {
                iface.clear();
            }

            if (ipToggleGroup.getSelectedToggle() == ipv4RadioButton) {

                int i = 1;
                for (RspecInterfaceWithIpAddress iface : addressesList) {
                    iface.setIpAddress(ipv4Subnetwork + "." + i);
                    i++;
                    iface.setNetmask("255.255.255.0");
                }
            } else if (ipToggleGroup.getSelectedToggle() == ipv6RadioButton) {
                int i = 1;
                for (RspecInterfaceWithIpAddress iface : addressesList) {
                    iface.setIpAddress(ipv6Subnetwork + "::" + i);
                    i++;
                    iface.setNetmask("64");
                }
            }
        });

        ipTableColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        netmaskTableColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        idTableColumn.prefWidthProperty().bind(addressesTableView.widthProperty().multiply(0.3));

        ipTableColumn.prefWidthProperty().bind(
                Bindings.when(ipv6RadioButton.selectedProperty())
                        .then(addressesTableView.widthProperty().multiply(0.5))
                        .otherwise(addressesTableView.widthProperty().multiply(0.35)));

        netmaskTableColumn.prefWidthProperty().bind(
                addressesTableView.widthProperty().subtract(idTableColumn.widthProperty())
                        .subtract(ipTableColumn.widthProperty())
                        .subtract(2));

        ipTableColumn.editableProperty().bind(autoConfigRadioButton.selectedProperty().not());
        netmaskTableColumn.editableProperty().bind(ipTableColumn.editableProperty());

        //initialize simple values on first FXLinkSetting-object values

        FXLinkSetting ls = link.getLinkSettings().get(0);

        capacity.setValue(ls.getCapacity_Kbps());
        latency.setValue(ls.getLatency_ms());
        packetLoss.setValue(ls.getPacketLoss());

        if (hasAdvancedImpairmentSettings())
            impairmentToggleGroup.selectToggle(advancedLinkImpairmentRadioButton);


        //initialize simple values
        capacityTextField.textProperty().bindBidirectional(capacity, new NumberStringConverter());

        capacityTextField.focusedProperty().addListener(observable -> {
            if (!capacityTextField.isFocused()) {
                if (capacity.get() < 0) {
                    JFDialogs.create()
                            .owner(capacityTextField.getScene().getWindow())
                            .message("Capacity must be a non-negative value")
                            .masthead("Invalid Capacity Loss value")
                            .showError();
                    capacity.setValue(0);
                }

            }
        });
        capacityCheckBox.selectedProperty().bind(Bindings.notEqual(0, capacity));

        capacityCheckBox.selectedProperty().addListener(observable -> LOG.debug("new checkbox value:" + capacityCheckBox.isSelected()));

        latencyTextField.textProperty().bindBidirectional(latency, new NumberStringConverter());

        latencyTextField.focusedProperty().addListener(observable -> {
            if (!latencyTextField.isFocused()) {
                if (latency.get() < 0) {
                    JFDialogs.create()
                            .owner(latencyTextField.getScene().getWindow())
                            .message("Latency must be a non-negative value")
                            .masthead("Invalid Latency value")
                            .showError();
                    latency.setValue(0);
                }

            }
        });

        latencyCheckBox.selectedProperty().bind(Bindings.notEqual(0, latency));

        packetLossTextField.textProperty().bindBidirectional(packetLoss, new NumberStringConverter());

        packetLossTextField.focusedProperty().addListener(observable -> {
            if (!packetLossTextField.isFocused()) {
                if (packetLoss.get() < 0 || packetLoss.get() > 1) {
                    JFDialogs.create()
                            .owner(packetLossTextField)
                            .message("Packet loss must be between 0 and 1")
                            .masthead("Invalid Packet Loss value")
                            .showError();
                    packetLoss.setValue(0);
                }

            }
        });
        packetLossCheckBox.selectedProperty().bind(Bindings.notEqual(0, packetLoss));

        //populate advancedRspecLink.LinkSettingTableView
        fromTableColumn.setCellValueFactory(p -> p.getValue().getFromIface().clientIdProperty());
        toTableColumn.setCellValueFactory(p -> p.getValue().getToIface().clientIdProperty());

        capacityTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

        capacityTableColumn.setOnEditCommit(e -> {
            if (e.getNewValue().intValue() < 0) {

                JFDialogs.create()
                        .owner(capacityTextField)
                        .message("Capacity must be a non-negative value")
                        .masthead("Invalid Capacity value")
                        .showError();

                return;
            }
            e.getRowValue().capacity_KbpsProperty().setValue(e.getNewValue());
        });

        capacityTableColumn.setCellValueFactory(p -> p.getValue().capacity_KbpsProperty());

        latencyTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

        latencyTableColumn.setOnEditCommit(e -> {
            if (e.getNewValue().intValue() < 0) {

                JFDialogs.create()
                        .owner(latencyTextField)
                        .message("Latency must be a non-negative value")
                        .masthead("Invalid Latency value")
                        .showError();
                return;
            }
            e.getRowValue().latency_msProperty().setValue(e.getNewValue());
        });

        latencyTableColumn.setCellValueFactory(p -> p.getValue().latency_msProperty());

        packetLossTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));

        packetLossTableColumn.setOnEditCommit(e -> {
            if (e.getNewValue().doubleValue() < 0 || e.getNewValue().doubleValue() > 1) {
                JFDialogs.create()
                        .owner(advancedImpairmentTableView)
                        .message("Packet loss must be between 0 and 1")
                        .masthead("Invalid Packet Loss value")
                        .showError();
                return;
            }

            e.getRowValue().setPacketLoss(e.getNewValue().doubleValue());
        });

        packetLossTableColumn.setCellValueFactory(p -> p.getValue().packetLossProperty());


        capacityTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
        packetLossTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
        latencyTableColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));


        advancedImpairmentTableView.setItems(impairmentList);

        latencyTextField.disableProperty().bind(Bindings.not(simpleLinkImpairmentRadioButton.selectedProperty()));
        capacityTextField.disableProperty().bind(Bindings.not(simpleLinkImpairmentRadioButton.selectedProperty()));
        packetLossTextField.disableProperty().bind(Bindings.not(simpleLinkImpairmentRadioButton.selectedProperty()));

        advancedImpairmentTableView.disableProperty().bind(Bindings.not(advancedLinkImpairmentRadioButton.selectedProperty()));


        //add link between simple and advanced impairment fields
        capacity.addListener(observable -> {
            for (FXLinkSetting ls13 : LinkPropertiesDialog.this.link.getLinkSettings()) {
                ls13.setCapacity_Kbps(capacity.get());
            }
        });

        packetLoss.addListener(observable -> {
            for (FXLinkSetting ls12 : LinkPropertiesDialog.this.link.getLinkSettings()) {
                ls12.setPacketLoss(packetLoss.get());
            }
        });

        latency.addListener(observable -> {
            for (FXLinkSetting ls1 : LinkPropertiesDialog.this.link.getLinkSettings()) {
                ls1.setLatency_ms(latency.get());
            }
        });


        //add warning when switching from advanced to simple

        impairmentToggleGroup.selectedToggleProperty().addListener((observableValue, oldValue, newValue) -> {
            if (newValue == simpleLinkImpairmentRadioButton && hasAdvancedImpairmentSettings()) {
                Optional<ButtonType> dialogResponse = JFDialogs.create()
                        .owner(advancedImpairmentTableView)
                        .message("Switching to simple impairment-mode will cause your advanced settings to reverse. Are you sure?")
                        .title("Switching to simple impairment mode")
                        .showConfirm();

                if (dialogResponse.isPresent() && dialogResponse.get() == ButtonType.YES) {
                    resetAdvancedImpairmentSettings();
                } else {
                    impairmentToggleGroup.selectToggle(advancedLinkImpairmentRadioButton);
                }

            }

        });


        //linkTypeTab.disableProperty().bind();

        linkTypeComboBox.getItems().addAll("lan", "gre-tunnel", "egre-tunnel", "vlan");
        if (link.getLinkTypes().isEmpty())
            linkTypeComboBox.getSelectionModel().select("lan");
        else
            linkTypeComboBox.getSelectionModel().select(link.getLinkTypes().get(0));

        sharedLanTextField.disableProperty().bind(sharedLanCheckBox.selectedProperty().not());
        if (link.getSharedLan() != null) {
            sharedLanCheckBox.setSelected(true);
            sharedLanTextField.setText(link.getSharedLan());
        } else {
            sharedLanCheckBox.setSelected(false);
            sharedLanTextField.setText("");
        }

        createSharedLanTextField.disableProperty().bind(createSharedLanCheckBox.selectedProperty().not());
        createSharedLanInfo.visibleProperty().bind(createSharedLanCheckBox.selectedProperty());
        createSharedLanInfo.managedProperty().bind(createSharedLanInfo.visibleProperty());
        if (!link.getAutoShareLanNames().isEmpty()) {
            createSharedLanCheckBox.setSelected(true);
            createSharedLanTextField.setText(link.getAutoShareLanNames().get(0));
        } else {
            createSharedLanCheckBox.setSelected(false);
            createSharedLanTextField.setText("");
        }

        vlanTaggingCheckBox.setSelected(link.isVlanTaggingEnabled());
        noMacLearningCheckBox.setSelected(link.isNoMacLearning());
        bestEffortCheckBox.setSelected(link.isBestEffortEnabled());
        disallowInterswitchCheckBox.setSelected(link.isInterswitchDisallowed());
        linkMultiplexingCheckBox.setSelected(link.isLinkMultiplexingEnabled());

        boolean allEmulabLinks = true;
        for (GeniUrn compMan : link.getComponentManagerUrns()) {
            Server auth =
                    this.testbedInfoSource.getByUrn(compMan, TestbedInfoSource.SubAuthMatchAllowed.ALLOW_OTHER_SUBAUTHORITY, TestbedInfoSource.SubAuthMatchPreference.PREFER_EXACT_SUBAUTHORITY);
            if (auth == null) {
                allEmulabLinks = false;
            } else {
                if (auth.getServerType() == null ||
                        !(auth.getServerType().equalsIgnoreCase("emulab") || auth.getServerType().equalsIgnoreCase("instageni")))
                    allEmulabLinks = false;
            }
        }
        linkTypeTab.setDisable(!allEmulabLinks);
    }


    private void resetAdvancedImpairmentSettings() {
        for (FXLinkSetting ls : link.getLinkSettings()) {
            ls.setCapacity_Kbps(capacity.get());
            ls.setLatency_ms(latency.get());
            ls.setPacketLoss(packetLoss.get());
        }

    }

    private boolean hasAdvancedImpairmentSettings() {
        for (FXLinkSetting ls : link.getLinkSettings()) {
            if (ls.getLatency_ms() != latency.get())
                return true;
            if (ls.getCapacity_Kbps() != capacity.get())
                return true;
            if (ls.getPacketLoss() != packetLoss.get())
                return true;
        }
        return false;
    }


    /**
     * Validate a link that would be shown in the Link Properties Dialog
     *
     * @param link incoming link
     * @return true if there are no validation issues.
     */
    public static boolean validateIncomingLink(FXRspecLink link) {
        if (link.getInterfaces().size() == 0) {
            JFDialogs.create()
                    .title("Link configuration contains errors")
                    .message(String.format("Link %s cannot be edited because it's current state is inconsistent. " +
                                    "Please check your configuration and try again.\n\n%s", link.getClientId(),
                            "A link must contain at least one interface!"))
                    .showError();
            return false;

        }

        List<FXRspecInterface> ipv4Interfaces = new ArrayList<>();
        List<FXRspecInterface> ipv6Interfaces = new ArrayList<>();


        for (FXRspecInterface iface : link.getInterfaces()) {
            if (iface.getIpAddresses().size() >= 2) {
                JFDialogs.create()
                        .title("Link configuration contains errors")
                        .message(String.format("Link %s cannot be edited because it's current state is inconsistent. " +
                                        "Please check your configuration and try again.\n\n%s", link.getClientId(),
                                "Only interfaces with at most one IP-address defined are supported"))
                        .showError();
                return false;

            }

            if (!iface.getIpAddresses().isEmpty()) {
                RspecInterface.IpAddress ipAddress = iface.getIpAddresses().get(0);

                if (RspecInterface.IpAddress.TYPE_IPV4.equalsIgnoreCase(ipAddress.getType())) {
                    ipv4Interfaces.add(iface);

                } else if (RspecInterface.IpAddress.TYPE_IPV6.equalsIgnoreCase(ipAddress.getType())) {
                    ipv6Interfaces.add(iface);
                }
            }
        }

        if (!ipv4Interfaces.isEmpty() && !ipv6Interfaces.isEmpty()) {

            CommandLinksDialog.CommandLinksButtonType keepIpv4Action =
                    new CommandLinksDialog.CommandLinksButtonType("Keep IPv4 addresses",
                            String.format("Remove the %d IPv6-address(es) from the link, and keep the %d IPv4-address(es).",
                                    ipv6Interfaces.size(), ipv4Interfaces.size()),
                            false);
            CommandLinksDialog.CommandLinksButtonType keepIpv6Action =
                    new CommandLinksDialog.CommandLinksButtonType("Keep IPv6 addresses",
                            String.format("Remove the %d IPv4-address(es) from the link, and keep the %d IPv6-address(es).",
                                    ipv4Interfaces.size(), ipv6Interfaces.size()),
                            false);
            CommandLinksDialog.CommandLinksButtonType resolveLaterAction =
                    new CommandLinksDialog.CommandLinksButtonType("Resolve manually",
                            "Don't open the preferences window. I will resolve the problem myself.",
                            true);

            CommandLinksDialog dialog = new CommandLinksDialog(keepIpv4Action, keepIpv6Action,resolveLaterAction);
            dialog.setTitle("Link configuration contains errors")      ;
            dialog.setContentText(String.format("Link %s cannot be edited because it's current state is inconsistent. " +
                            "A link can only contain IPv4 OR IPv6 addresses", link.getClientId()));

            Optional<ButtonType> result = dialog.showAndWait();

            if(result.isPresent()){
                if(result.get() == keepIpv4Action.getButtonType()){
                    for (FXRspecInterface iface : ipv6Interfaces) {
                        iface.getIpAddresses().clear();
                    }
                    return true;
                } else if(result.get() == keepIpv6Action.getButtonType()){
                    for (FXRspecInterface iface : ipv4Interfaces) {
                        iface.getIpAddresses().clear();
                    }
                    return true;
                }
            }
            return false;
        }

        return true;
    }


    /**
     * Validates the current state of the configuration.
     *
     * @return null if there are no problems, error message otherwise.
     */
    private String validateOutgoingLink() {
        if (!isValidLinkName(nameTextField.getText())) {

            return "A link name may only contain alphanumerical characters, " +
                    "and is maximum " + getMaximumLinkNameLength() + " characters long.";
        }

        if (ipv4RadioButton.isSelected()) {
            for (RspecInterfaceWithIpAddress iface : addressesList) {
                if (!InetAddressUtil.isValidInet4Address(iface.getIpAddress())) {
                    return String.format("'%s' is not a valid IPv4 address", iface.getIpAddress());
                }

                if (!InetAddressUtil.isValidInet4Address(iface.getNetmask())) {
                    return String.format("'%s' is not a valid IPv4 netmask", iface.getNetmask());
                }
            }
        } else if (ipv6RadioButton.isSelected()) {
            for (RspecInterfaceWithIpAddress iface : addressesList) {
                if (!InetAddressUtil.isValidInet6Address(iface.getIpAddress())) {
                    return String.format("'%s' is not a valid IPv6 address", iface.getIpAddress());
                }

                if (!InetAddressUtil.isValidInet6Netmask(iface.getNetmask())) {
                    return String.format("'%s' is not a valid IPv6 netmask", iface.getNetmask());
                }
            }
        }

        return null;

    }


    @FXML
    public void onSaveAction(ActionEvent actionEvent) {
        String validationError = validateOutgoingLink();

        if (validationError != null) {
            JFDialogs.create()
                    .owner(this)
                    .message(validationError)
                    .title("Current link configuration is invalid")
                    .showError();
            return;
        }


        link.setClientId(nameTextField.getText());

        for (RspecInterfaceWithIpAddress ifaceWithIp : addressesList) {
            FXRspecInterface iface = link.getInterfaceForNode(ifaceWithIp.getRspecInterface().getNode());

            iface.getIpAddresses().clear();

            if (!autoConfigRadioButton.isSelected())
                iface.getIpAddresses().add(new RspecInterface.IpAddress(ifaceWithIp.getIpAddress(),
                        ifaceWithIp.getNetmask(),
                        ipv4RadioButton.isSelected() ? "ipv4" : "ipv6"));


        }

        String linkType = linkTypeComboBox.getSelectionModel().getSelectedItem();
        if (linkType != null) {
            link.getLinkTypes().setAll(linkType);
        } else {
            link.getLinkTypes().setAll("lan");
        }

        if (sharedLanCheckBox.isSelected() && !sharedLanTextField.getText().trim().isEmpty()) {
            link.setSharedLan(sharedLanTextField.getText().trim());
        } else {
            link.setSharedLan(null);
        }

        if (createSharedLanCheckBox.isSelected() && !createSharedLanTextField.getText().trim().isEmpty()) {
            link.getAutoShareLanNames().setAll(createSharedLanTextField.getText().trim());
        } else {
            link.getAutoShareLanNames().clear();
        }

        link.setVlanTagging(vlanTaggingCheckBox.isSelected() ? true : null /* remove entire tag */);
        link.setNoMacLearning(noMacLearningCheckBox.isSelected() ? true : null /*remove entire tag */);
        link.setBestEffort(bestEffortCheckBox.isSelected() ? true : null /*remove entire tag*/);
        link.setLinkMultiplexing(linkMultiplexingCheckBox.isSelected() ? true : null  /*remove entire tag*/);
        link.setInterswitchDisallow(disallowInterswitchCheckBox.isSelected() ? true : null  /*remove entire tag*/);

        Stage stage = (Stage) saveButton.getScene().getWindow();
        stage.close();
    }

    private int getMaximumLinkNameLength() {
        return 25;
    }

    private boolean isValidLinkName(String text) {
        if (text.length() > getMaximumLinkNameLength())
            return false;

        for (char c : text.toCharArray()) {
            boolean validChar = (c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z')
                    || (c >= '0' && c <= '9');

            if (!validChar) {
                return false;
            }
        }

        return true;
    }

    @FXML
    public void onCancelAction(ActionEvent actionEvent) {
        Stage stage = (Stage) cancelButton.getScene().getWindow();
        stage.close();
    }

    private static final class RspecInterfaceWithIpAddress {
        private final FXRspecInterface rspecInterface;

        private final StringProperty ipAddress = new SimpleStringProperty();
        private final StringProperty netmask = new SimpleStringProperty();


        public RspecInterfaceWithIpAddress(FXRspecInterface rspecInterface) {
            this.rspecInterface = rspecInterface;
            if (!rspecInterface.getIpAddresses().isEmpty()) {

                RspecInterface.IpAddress ip = rspecInterface.getIpAddresses().get(0);
                ipAddress.setValue(ip.getAddress());
                netmask.setValue(ip.getNetmask());
            }
        }


        public FXRspecInterface getRspecInterface() {
            return rspecInterface;
        }

        public String getIpAddress() {
            return ipAddress.get();
        }

        public StringProperty ipAddressProperty() {
            return ipAddress;
        }

        public String getNetmask() {
            return netmask.get();
        }

        public StringProperty netmaskProperty() {
            return netmask;
        }

        public void setNetmask(String netmask) {
            this.netmask.set(netmask);
        }

        public void setIpAddress(String ipAddress) {
            this.ipAddress.set(ipAddress);
        }

        public void clear() {
            ipAddress.setValue("");
            netmask.setValue("");
        }
    }
}

