/**
   * Choose which of the connected peers should be unchoked and authorized to upload from this
   * client. A peer gets unchoked if it is not interested, or if it is interested and has one of the
   * 5 highest download rate among the interested peers. \r\n Every 3 times this method is called,
   * calls the optimisticUnchoke method, which unchoke a peer no matter its download rate, in a try
   * to find a better source
   */
  private synchronized void unchokePeers() {
    synchronized (this.task) {
      int nbNotInterested = 0;
      int nbDownloaders = 0;
      int nbChoked = 0;
      this.unchoken.clear();
      List<Peer> l = new LinkedList<Peer>(this.peerList.values());
      if (!this.isComplete()) Collections.sort(l, new DLRateComparator());
      else Collections.sort(l, new ULRateComparator());

      downloadRate = 0;
      for (Iterator it = l.iterator(); it.hasNext(); ) {
        Peer p = (Peer) it.next();
        if (p.getDLRate(false) > 0) {
          downloadRate += p.getDLRate(true) / (1024 * 10);
          //                    System.out.println(p + " rate: " +
          //                                       p.getDLRate(true) / (1024 * 10) +
          //                                       "ko/s");
        }

        DownloadTask dt = this.task.get(p.toString());
        if (nbDownloaders < 5 && dt != null) {
          if (!p.isInterested()) {
            this.unchoken.put(p.toString(), p);
            if (p.isChoked()) dt.ms.addMessageToQueue(new Message_PP(PeerProtocol.UNCHOKE));
            p.setChoked(false);

            while (this.unchokeList.remove(p)) ;
            nbNotInterested++;
          } else if (p.isChoked()) {
            this.unchoken.put(p.toString(), p);
            dt.ms.addMessageToQueue(new Message_PP(PeerProtocol.UNCHOKE));
            p.setChoked(false);
            while (this.unchokeList.remove(p)) ;
            nbDownloaders++;
          }

        } else {
          if (!p.isChoked()) {
            dt.ms.addMessageToQueue(new Message_PP(PeerProtocol.CHOKE));
            p.setChoked(true);
          }
          if (!this.unchokeList.contains(p)) this.unchokeList.add(p);
          nbChoked++;
        }
        p = null;
        dt = null;
      }
    }
    this.lastUnchoking = System.currentTimeMillis();
    if (this.optimisticUnchoke-- == 0) {
      this.optimisticUnchoke();
      this.optimisticUnchoke = 3;
    }
  }
 private synchronized void optimisticUnchoke() {
   if (!this.unchokeList.isEmpty()) {
     Peer p = null;
     do {
       p = (Peer) this.unchokeList.remove(0);
       synchronized (this.task) {
         DownloadTask dt = this.task.get(p.toString());
         if (dt != null) {
           dt.ms.addMessageToQueue(new Message_PP(PeerProtocol.UNCHOKE));
           p.setChoked(false);
           this.unchoken.put(p.toString(), p);
           System.out.println(p + " optimistically unchoken...");
         } else p = null;
         dt = null;
       }
     } while ((p == null) && (!this.unchokeList.isEmpty()));
     p = null;
   }
 }