/** This method starts the thread and begins to download the file. */
  public void run() {
    int maxThreads = Integer.parseInt(mainApp.getPrefValue("ServerSettingsThreadCount"));
    int runningThreads = 0;
    HashMap<String, Integer> downloadedBytes = new HashMap<String, Integer>();
    HashMap<String, Integer> lastProgBarUpdate = new HashMap<String, Integer>();

    // loop at all segments of the download file
    while (!shutdown && (segQueue.hasMoreSegments() || runningThreads > 0)) {
      // more segments to go?
      while (segQueue.hasMoreSegments()
          && runningThreads < maxThreads
          && !pause
          && nioClient.hasFreeSlot()) {
        // get next download segment of the download file
        DownloadFileSegment seg = segQueue.nextSegment();
        if (seg == null) break;
        String filename = seg.getDlFile().getFilename();
        logger.msg("Downloading next segment of file: " + filename, MyLogger.SEV_DEBUG);

        // create new response handler
        RspHandler newHandler = new RspHandler(seg);
        activeRspHandlers.add(newHandler);

        // map the new response handler to the download file
        Vector<RspHandler> tmpVector = dlFileRspHandlerMap.get(seg.getDlFile());
        if (tmpVector == null) tmpVector = new Vector<RspHandler>();
        tmpVector.add(newHandler);
        dlFileRspHandlerMap.put(seg.getDlFile(), tmpVector);

        // start data download
        nioClient.fetchArticleData(seg.getGroups().firstElement(), seg.getArticleId(), newHandler);

        // increase thread counter
        runningThreads++;
      }

      // check if the next element of the result set is already finished
      Vector<RspHandler> toRemoveVector = new Vector<RspHandler>();
      for (int i = 0; i < activeRspHandlers.size(); i++) {
        RspHandler handler = activeRspHandlers.get(i);

        // handle error response from NNTP server
        if (handler.getError() == RspHandler.ERR_NONE) {
          // no error, do nothing
        } else if (handler.getError() == RspHandler.ERR_AUTH) {
          // do nothing for this error (?)
        } else if (handler.getError() == RspHandler.ERR_FETCH) {
          // TODO: handle "430 no such article" error (?)
          String msg =
              "no such article found: <"
                  + handler.dlFileSeg().getArticleId()
                  + "> ("
                  + handler.getErrorMsg()
                  + ")";
          logger.msg(msg, MyLogger.SEV_WARNING);
        } else {
          // all other errors
          shutdown = true;
        }

        // update downloaded byte counter ...
        DownloadFile dlFile = handler.dlFileSeg().getDlFile();
        String filename = dlFile.getFilename();
        int bytes = 0;
        Integer bytesInt = downloadedBytes.get(filename);
        if (bytesInt != null) bytes = bytesInt;
        bytes += handler.newByteCount();
        downloadedBytes.put(filename, bytes);

        // ... and progres bar in main window
        int last = 0;
        Integer lastInt = lastProgBarUpdate.get(filename);
        if (lastInt != null) last = lastInt;
        last = updateProgressBar(bytes, last, dlFile);
        lastProgBarUpdate.put(filename, last);

        // all data downloaded?
        if (handler.isFinished()) {
          toRemoveVector.add(handler);
          runningThreads--;
          decrSegCount(filename); // decrease main window segment
          // counter

          // segment done, so check if whole download file is finished
          // now
          dlFile.removeSegment(handler.dlFileSeg().getIndex());
          if (!dlFile.hasMoreSegments()) {
            try {
              handleFinishedDlFile(dlFile);
            } catch (Exception e) {
              logger.printStackTrace(e);
            }
          }
        }
      }
      activeRspHandlers.removeAll(toRemoveVector);
      toRemoveVector.removeAllElements();

      // all tasks done?
      if (!segQueue.hasMoreSegments() && runningThreads == 0) {
        break;
      }

      try {
        // let the thread sleep a bit
        Thread.sleep(10);
      } catch (InterruptedException e) {
        // shutdown if interrupted
        shutdown = true;
      }
    } // end of main loop

    logger.msg("FileDownloader has finished downloading all files", MyLogger.SEV_DEBUG);
  }