private TRTrackerAnnouncerHelper create(
      TOTorrent torrent, String[] networks, TOTorrentAnnounceURLSet[] sets)
      throws TRTrackerAnnouncerException {
    TRTrackerAnnouncerHelper announcer;

    boolean decentralised;

    if (sets.length == 0) {

      decentralised = TorrentUtils.isDecentralised(torrent.getAnnounceURL());

    } else {

      decentralised = TorrentUtils.isDecentralised(sets[0].getAnnounceURLs()[0]);
    }

    if (decentralised) {

      announcer = new TRTrackerDHTAnnouncerImpl(torrent, networks, is_manual, getHelper());

    } else {

      announcer = new TRTrackerBTAnnouncerImpl(torrent, sets, networks, is_manual, getHelper());
    }

    for (TOTorrentAnnounceURLSet set : sets) {

      URL[] urls = set.getAnnounceURLs();

      for (URL u : urls) {

        String key = u.toExternalForm();

        StatusSummary summary = recent_responses.get(key);

        if (summary == null) {

          summary = new StatusSummary(announcer, u);

          recent_responses.put(key, summary);

        } else {

          summary.setHelper(announcer);
        }
      }
    }

    if (provider != null) {

      announcer.setAnnounceDataProvider(provider);
    }

    if (ip_override != null) {

      announcer.setIPOverride(ip_override);
    }

    return (announcer);
  }
  public URL getTrackerURL() {
    TRTrackerAnnouncerHelper active = getBestActive();

    if (active != null) {

      return (active.getTrackerURL());
    }

    return (null);
  }
  public int getLastUpdateTime() {
    TRTrackerAnnouncerHelper active = getBestActive();

    if (active != null) {

      return (active.getLastUpdateTime());
    }

    return (0);
  }
  public int getTimeUntilNextUpdate() {
    TRTrackerAnnouncerHelper active = getBestActive();

    if (active != null) {

      return (active.getTimeUntilNextUpdate());
    }

    return (Integer.MAX_VALUE);
  }
  public TRTrackerAnnouncerResponse getLastResponse() {
    TRTrackerAnnouncerResponse result = null;

    TRTrackerAnnouncerHelper best = getBestActive();

    if (best != null) {

      result = best.getLastResponse();
    }

    if (result == null) {

      result = last_response_informed;
    }

    return (result);
  }
  protected TRTrackerAnnouncerHelper getBestActiveSupport() {
    List<TRTrackerAnnouncerHelper> x = announcers.getList();

    TRTrackerAnnouncerHelper error_resp = null;

    for (TRTrackerAnnouncerHelper announcer : x) {

      TRTrackerAnnouncerResponse response = announcer.getLastResponse();

      if (response != null) {

        int resp_status = response.getStatus();

        if (resp_status == TRTrackerAnnouncerResponse.ST_ONLINE) {

          return (announcer);

        } else if (error_resp == null
            && resp_status == TRTrackerAnnouncerResponse.ST_REPORTED_ERROR) {

          error_resp = announcer;
        }
      }
    }

    if (error_resp != null) {

      return (error_resp);
    }

    if (x.size() > 0) {

      return (x.get(0));
    }

    return (null);
  }
    protected void updateFrom(TRTrackerAnnouncerResponse response) {
      time = SystemTime.getMonotonousTime();

      int state = response.getStatus();

      if (state == TRTrackerAnnouncerResponse.ST_ONLINE) {

        status = TrackerPeerSource.ST_ONLINE;

        seeds = response.getScrapeCompleteCount();
        leechers = response.getScrapeIncompleteCount();
        completed = response.getScrapeDownloadedCount();
        peers = response.getPeers().length;

      } else {

        status = TrackerPeerSource.ST_ERROR;
      }

      status_str = response.getStatusString();

      interval = (int) helper.getInterval();
      min_interval = (int) helper.getMinInterval();
    }
  protected void checkActivation(boolean force) {
    synchronized (this) {
      int next_check_delay;

      if (destroyed || stopped || announcers.size() <= activated.size()) {

        return;
      }

      if (provider == null) {

        next_check_delay = ACT_CHECK_INIT_DELAY;

      } else {

        boolean activate = force;

        boolean seeding = provider.getRemaining() == 0;

        if (seeding && activated.size() > 0) {

          // when seeding we only activate on tracker fail or major lack of connections
          // as normally we rely on downloaders rotating and finding us

          int connected = provider.getConnectedConnectionCount();

          if (connected < 1) {

            activate = SystemTime.getMonotonousTime() - last_activation_time >= 60 * 1000;

            next_check_delay = ACT_CHECK_SEEDING_SHORT_DELAY;

          } else if (connected < 3) {

            next_check_delay = ACT_CHECK_SEEDING_LONG_DELAY;

          } else {

            next_check_delay = 0;
          }
        } else {

          int allowed = provider.getMaxNewConnectionsAllowed();
          int pending = provider.getPendingConnectionCount();
          int connected = provider.getConnectedConnectionCount();

          int online = 0;

          for (TRTrackerAnnouncerHelper a : activated) {

            TRTrackerAnnouncerResponse response = a.getLastResponse();

            if (response != null && response.getStatus() == TRTrackerAnnouncerResponse.ST_ONLINE) {

              online++;
            }
          }

          /*
          System.out.println(
          	"checkActivation: announcers=" + announcers.size() +
          	", active=" + activated.size() +
          	", online=" + online +
          	", allowed=" + allowed +
          	", pending=" + pending +
          	", connected=" + connected +
          	", seeding=" + seeding );
          */

          if (online == 0) {

            activate = true;

            // no trackers online, start next and recheck soon

            next_check_delay = ACT_CHECK_INIT_DELAY;

          } else {

            int potential = connected + pending;

            if (potential < 10) {

              // minimal connectivity

              activate = true;

              next_check_delay = ACT_CHECK_INIT_DELAY;

            } else if (allowed >= 5 && pending < 3 * allowed / 4) {

              // not enough to fulfill our needs

              activate = true;

              next_check_delay = ACT_CHECK_INTERIM_DELAY;

            } else {
              // things look good, recheck in a bit

              next_check_delay = ACT_CHECK_IDLE_DELAY;
            }
          }
        }

        if (activate) {

          for (TRTrackerAnnouncerHelper a : announcers) {

            if (!activated.contains(a)) {

              if (Logger.isEnabled()) {
                Logger.log(
                    new LogEvent(
                        getTorrent(), LOGID, "Activating " + getString(a.getAnnounceSets())));
              }

              activated.add(a);

              last_activation_time = SystemTime.getMonotonousTime();

              if (complete) {

                a.complete(true);

              } else {

                a.update(false);
              }

              break;
            }
          }
        }
      }

      if (next_check_delay > 0) {

        setupActivationCheck(next_check_delay);
      }
    }
  }
 public void manualUpdate() {
   helper.update(true);
 }
 public boolean canManuallyUpdate() {
   return (((SystemTime.getCurrentTime() / 1000 - helper.getLastUpdateTime()
       >= TRTrackerAnnouncer.REFRESH_MINIMUM_SECS)));
 }
 public int getSecondsToUpdate() {
   return (helper.getTimeUntilNextUpdate());
 }
 public boolean isUpdating() {
   return (helper.isUpdating());
 }
  protected void split() throws TRTrackerAnnouncerException {

    String[] networks = f_provider == null ? null : f_provider.getNetworks();

    TRTrackerAnnouncerHelper to_activate = null;

    synchronized (this) {
      if (stopped || destroyed) {

        return;
      }

      TOTorrent torrent = getTorrent();

      TOTorrentAnnounceURLSet[] sets = torrent.getAnnounceURLGroup().getAnnounceURLSets();

      // sanitise dht entries

      if (sets.length == 0) {

        sets =
            new TOTorrentAnnounceURLSet[] {
              torrent
                  .getAnnounceURLGroup()
                  .createAnnounceURLSet(new URL[] {torrent.getAnnounceURL()})
            };

      } else {

        boolean found_decentralised = false;
        boolean modified = false;

        for (int i = 0; i < sets.length; i++) {

          TOTorrentAnnounceURLSet set = sets[i];

          URL[] urls = set.getAnnounceURLs().clone();

          for (int j = 0; j < urls.length; j++) {

            URL u = urls[j];

            if (u != null && TorrentUtils.isDecentralised(u)) {

              if (found_decentralised) {

                modified = true;

                urls[j] = null;

              } else {

                found_decentralised = true;
              }
            }
          }
        }

        if (modified) {

          List<TOTorrentAnnounceURLSet> s_list = new ArrayList<TOTorrentAnnounceURLSet>();

          for (TOTorrentAnnounceURLSet set : sets) {

            URL[] urls = set.getAnnounceURLs();

            List<URL> u_list = new ArrayList<URL>(urls.length);

            for (URL u : urls) {

              if (u != null) {

                u_list.add(u);
              }
            }

            if (u_list.size() > 0) {

              s_list.add(
                  torrent
                      .getAnnounceURLGroup()
                      .createAnnounceURLSet(u_list.toArray(new URL[u_list.size()])));
            }
          }

          sets = s_list.toArray(new TOTorrentAnnounceURLSet[s_list.size()]);
        }
      }

      List<TOTorrentAnnounceURLSet[]> new_sets = new ArrayList<TOTorrentAnnounceURLSet[]>();

      if (is_manual || sets.length < 2) {

        new_sets.add(sets);

      } else {

        List<TOTorrentAnnounceURLSet> list =
            new ArrayList<TOTorrentAnnounceURLSet>(Arrays.asList(sets));

        // often we have http:/xxxx/ and udp:/xxxx/ as separate groups - keep these together

        while (list.size() > 0) {

          TOTorrentAnnounceURLSet set1 = list.remove(0);

          boolean done = false;

          URL[] urls1 = set1.getAnnounceURLs();

          if (urls1.length == 1) {

            URL url1 = urls1[0];

            String prot1 = url1.getProtocol().toLowerCase();
            String host1 = url1.getHost();

            for (int i = 0; i < list.size(); i++) {

              TOTorrentAnnounceURLSet set2 = list.get(i);

              URL[] urls2 = set2.getAnnounceURLs();

              if (urls2.length == 1) {

                URL url2 = urls2[0];

                String prot2 = url2.getProtocol().toLowerCase();
                String host2 = url2.getHost();

                if (host1.equals(host2)) {

                  if ((prot1.equals("udp") && prot2.startsWith("http"))
                      || (prot2.equals("udp") && prot1.startsWith("http"))) {

                    list.remove(i);

                    new_sets.add(new TOTorrentAnnounceURLSet[] {set1, set2});

                    done = true;
                  }
                }
              }
            }
          }

          if (!done) {

            new_sets.add(new TOTorrentAnnounceURLSet[] {set1});
          }
        }
      }

      // work out the difference

      Iterator<TOTorrentAnnounceURLSet[]> ns_it = new_sets.iterator();

      // need to copy list as we modify it and returned list ain't thread safe

      List<TRTrackerAnnouncerHelper> existing_announcers =
          new ArrayList<TRTrackerAnnouncerHelper>(announcers.getList());

      List<TRTrackerAnnouncerHelper> new_announcers = new ArrayList<TRTrackerAnnouncerHelper>();

      // first look for unchanged sets

      while (ns_it.hasNext()) {

        TOTorrentAnnounceURLSet[] ns = ns_it.next();

        Iterator<TRTrackerAnnouncerHelper> a_it = existing_announcers.iterator();

        while (a_it.hasNext()) {

          TRTrackerAnnouncerHelper a = a_it.next();

          TOTorrentAnnounceURLSet[] os = a.getAnnounceSets();

          if (same(ns, os)) {

            ns_it.remove();
            a_it.remove();

            new_announcers.add(a);

            break;
          }
        }
      }

      // reuse existing announcers

      // first remove dht ones from the equation

      TRTrackerAnnouncerHelper existing_dht_announcer = null;
      TOTorrentAnnounceURLSet[] new_dht_set = null;

      ns_it = new_sets.iterator();

      while (ns_it.hasNext()) {

        TOTorrentAnnounceURLSet[] x = ns_it.next();

        if (TorrentUtils.isDecentralised(x[0].getAnnounceURLs()[0])) {

          new_dht_set = x;

          ns_it.remove();

          break;
        }
      }

      Iterator<TRTrackerAnnouncerHelper> an_it = existing_announcers.iterator();

      while (an_it.hasNext()) {

        TRTrackerAnnouncerHelper a = an_it.next();

        TOTorrentAnnounceURLSet[] x = a.getAnnounceSets();

        if (TorrentUtils.isDecentralised(x[0].getAnnounceURLs()[0])) {

          existing_dht_announcer = a;

          an_it.remove();

          break;
        }
      }

      if (existing_dht_announcer != null && new_dht_set != null) {

        new_announcers.add(existing_dht_announcer);

      } else if (existing_dht_announcer != null) {

        activated.remove(existing_dht_announcer);

        existing_dht_announcer.destroy();

      } else if (new_dht_set != null) {

        TRTrackerAnnouncerHelper a = create(torrent, networks, new_dht_set);

        new_announcers.add(a);
      }

      // now do the non-dht ones

      ns_it = new_sets.iterator();

      while (ns_it.hasNext() && existing_announcers.size() > 0) {

        TRTrackerAnnouncerHelper a = existing_announcers.remove(0);

        TOTorrentAnnounceURLSet[] s = ns_it.next();

        ns_it.remove();

        if (activated.contains(a)
            && torrent.getPrivate()
            && a instanceof TRTrackerBTAnnouncerImpl) {

          URL url = a.getTrackerURL();

          if (url != null) {

            forceStop((TRTrackerBTAnnouncerImpl) a, networks, url);
          }
        }

        a.setAnnounceSets(s, networks);

        new_announcers.add(a);
      }

      // create any new ones required

      ns_it = new_sets.iterator();

      while (ns_it.hasNext()) {

        TOTorrentAnnounceURLSet[] s = ns_it.next();

        TRTrackerAnnouncerHelper a = create(torrent, networks, s);

        new_announcers.add(a);
      }

      // finally fix up the announcer list to represent the new state

      Iterator<TRTrackerAnnouncerHelper> a_it = announcers.iterator();

      while (a_it.hasNext()) {

        TRTrackerAnnouncerHelper a = a_it.next();

        if (!new_announcers.contains(a)) {

          a_it.remove();

          try {
            if (activated.contains(a)
                && torrent.getPrivate()
                && a instanceof TRTrackerBTAnnouncerImpl) {

              URL url = a.getTrackerURL();

              if (url != null) {

                forceStop((TRTrackerBTAnnouncerImpl) a, networks, url);
              }
            }
          } finally {

            if (Logger.isEnabled()) {
              Logger.log(
                  new LogEvent(
                      getTorrent(), LOGID, "Deactivating " + getString(a.getAnnounceSets())));
            }

            activated.remove(a);

            a.destroy();
          }
        }
      }

      a_it = new_announcers.iterator();

      while (a_it.hasNext()) {

        TRTrackerAnnouncerHelper a = a_it.next();

        if (!announcers.contains(a)) {

          announcers.add(a);
        }
      }

      if (!is_manual && announcers.size() > 0) {

        if (activated.size() == 0) {

          TRTrackerAnnouncerHelper a = announcers.get(0);

          if (Logger.isEnabled()) {
            Logger.log(
                new LogEvent(getTorrent(), LOGID, "Activating " + getString(a.getAnnounceSets())));
          }

          activated.add(a);

          last_activation_time = SystemTime.getMonotonousTime();

          if (provider != null) {

            to_activate = a;
          }
        }

        setupActivationCheck(ACT_CHECK_INIT_DELAY);
      }
    }

    if (to_activate != null) {

      if (complete) {

        to_activate.complete(true);

      } else {

        to_activate.update(false);
      }
    }
  }