/**
  * Add messages to <code>messages</code> until there are no more messages to add or <code>size
  * </code> would exceed <code>maxSize</code>. If <code>size == maxSize</code>, a message in the
  * queue will be added even if it makes <code>size</code> exceed <code>maxSize</code>. If <code>
  * isUrgent</code> is set, only messages that are considered urgent are added.
  *
  * @param size the current size of <code>messages</code>
  * @param minSize the size when <code>messages</code> is empty
  * @param maxSize the maximum size of <code>messages</code>
  * @param now the current time
  * @param messages the list that messages will be added to
  * @param isUrgent <code>true</code> if only urgent messages should be added
  * @return the size of <code>messages</code>, multiplied by -1 if there were messages that
  *     didn't fit
  */
 private int addUrgentMessages(
     int size, int minSize, int maxSize, long now, ArrayList<MessageItem> messages) {
   assert (size >= 0);
   assert (minSize >= 0);
   assert (maxSize >= minSize);
   if (size < 0) size = -size; // FIXME remove extra paranoia
   int added = 0;
   while (true) {
     boolean addedNone = true;
     int lists = 0;
     if (nonEmptyItemsWithID == null) return size;
     lists += nonEmptyItemsWithID.size();
     Items list = nonEmptyItemsWithID.head();
     for (int i = 0; i < lists && list != null; i++) {
       if (list.items.isEmpty()) {
         // Should not happen, but check for it anyway since it keeps happening. :(
         Logger.error(this, "List is in nonEmptyItemsWithID yet it is empty?!: " + list);
         nonEmptyItemsWithID.remove(list);
         addToEmptyBackward(list);
         if (nonEmptyItemsWithID.isEmpty()) return size;
         list = nonEmptyItemsWithID.head();
         continue;
       }
       MessageItem item = list.items.getFirst();
       int thisSize = item.getLength();
       boolean oversize = false;
       if (size + 2 + thisSize > maxSize) {
         if (size == minSize) {
           // Won't fit regardless, send it on its own.
           oversize = true;
         } else {
           // Send what we have so far.
           if (logDEBUG && added != 0)
             Logger.debug(
                 this,
                 "Added "
                     + added
                     + " urgent messages, could add more but out of space at "
                     + size);
           return -size;
         }
       }
       size += 2 + thisSize;
       list.items.removeFirst();
       // Move to end of list.
       Items prev = list.getPrev();
       nonEmptyItemsWithID.remove(list);
       list.timeLastSent = now;
       if (!list.items.isEmpty()) {
         addToNonEmptyBackward(list);
       } else {
         addToEmptyBackward(list);
       }
       if (prev == null) list = nonEmptyItemsWithID.head();
       else list = prev.getNext();
       messages.add(item);
       added++;
       addedNone = false;
       if (oversize) {
         if (logDEBUG) Logger.debug(this, "Returning with oversize urgent message");
         return size;
       }
     }
     if (addedNone) {
       if (logDEBUG && added != 0)
         Logger.debug(
             this,
             "Added "
                 + added
                 + " urgent messages, size now "
                 + size
                 + " no more queued at this priority");
       return size;
     }
   }
 }
 private int addNonUrgentMessages(
     int size, int minSize, int maxSize, long now, ArrayList<MessageItem> messages) {
   assert (size >= 0);
   assert (minSize >= 0);
   assert (maxSize >= minSize);
   if (size < 0) size = -size; // FIXME remove extra paranoia
   if (itemsNonUrgent == null) return size;
   int added = 0;
   for (ListIterator<MessageItem> items = itemsNonUrgent.listIterator(); items.hasNext(); ) {
     MessageItem item = items.next();
     int thisSize = item.getLength();
     boolean oversize = false;
     if (size + 2 + thisSize > maxSize) {
       if (size == minSize) {
         // Won't fit regardless, send it on its own.
         oversize = true;
       } else {
         // Send what we have so far.
         if (logDEBUG && added != 0)
           Logger.debug(
               this,
               "Returning with "
                   + added
                   + " non-urgent messages (have more but they don't fit)");
         return -size;
       }
     }
     size += 2 + thisSize;
     items.remove();
     messages.add(item);
     if (itemsByID != null) {
       long id = item.getID();
       Items tracker = itemsByID.get(id);
       if (tracker != null) {
         tracker.timeLastSent = now;
         DoublyLinkedList<? super Items> parent = tracker.getParent();
         // Demote the corresponding tracker to maintain round-robin.
         if (tracker.items.isEmpty()) {
           if (emptyItemsWithID == null) emptyItemsWithID = new DoublyLinkedListImpl<Items>();
           if (parent == null) {
             Logger.error(this, "Tracker is in itemsByID but not in either list! (empty)");
           } else if (parent == emptyItemsWithID) {
             // Normal. Remove it so we can re-add it in the right place.
             emptyItemsWithID.remove(tracker);
           } else if (parent == nonEmptyItemsWithID) {
             Logger.error(this, "Tracker is in non empty items list when is empty");
             nonEmptyItemsWithID.remove(tracker);
           } else assert (false);
           addToEmptyBackward(tracker);
         } else {
           if (nonEmptyItemsWithID == null)
             nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>();
           if (parent == null) {
             Logger.error(this, "Tracker is in itemsByID but not in either list! (non-empty)");
           } else if (parent == nonEmptyItemsWithID) {
             // Normal. Remove it so we can re-add it in the right place.
             nonEmptyItemsWithID.remove(tracker);
           } else if (parent == emptyItemsWithID) {
             Logger.error(this, "Tracker is in empty items list when is non-empty");
             emptyItemsWithID.remove(tracker);
           } else assert (false);
           addToNonEmptyBackward(tracker);
         }
       }
     }
     added++;
     if (oversize) {
       if (logDEBUG) Logger.debug(this, "Returning with non-urgent oversize message");
       return size;
     }
   }
   if (logDEBUG && added != 0)
     Logger.debug(this, "Returning with " + added + " non-urgent messages (all gone)");
   return size;
 }