// TODO: needs to be InetAddress[]
  public InetAddress resolvePublishHostAddresses(String publishHosts[]) throws IOException {
    if (publishHosts == null) {
      if (GLOBAL_NETWORK_PUBLISHHOST_SETTING.exists(settings)
          || GLOBAL_NETWORK_HOST_SETTING.exists(settings)) {
        // if we have settings use them (we have a fallback to GLOBAL_NETWORK_HOST_SETTING inline
        publishHosts =
            GLOBAL_NETWORK_PUBLISHHOST_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
      } else {
        // next check any registered custom resolvers
        for (CustomNameResolver customNameResolver : customNameResolvers) {
          InetAddress addresses[] = customNameResolver.resolveDefault();
          if (addresses != null) {
            return addresses[0];
          }
        }
        // we know it's not here. get the defaults
        publishHosts =
            GLOBAL_NETWORK_PUBLISHHOST_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
      }
    }

    InetAddress addresses[] = resolveInetAddresses(publishHosts);
    // TODO: allow publishing multiple addresses
    // for now... the hack begins

    // 1. single wildcard address, probably set by network.host: expand to all interface addresses.
    if (addresses.length == 1 && addresses[0].isAnyLocalAddress()) {
      HashSet<InetAddress> all = new HashSet<>(Arrays.asList(NetworkUtils.getAllAddresses()));
      addresses = all.toArray(new InetAddress[all.size()]);
    }

    // 2. try to deal with some (mis)configuration
    for (InetAddress address : addresses) {
      // check if its multicast: flat out mistake
      if (address.isMulticastAddress()) {
        throw new IllegalArgumentException(
            "publish address: {"
                + NetworkAddress.format(address)
                + "} is invalid: multicast address");
      }
      // check if its a wildcard address: this is only ok if its the only address!
      // (if it was a single wildcard address, it was replaced by step 1 above)
      if (address.isAnyLocalAddress()) {
        throw new IllegalArgumentException(
            "publish address: {"
                + NetworkAddress.format(address)
                + "} is wildcard, but multiple addresses specified: this makes no sense");
      }
    }

    // 3. if we end out with multiple publish addresses, select by preference.
    // don't warn the user, or they will get confused by bind_host vs publish_host etc.
    if (addresses.length > 1) {
      List<InetAddress> sorted = new ArrayList<>(Arrays.asList(addresses));
      NetworkUtils.sortAddresses(sorted);
      addresses = new InetAddress[] {sorted.get(0)};
    }
    return addresses[0];
  }
  /**
   * Resolves {@code bindHosts} to a list of internet addresses. The list will not contain duplicate
   * addresses.
   *
   * @param bindHosts list of hosts to bind to. this may contain special pseudo-hostnames such as
   *     _local_ (see the documentation). if it is null, it will be populated based on global
   *     default settings.
   * @return unique set of internet addresses
   */
  public InetAddress[] resolveBindHostAddresses(String bindHosts[]) throws IOException {
    // first check settings
    if (bindHosts == null) {
      if (GLOBAL_NETWORK_BINDHOST_SETTING.exists(settings)
          || GLOBAL_NETWORK_HOST_SETTING.exists(settings)) {
        // if we have settings use them (we have a fallback to GLOBAL_NETWORK_HOST_SETTING inline
        bindHosts = GLOBAL_NETWORK_BINDHOST_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
      } else {
        // next check any registered custom resolvers
        for (CustomNameResolver customNameResolver : customNameResolvers) {
          InetAddress addresses[] = customNameResolver.resolveDefault();
          if (addresses != null) {
            return addresses;
          }
        }
        // we know it's not here. get the defaults
        bindHosts = GLOBAL_NETWORK_BINDHOST_SETTING.get(settings).toArray(Strings.EMPTY_ARRAY);
      }
    }

    InetAddress addresses[] = resolveInetAddresses(bindHosts);

    // try to deal with some (mis)configuration
    for (InetAddress address : addresses) {
      // check if its multicast: flat out mistake
      if (address.isMulticastAddress()) {
        throw new IllegalArgumentException(
            "bind address: {" + NetworkAddress.format(address) + "} is invalid: multicast address");
      }
      // check if its a wildcard address: this is only ok if its the only address!
      if (address.isAnyLocalAddress() && addresses.length > 1) {
        throw new IllegalArgumentException(
            "bind address: {"
                + NetworkAddress.format(address)
                + "} is wildcard, but multiple addresses specified: this makes no sense");
      }
    }
    return addresses;
  }
  /**
   * Returns an instance of ArtnetController for the given host address and port.
   *
   * @param host host IP for the ArtnetController
   * @param port port for the ArtnetController
   * @return an instance of ArtnetController
   * @throws IOException when the port cannot be opened
   */
  public static ArtnetController getInstance(NetworkAddress host, int port) throws IOException {
    if (host == null) {
      throw new IllegalArgumentException("cannot getInstance of ArtnetController: host is null");
    }
    if (port <= 0) {
      throw new IllegalArgumentException(
          "cannot getInstance of ArtnetController: port has to be greater than 0");
    }

    String identifier = host.getInterfaceAddress().getAddress() + ":" + port;

    if (instances.containsKey(identifier)) {
      return instances.get(identifier);
    } else {
      ArtnetController artnetController = new ArtnetController(host, port);
      instances.put(identifier, artnetController);
      return artnetController;
    }
  }
 public static ArtnetController getTestingInstance() throws IOException {
   if (testingInstance == null)
     testingInstance = new ArtnetController(NetworkAddress.getLoopbackAddress(), 6454);
   return testingInstance;
 }