private void reduceTree(int numColors) {
    for (int level = MAX_LEVEL - 1; level >= 0; level--) {
      Vector v = colorList[level];
      if (v != null && v.size() > 0) {
        for (int j = 0; j < v.size(); j++) {
          OctTreeNode node = (OctTreeNode) v.elementAt(j);
          if (node.children > 0) {
            for (int i = 0; i < 8; i++) {
              OctTreeNode child = node.leaf[i];
              if (child != null) {
                if (!child.isLeaf) LOGGER.debug("not a leaf!");
                node.count += child.count;
                node.totalRed += child.totalRed;
                node.totalGreen += child.totalGreen;
                node.totalBlue += child.totalBlue;
                node.leaf[i] = null;
                node.children--;
                colors--;
                nodes--;
                colorList[level + 1].removeElement(child);
              }
            }
            node.isLeaf = true;
            colors++;
            if (colors <= numColors) return;
          }
        }
      }
    }

    LOGGER.debug("Unable to reduce the OctTree");
  }
  /**
   * Get the color table index for a color.
   *
   * @param rgb the color
   * @return the index
   */
  public int getIndexForColor(int rgb) {
    int red = (rgb >> 16) & 0xff;
    int green = (rgb >> 8) & 0xff;
    int blue = rgb & 0xff;

    OctTreeNode node = root;

    for (int level = 0; level <= MAX_LEVEL; level++) {
      OctTreeNode child;
      int bit = 0x80 >> level;

      int index = 0;
      if ((red & bit) != 0) index += 4;
      if ((green & bit) != 0) index += 2;
      if ((blue & bit) != 0) index += 1;

      child = node.leaf[index];

      if (child == null) return node.index;
      else if (child.isLeaf) return child.index;
      else node = child;
    }
    LOGGER.debug("getIndexForColor failed");
    return 0;
  }
  private void insertColor(int rgb) {
    int red = (rgb >> 16) & 0xff;
    int green = (rgb >> 8) & 0xff;
    int blue = rgb & 0xff;

    OctTreeNode node = root;

    //		LOGGER.debug("insertColor="+Integer.toHexString(rgb));
    for (int level = 0; level <= MAX_LEVEL; level++) {
      OctTreeNode child;
      int bit = 0x80 >> level;

      int index = 0;
      if ((red & bit) != 0) index += 4;
      if ((green & bit) != 0) index += 2;
      if ((blue & bit) != 0) index += 1;

      child = node.leaf[index];

      if (child == null) {
        node.children++;

        child = new OctTreeNode();
        child.parent = node;
        node.leaf[index] = child;
        node.isLeaf = false;
        nodes++;
        colorList[level].addElement(child);

        if (level == MAX_LEVEL) {
          child.isLeaf = true;
          child.count = 1;
          child.totalRed = red;
          child.totalGreen = green;
          child.totalBlue = blue;
          child.level = level;
          colors++;
          return;
        }

        node = child;
      } else if (child.isLeaf) {
        child.count++;
        child.totalRed += red;
        child.totalGreen += green;
        child.totalBlue += blue;
        return;
      } else node = child;
    }
    LOGGER.debug("insertColor failed");
  }
  /** Resolve the IP and obtain a valid transport method. */
  @Override
  public synchronized void resolve(JingleSession session) throws XMPPException {

    setResolveInit();

    clearCandidates();

    final TransportCandidate candidate =
        new TransportCandidate.Fixed(resolvedPublicIP, getFreePort());
    candidate.setLocalIp(resolvedLocalIP);

    LOGGER.debug("RESOLVING : " + resolvedPublicIP + ":" + candidate.getPort());

    addCandidate(candidate);

    setResolveEnd();
  }
  /**
   * Initialize the resolver.
   *
   * @throws XMPPException
   */
  @Override
  public void initialize() throws XMPPException {
    LOGGER.debug("Initialized");
    if (!isResolving() && !isResolved()) {
      // Get the best STUN server available
      if (currentServer.isNull()) {
        loadSTUNServers();
      }
      // We should have a valid STUN server by now...
      if (!currentServer.isNull()) {

        clearCandidates();

        resolverThread =
            new Thread(
                new Runnable() {
                  @Override
                  public void run() {
                    // Iterate through the list of interfaces, and ask
                    // to the STUN server for our address.
                    try {
                      final Enumeration ifaces = NetworkInterface.getNetworkInterfaces();
                      String candAddress;
                      int candPort;

                      while (ifaces.hasMoreElements()) {

                        final NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
                        final Enumeration iaddresses = iface.getInetAddresses();

                        while (iaddresses.hasMoreElements()) {
                          final InetAddress iaddress = (InetAddress) iaddresses.nextElement();
                          if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {

                            // Reset the candidate
                            candAddress = null;
                            candPort = -1;

                            final DiscoveryTest test =
                                new DiscoveryTest(
                                    iaddress, currentServer.getHostname(), currentServer.getPort());
                            try {
                              // Run the tests and get the
                              // discovery
                              // information, where all the
                              // info is stored...
                              final DiscoveryInfo di = test.test();

                              candAddress =
                                  di.getPublicIP() != null
                                      ? di.getPublicIP().getHostAddress()
                                      : null;

                              // Get a valid port
                              if (defaultPort == 0) {
                                candPort = getFreePort();
                              } else {
                                candPort = defaultPort;
                              }

                              // If we have a valid candidate,
                              // add it to the list.
                              if (candAddress != null && candPort >= 0) {
                                final TransportCandidate candidate =
                                    new TransportCandidate.Fixed(candAddress, candPort);
                                candidate.setLocalIp(
                                    iaddress.getHostAddress() != null
                                        ? iaddress.getHostAddress()
                                        : iaddress.getHostName());
                                addCandidate(candidate);

                                resolvedPublicIP = candidate.getIp();
                                resolvedLocalIP = candidate.getLocalIp();
                                return;
                              }
                            } catch (final Exception e) {
                              LOGGER.error(e.getMessage(), e);
                            }
                          }
                        }
                      }
                    } catch (final SocketException e) {
                      LOGGER.error(e.getMessage(), e);
                    } finally {
                      setInitialized();
                    }
                  }
                },
                "Waiting for all the transport candidates checks...");

        resolverThread.setName("STUN resolver");
        resolverThread.start();
      } else {
        throw new IllegalStateException("No valid STUN server found.");
      }
    }
  }