/**
   * Process a comment box by putting it into a property of the corresponding node.
   *
   * @param box a comment box
   * @param edge the edge that connects the box with the real node
   * @param oppositePort the port of the real node to which the edge is incident
   * @param realNode the normal node that is connected with the comment
   */
  private void processBox(
      final LNode box, final LEdge edge, final LPort oppositePort, final LNode realNode) {
    boolean topFirst, onlyTop = false, onlyBottom = false;
    if (realNode.getProperty(LayoutOptions.PORT_CONSTRAINTS).isSideFixed()) {
      boolean hasNorth = false, hasSouth = false;
      portLoop:
      for (LPort port1 : realNode.getPorts()) {
        for (LPort port2 : port1.getConnectedPorts()) {
          if (!port2.getNode().getProperty(LayoutOptions.COMMENT_BOX)) {
            if (port1.getSide() == PortSide.NORTH) {
              hasNorth = true;
              break portLoop;
            }
            if (port1.getSide() == PortSide.SOUTH) {
              hasSouth = true;
              break portLoop;
            }
          }
        }
      }
      onlyTop = hasSouth && !hasNorth;
      onlyBottom = hasNorth && !hasSouth;
    }
    if (!onlyTop && !onlyBottom && !realNode.getLabels().isEmpty()) {
      double labelPos = 0;
      for (LLabel label : realNode.getLabels()) {
        labelPos += label.getPosition().y + label.getSize().y / 2;
      }
      labelPos /= realNode.getLabels().size();
      topFirst = labelPos >= realNode.getSize().y / 2;
    } else {
      topFirst = !onlyBottom;
    }

    List<LNode> boxList;
    if (topFirst) {
      // determine the position to use, favoring the top position
      List<LNode> topBoxes = realNode.getProperty(InternalProperties.TOP_COMMENTS);
      if (topBoxes == null) {
        boxList = Lists.newArrayList();
        realNode.setProperty(InternalProperties.TOP_COMMENTS, boxList);
      } else if (onlyTop) {
        boxList = topBoxes;
      } else {
        List<LNode> bottomBoxes = realNode.getProperty(InternalProperties.BOTTOM_COMMENTS);
        if (bottomBoxes == null) {
          boxList = Lists.newArrayList();
          realNode.setProperty(InternalProperties.BOTTOM_COMMENTS, boxList);
        } else {
          if (topBoxes.size() <= bottomBoxes.size()) {
            boxList = topBoxes;
          } else {
            boxList = bottomBoxes;
          }
        }
      }
    } else {
      // determine the position to use, favoring the bottom position
      List<LNode> bottomBoxes = realNode.getProperty(InternalProperties.BOTTOM_COMMENTS);
      if (bottomBoxes == null) {
        boxList = Lists.newArrayList();
        realNode.setProperty(InternalProperties.BOTTOM_COMMENTS, boxList);
      } else if (onlyBottom) {
        boxList = bottomBoxes;
      } else {
        List<LNode> topBoxes = realNode.getProperty(InternalProperties.TOP_COMMENTS);
        if (topBoxes == null) {
          boxList = Lists.newArrayList();
          realNode.setProperty(InternalProperties.TOP_COMMENTS, boxList);
        } else {
          if (bottomBoxes.size() <= topBoxes.size()) {
            boxList = bottomBoxes;
          } else {
            boxList = topBoxes;
          }
        }
      }
    }

    // add the comment box to one of the two possible lists
    boxList.add(box);

    // set the opposite port as property for the comment box
    box.setProperty(InternalProperties.COMMENT_CONN_PORT, oppositePort);
    // detach the edge and the opposite port
    if (edge.getTarget() == oppositePort) {
      edge.setTarget(null);
      if (oppositePort.getDegree() == 0) {
        oppositePort.setNode(null);
      }
    } else {
      edge.setSource(null);
      if (oppositePort.getDegree() == 0) {
        oppositePort.setNode(null);
      }
    }
    edge.getBendPoints().clear();
  }