package be.iminds.ilabt.jfed.rspec.model.javafx_impl;

import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.lowlevel.TestbedInfoSource;
import be.iminds.ilabt.jfed.rspec.model.*;
import be.iminds.ilabt.jfed.rspec.parser.ExtraXml;
import be.iminds.ilabt.jfed.util.GeniUrn;
import be.iminds.ilabt.jfed.util.TextUtil;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

public class FXRspecLink implements ListChangeListener<FXRspecInterface>, RspecLink {

    private static final Logger LOG = LoggerFactory.getLogger(FXRspecLink.class);
    @GuiEditable(nullable = false, clazz = String.class, guiName = "ID",
            guiHelp = "Unique name for this link")
    protected final List<ExtraXml> extraXml = new ArrayList<>();
    private final StringProperty clientId = new SimpleStringProperty();
    private final StringProperty sharedLanName = new SimpleStringProperty();

    private final BooleanProperty vlanTaggingTagPresent = new SimpleBooleanProperty(false);
    private final BooleanProperty vlanTaggingEnabled = new SimpleBooleanProperty(false);
    private final ObjectProperty<Integer> vlanTag = new SimpleObjectProperty<>();

    private final BooleanProperty noMacLearningTagPresent = new SimpleBooleanProperty(false);
    private final BooleanProperty noMacLearningEnabled = new SimpleBooleanProperty(false);
    private final StringProperty noMacLearningValueString = new SimpleStringProperty(null);

    private final ObjectProperty<Boolean> linkMultiplexing = new SimpleObjectProperty<>(null);
    private final ObjectProperty<Boolean> bestEffort = new SimpleObjectProperty<>(null);
    private final ObjectProperty<Boolean> interswitchDisallow = new SimpleObjectProperty<>(null);

    private final ObservableList<String> autoSharedLanNames = FXCollections.observableArrayList();

    /**
     * Will only be set in ManifestRspec
     */
    private final ObjectProperty<GeniUrn> sliverId = new SimpleObjectProperty<>();
    private final FXModelRspec modelRspec;
    @GuiEditable(nullable = false, clazz = ListProperty.class, listClass = String.class,
            guiName = "Component Manager URN(s)", guiHelp = "URN(s) of the component manager(s) for this link")
    private final ObservableList<GeniUrn> componentManagerUrns = FXCollections.observableArrayList();
    @GuiEditable(nullable = false, clazz = ListProperty.class, listClass = String.class,
            guiName = "Link Type(s)", guiHelp = "Link type name(s)")
    private final ObservableList<String> linkTypes = FXCollections.observableArrayList();

    private final ObservableList<FXRspecInterface> interfaces = FXCollections.observableArrayList();
    private final IntegerProperty activeLinkSettingsCount = new SimpleIntegerProperty();
    private final ReadOnlyBooleanWrapper impaired = new ReadOnlyBooleanWrapper();
    private final ChangeListener<Boolean> linkSettingsListener = (observableValue, oldValue, newValue) -> {
        if (newValue == oldValue)
            return;

        if (newValue) {
            activeLinkSettingsCount.set(activeLinkSettingsCount.get() + 1);
        } else {
            activeLinkSettingsCount.set(activeLinkSettingsCount.get() + 1);
        }
    };
    @GuiEditable(nullable = false, clazz = ListProperty.class,
            listClass = FXLinkSetting.class, guiName = "Link Setting",
            guiHelp = "Bandwidth, latency and loss settings for each one-directional link")
    private final ObservableList<FXLinkSetting> linkSettings = FXCollections.observableArrayList();

    //instance constructor
    {
        linkSettings.addListener((Change<? extends FXLinkSetting> change) -> {
            while (change.next()) {
                if (change.wasAdded()) {
                    for (FXLinkSetting ls : change.getAddedSubList()) {
                        if (ls.isActive())
                            activeLinkSettingsCount.set(activeLinkSettingsCount.get() + 1);
                        ls.activeProperty().addListener(linkSettingsListener);
                    }
                } else if (change.wasRemoved()) {
                    for (FXLinkSetting ls : change.getRemoved()) {
                        ls.activeProperty().removeListener(linkSettingsListener);
                        if (ls.isActive())
                            activeLinkSettingsCount.set(activeLinkSettingsCount.get() - 1);
                    }
                }
            }
        });

        impaired.bind(Bindings.greaterThan(activeLinkSettingsCount, 0));
    }

    /**
     * Create a link with a specific id. Optionally add a default link type (useful when creating RSpec).
     *
     * @param modelRspec         -
     * @param clientId           -
     * @param addDefaultLinkType a default link type to add. "lan" is a a typical value. May be null (= add none).
     */
    FXRspecLink(FXModelRspec modelRspec, String clientId, String addDefaultLinkType) {
        this.modelRspec = modelRspec;
        this.clientId.setValue(clientId);
        interfaces.addListener(this);
        if (addDefaultLinkType != null)
            linkTypes.add(addDefaultLinkType);
    }

    public FXRspecLink(FXModelRspec newModelRspec, RspecLink other, RspecNode.InterfaceCopyMethod interfaceCopyMethod) {
        this(newModelRspec, other.getClientId(), null);

        this.extraXml.addAll(other.getExtraXml());
        setSliverId(other.getSliverId());
        this.componentManagerUrns.addAll(other.getComponentManagerUrns());
        this.linkTypes.addAll(other.getLinkTypes());

        this.vlanTaggingEnabled.setValue(other.isVlanTaggingEnabled());
        this.vlanTaggingTagPresent.setValue(other.hasVlanTaggingTag());
        this.vlanTag.setValue(other.getVlanTag());

        this.noMacLearningEnabled.setValue(other.isNoMacLearning());
        this.noMacLearningTagPresent.setValue(other.hasNoMacLearningTag());
        this.noMacLearningValueString.setValue(other.getNoMacLeaningValueString());

        this.bestEffort.setValue(other.hasBestEffortTag()? other.isBestEffortEnabled() : null);
        this.linkMultiplexing.setValue(other.hasLinkMultiplexingTag() ? other.isLinkMultiplexingEnabled() : null);
        this.interswitchDisallow.setValue(other.hasInterswitchDisallowTag() ? other.isInterswitchDisallowed() : null);

        this.sharedLanName.setValue(other.getSharedLan());
        this.autoSharedLanNames.setAll(other.getAutoShareLanNames());

        //first copy interfaces
        if (Objects.equals(interfaceCopyMethod, RspecNode.InterfaceCopyMethod.CREATE_NEW_INTERFACE)) {
            copyInterfaces(other);
        }
        if (Objects.equals(interfaceCopyMethod, RspecNode.InterfaceCopyMethod.STEAL_INTERFACE)) {
            stealInterfaces(other);
        }

        if (interfaceCopyMethod.equals(RspecNode.InterfaceCopyMethod.CREATE_NEW_INTERFACE) || interfaceCopyMethod.equals(RspecNode.InterfaceCopyMethod.STEAL_INTERFACE)) {
            initializeLinkSettings();
            //then copy linksettings
            for (LinkSetting otherLinkSetting : other.getLinkSettings()) {
                FXLinkSetting linkSetting =
                        getLinkSettingByClientIds(otherLinkSetting.getFromIface().getClientId(), otherLinkSetting.getToIface().getClientId());
                assert linkSetting != null :
                        "no linksetting for from=" + otherLinkSetting.getFromIface().getClientId() + " to=" + otherLinkSetting.getToIface().getClientId();
                linkSetting.setSameAs(otherLinkSetting);
            }
        }
    }

    public void copyInterfaces(RspecLink rspecLink) {
        for (RspecInterface rspecInterface : rspecLink.getInterfaces()) {
            //clone interface
            FXRspecInterface res;
            if (rspecLink.getRspec() == getRspec())
                res =
                        new FXRspecInterface((FXRspecNode) rspecInterface.getNode(), this, FXRspecNode.generateNewInterfaceId(rspecInterface));
            else
                res =
                        new FXRspecInterface(this, rspecInterface.getClientId()); //since we copy to new model, just copy interface name
            res.overwritePropertiesWith(rspecInterface, true);
        }
    }

    public void stealInterfaces(RspecLink rspecLink) {
        List<RspecInterface> origIfaces = new ArrayList<>(rspecLink.getInterfaces());
        for (RspecInterface rspecInterface : origIfaces) {
            rspecInterface.moveToLink(this);
        }
    }

    @Override
    public void overwritePropertiesWith(RspecLink other, boolean overwriteWithNull) {
        assert Objects.equals(this.getClientId(), other.getClientId()) :
                "Expected other link to have clientId " + this.getClientId() + ", but got " + other.getClientId();

        if (overwriteWithNull) {
            this.sliverId.set(other.getSliverId());
            this.componentManagerUrns.setAll(other.getComponentManagerUrns());
            this.linkTypes.setAll(other.getLinkTypes());
            for (LinkSetting ls : other.getLinkSettings())
                this.getLinkSettingByClientIds(ls.getFromIface().getClientId(), ls.getToIface().getClientId()).setSameAs(ls);

            this.extraXml.clear();
            this.extraXml.addAll(other.getExtraXml());

            //not needed, overwritePropertiesWith is only used for manifest merge:
//            this.vlanTaggingEnabled.setValue(other.isVlanTaggingEnabled());
//            this.vlanTaggingTagPresent.setValue(other.hasVlanTaggingTag());
//            this.vlanTag.setValue(other.getVlanTag());
//
//            this.noMacLearningEnabled.setValue(other.isNoMacLearning());
//            this.noMacLearningTagPresent.setValue(other.hasNoMacLearningTag());
//            this.noMacLearningValueString.setValue(other.getNoMacLeaningValueString());
//
//            this.bestEffortEnabled.setValue(other.isBestEffortEnabled());
//            this.bestEffortTagPresent.setValue(other.hasBestEffortTag());
//
//            this.linkMultiplexingEnabled.setValue(other.isLinkMultiplexingEnabled());
//            this.linkMultiplexingTagPresent.setValue(other.hasLinkMultiplexingTag());
//
//            this.sharedLanName.setValue(other.getSharedLan());
//            this.autoSharedLanNames.setAll(other.getAutoShareLanNames());
        } else {
            if (other.getSliverId() != null)
                this.sliverId.set(other.getSliverId());
            for (LinkSetting ls : other.getLinkSettings()) {
                if (ls.isActive()) {
                    FXLinkSetting ols =
                            this.getLinkSettingByClientIds(ls.getFromIface().getClientId(), ls.getToIface().getClientId());
                    if (ols != null) {
                        ols.setSameAs(ls);
                    } else {
                        FXRspecInterface fromIface = this.getInterfaceByClientId(ls.getFromIface().getUniqueId());
                        FXRspecInterface toIface = this.getInterfaceByUniqueId(ls.getToIface().getUniqueId());
                        if (fromIface != null && toIface != null) {
                            FXLinkSetting newLs = getLinkSetting(fromIface, toIface);
                            assert newLs != null :
                                    "link settings not found for " + ls.getFromIface().getClientId() + " " + ls.getToIface().getClientId();
                            newLs.setSameAs(ls);
                        }
                    }
                }
            }
            addMissing(this.componentManagerUrns, other.getComponentManagerUrns());
            addMissing(this.linkTypes, other.getLinkTypes());
            addMissing(this.extraXml, other.getExtraXml()); //works due to custom comparison in EventListExtraXml

            //not needed, overwritePropertiesWith is only used for manifest merge:
//            if (other.hasVlanTaggingTag()) {
//                this.vlanTaggingEnabled.setValue(other.isVlanTaggingEnabled());
//                this.vlanTaggingTagPresent.setValue(other.hasVlanTaggingTag());
//            }
//
//            if (other.getVlanTag() != null)
//                this.vlanTag.setValue(other.getVlanTag());
//
//            if (other.hasNoMacLearningTag()) {
//                this.noMacLearningEnabled.setValue(other.isNoMacLearning());
//                this.noMacLearningTagPresent.setValue(other.hasNoMacLearningTag());
//                this.noMacLearningValueString.setValue(other.getNoMacLeaningValueString());
//            }
//
//            if (other.hasBestEffortTag()) {
//                this.bestEffortEnabled.setValue(other.isBestEffortEnabled());
//                this.bestEffortTagPresent.setValue(other.hasBestEffortTag());
//            }
//
//            if (other.hasLinkMultiplexingTag()) {
//                this.linkMultiplexingEnabled.setValue(other.isLinkMultiplexingEnabled());
//                this.linkMultiplexingTagPresent.setValue(other.hasLinkMultiplexingTag());
//            }
//
//            this.sharedLanName.setValue(other.getSharedLan());
//            addMissing(this.autoSharedLanNames, other.getAutoShareLanNames());
        }
    }

    private static <T> void addMissing(List<T> target, List<T> other) {
        for (T o : other) {
            if (!target.contains(o))
                target.add(o);
        }
    }

    @Override
    public void onChanged(Change<? extends FXRspecInterface> change) {
        //reflect these changes in the linksettings

        while (change.next()) {
            if (change.wasRemoved()) {
                //remove unneeded link settings
                linkSettings.removeIf(
                        ls -> change.getRemoved().contains(ls.getFromIface())
                                || change.getRemoved().contains(ls.getToIface())  //from- or to-iface doesn't exist anymore
                );
            }

            if (change.wasAdded()) {
                for (FXRspecInterface iface : interfaces) {
                    //we have to handle the combinations to newly added interfaces
                    // separately to prevent double processing!
                    if (change.getAddedSubList().contains(iface))
                        continue;

                    for (FXRspecInterface newIface : change.getAddedSubList()) {
                        linkSettings.addAll(new FXLinkSetting(iface, newIface), new FXLinkSetting(newIface, iface));
                    }
                }

                for (FXRspecInterface iface : change.getAddedSubList()) {
                    for (FXRspecInterface iface2 : change.getAddedSubList()) {
                        if (iface == iface2)
                            continue;
                        linkSettings.addAll(new FXLinkSetting(iface, iface2));
                    }
                }
            }
        }


        Collections.sort(linkSettings);
    }

    @Override
    public FXModelRspec getRspec() {
        return modelRspec;
    }

    @Override
    public String getUniqueId() {
        return BasicStringRspec.makeUniqueLinkId(clientId.get(), null, getComponentManagerUrnStrings());
    }

    public String getPreStitchingUniqueId() {
        Set<String> componentManagerIds = new HashSet<>();
        for (RspecInterface iface : interfaces) {
            componentManagerIds.add(iface.getNode().getComponentManagerId().toString());
        }

        return BasicStringRspec.makeUniqueLinkId(clientId.get(), null, componentManagerIds);
    }

    public List<String> getComponentManagerUrnStrings() {
        return componentManagerUrns.stream()
                .map(GeniUrn::getValue)
                .collect(Collectors.toList());
    }

    @Override
    public String getClientId() {
        return clientId.get();
    }

    @Override
    public void setClientId(String value) {
        clientId.set(value);
    }

    public StringProperty clientIdProperty() {
        return clientId;
    }

    @Override
    public ObservableList<GeniUrn> getComponentManagerUrns() {
        return componentManagerUrns;
    }

    @Override
    public ObservableList<String> getLinkTypes() {
        return linkTypes;
    }

    @Override
    public ObservableList<FXLinkSetting> getLinkSettings() {
        return linkSettings;
    }

    @Override
    public FXLinkSetting getLinkSetting(RspecInterface fromIface, RspecInterface toIface) {
        //this one only makes sense within the same modelRspec
        assert fromIface.getRspec() == getRspec();
        assert toIface.getRspec() == getRspec();

        assert fromIface instanceof FXRspecInterface && interfaces.contains(fromIface);
        assert toIface instanceof FXRspecInterface && interfaces.contains(toIface);
        for (FXLinkSetting ls : linkSettings) {
            if (ls.getFromIface() == fromIface && ls.getToIface() == toIface)
                return ls;
        }
        return null;
    }

    @Override
    public FXLinkSetting getLinkSettingByUniqueIds(String fromIfaceName, String toIfaceName) {
        for (FXLinkSetting ls : linkSettings) {
            if (Objects.equals(ls.getFromIface().getUniqueId(), fromIfaceName) &&
                    Objects.equals(ls.getToIface().getUniqueId(), toIfaceName))
                return ls;
        }
        return null;
    }

    @Override
    public FXLinkSetting getLinkSettingByClientIds(String fromIfaceName, String toIfaceName) {
        for (FXLinkSetting ls : linkSettings) {
            if (Objects.equals(ls.getFromIface().getClientId(), fromIfaceName) &&
                    Objects.equals(ls.getToIface().getClientId(), toIfaceName))
                return ls;
        }
        return null;
    }


    private void initializeLinkSettings() {
        for (FXRspecInterface fromIface : interfaces) {
            for (FXRspecInterface toIface : interfaces) {
                if (fromIface == toIface)
                    continue;
                linkSettings.add(new FXLinkSetting(fromIface, toIface));
            }
        }
    }

    @Override
    public boolean isImpaired() {
        return impaired.get();
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    public ReadOnlyBooleanProperty impairedProperty() {
        return impaired.getReadOnlyProperty();
    }

    @Override
    public ObservableList<FXRspecInterface> getInterfaces() {
        return /*FXCollections.unmodifiableObservableList(*/interfaces/*)*/;
    }

    protected boolean addInterface(RspecInterface iface) {
        assert iface instanceof FXRspecInterface;
        return interfaces.add((FXRspecInterface) iface);
    }

    protected boolean removeInterface(RspecInterface iface) {
        assert iface instanceof FXRspecInterface;
        assert interfaces.contains(iface);
        LOG.trace("Removing {} from {}", iface.getUniqueId(), iface.getUniqueId());

        return interfaces.remove(iface);
    }

    @Override
    public FXRspecInterface getInterfaceForNode(RspecNode node) {
        for (FXRspecInterface iface : interfaces) {
            if (iface.getNode() == node)
                return iface;
        }
        return null;
    }

    @Override
    public FXRspecInterface getInterfaceByClientId(String clientId) {
        for (FXRspecInterface iface : interfaces) {
            if (Objects.equals(iface.getClientId(), clientId))
                return iface;
        }
        return null;
    }

    @Override
    public FXRspecInterface getInterfaceByUniqueId(String uniqueId) {
        for (FXRspecInterface iface : interfaces) {
            if (Objects.equals(iface.getUniqueId(), uniqueId))
                return iface;
        }
        return null;
    }

    @Override
    public GeniUrn getSliverId() {
        return sliverId.get();
    }

    @Override
    public void setSliverId(String sliverId) {
        if (sliverId == null) {
            this.sliverId.set(null);
            return;
        }

        GeniUrn geniUrn = GeniUrn.parse(sliverId);

        if (geniUrn != null) {
            this.sliverId.set(geniUrn);
        } else {
            LOG.warn("Could not set sliverId to {} as it is not a valid URN!", sliverId);
            this.sliverId.set(null);
        }
    }

    public ObjectProperty<GeniUrn> sliverIdProperty() {
        return sliverId;
    }

    @Override
    public void setSliverId(GeniUrn sliverId) {
        this.sliverId.set(sliverId);
    }

    @Override
    public String toString() {
        return "RspecLink{" +
                "id=" + clientId +
                ", rspec=" + modelRspec +
                ", componentManagerUrns=" + componentManagerUrns +
                ", linkSettings=" + linkSettings +
                ", interfaces=" + interfaces +
                '}';
    }


    public List<ExtraXml> getExtraXml() {
        return extraXml;
    }

    @Override
    public boolean isStitched(TestbedInfoSource testbedInfoSource) {
        if (getComponentManagerUrns().size() < 2) return false;

        //only links with type "lan" or links without type can be stitching
        if (getLinkTypes().size() > 1 ||
                (getLinkTypes().size() == 1 && !(getLinkTypes().get(0).equalsIgnoreCase("lan") || getLinkTypes().get(0).equalsIgnoreCase("vlan")))
                )
            return false;

        if (testbedInfoSource == null) return true;

        Server firstServer = null;
        for (GeniUrn urn : getComponentManagerUrns()) {
            //TODO this is actually more complex, depends on exogeni setting setc
            Server server =
                    testbedInfoSource.getByUrn(urn, TestbedInfoSource.SubAuthMatchAllowed.ALLOW_TOPLEVEL, TestbedInfoSource.SubAuthMatchPreference.PREFER_TOPLEVEL);
            if (server == null) {
                LOG.warn("Authority not found for " + urn + " during stitch check -> falling back to assuming link is stitched");
                return true; //fall back to assuming it is stitched, if any authority is not found
            }
            if (firstServer == null)
                firstServer = server;
            else {
                if (!Objects.equals(firstServer.getDefaultComponentManagerUrn(), server.getDefaultComponentManagerUrn()))
                    return true; //authorities have different URN, so stitched link
            }
        }
        return false;
    }

    void updateComponentManagerUrns() {

        componentManagerUrns.clear();

        for (RspecInterface iface : interfaces) {
            if (!componentManagerUrns.contains(iface.getNode().getComponentManagerId())) {
                componentManagerUrns.add(iface.getNode().getComponentManagerId());
            }
        }

    }


    @Override
    public String getSharedLan() {
        return sharedLanName.get();
    }

    @Override
    public void setSharedLan(String sharedLanName) {
        this.sharedLanName.setValue(sharedLanName);
    }

    public StringProperty sharedLanProperty() {
        return sharedLanName;
    }

    @Override
    public boolean isVlanTaggingEnabled() {
        return vlanTaggingTagPresent.getValue() ? vlanTaggingEnabled.getValue() : false;
    }

    @Override
    public boolean hasVlanTaggingTag() {
        return vlanTaggingTagPresent.getValue();
    }

    /**
     * set to null to remove tag completely
     */
    @Override
    public void setVlanTagging(@Nullable Boolean value) {
        if (value == null) {
            vlanTaggingTagPresent.setValue(false);
            vlanTaggingEnabled.setValue(false);
        } else {
            vlanTaggingTagPresent.setValue(true);
            vlanTaggingEnabled.setValue(value);
        }
    }

    @Override
    public Integer getVlanTag() {
        return vlanTag.get();
    }

    public ObjectProperty<Integer> vlanTagProperty() {
        return vlanTag;
    }

    @Override
    public void setVlanTag(Integer vlanTag) {
        this.vlanTag.set(vlanTag);
    }

    @Override
    public boolean isNoMacLearning() {
        return noMacLearningTagPresent.getValue() ? noMacLearningEnabled.getValue() : false;
    }

    @Override
    public boolean hasNoMacLearningTag() {
        return noMacLearningTagPresent.getValue();
    }

    @Override
    public void setNoMacLearning(Boolean value) {
        if (value == null) {
            noMacLearningTagPresent.setValue(false);
            noMacLearningEnabled.setValue(false);
            noMacLearningValueString.set(null);
        } else {
            noMacLearningTagPresent.setValue(true);
            noMacLearningEnabled.setValue(value);
            noMacLearningValueString.set(value + "");
        }

    }

    @Override
    public String getNoMacLeaningValueString() {
        if (!noMacLearningTagPresent.getValue()) return null;
        return noMacLearningValueString.getValue();
    }

    @Override
    public void setNoMacLearning(String value) {
        Boolean enabled = TextUtil.stringToBoolean(value);
        if (enabled == null) {
            noMacLearningTagPresent.setValue(false);
            noMacLearningEnabled.setValue(false);
            noMacLearningValueString.set(null);
        } else {
            noMacLearningTagPresent.setValue(true);
            noMacLearningEnabled.setValue(enabled);
            noMacLearningValueString.set(value);
        }
    }

    @Override
    public boolean isBestEffortEnabled() {
        return bestEffort.get() != null ? bestEffort.get() : false;
    }

    @Override
    public boolean hasBestEffortTag() {
        return bestEffort.get() != null;
    }

    /**
     * set to null to remove tag completely
     */
    @Override
    public void setBestEffort(@Nullable Boolean value) {
        this.bestEffort.set(value);
    }

    @Override
    public boolean isLinkMultiplexingEnabled() {
        return linkMultiplexing.get() != null ? linkMultiplexing.get() : false;
    }

    @Override
    public boolean hasLinkMultiplexingTag() {
        return linkMultiplexing.get() != null;
    }

    /**
     * set to null to remove tag completely
     */
    @Override
    public void setLinkMultiplexing(@Nullable Boolean value) {
        linkMultiplexing.set(value);
    }

    @Override
    public boolean hasAutoShareLan() {
        return !autoSharedLanNames.isEmpty();
    }

    @Override
    public ObservableList<String> getAutoShareLanNames() {
        return autoSharedLanNames;
    }

    @Override
    public boolean isInterswitchDisallowed() {
        return interswitchDisallow.get() == Boolean.TRUE;
    }

    @Override
    public boolean hasInterswitchDisallowTag() {
        return interswitchDisallow.get() != null;
    }

    @Override
    public void setInterswitchDisallow(@Nullable Boolean value) {
        interswitchDisallow.setValue(value);
    }
}
