/**
     * Finds the "center" of the setup of currently available sides.
     *
     * @return The side considered as "center" for current setup of available port-sides.
     */
    private LoopSide findCenter() {
      final EnumSet<LoopSide> retVal;
      Iterator<LoopSide> itr;
      switch (loopSides.availableStraightSides().size()) {
        case 4:
          // all sides available: south is the center.
          return LoopSide.S;
        case 3:
          // only one side not available: the side not available is the center.
          return loopSides.allRemovedStraightSides().iterator().next();
        case 2:
          // Two sides were removed, two sides remain available. The center side depends on
          // the pattern of the removed/available sides:
          retVal = loopSides.availableStraightSides();
          itr = retVal.iterator();
          LoopSide first = itr.next();
          LoopSide second = itr.next();

          if (first.opposite() == second) {
            // the removed sides are opposed to each other.
            if (retVal.contains(LoopSide.S)) {
              // n-s sides were removed. Center is east.
              return LoopSide.E;
            } else {
              // w-e sides were removed. center is south.
              return LoopSide.S;
            }
          } else {
            // the removed sides were neighbors. The center is between them.
            if (first.left().left() == second) {
              // first element is right of second element.
              return first.left();
            } else {
              // first element is left of second element
              return first.right();
            }
          }
        case 1:
          // only one straight side is remaining. The opposite of this side is the center.
          retVal = loopSides.availableStraightSides();
          return retVal.iterator().next().opposite();
        case 0:
          // all straight sides were removed. The center is the s-e corner.
          return LoopSide.SE;
        default:
          return null;
      }
    }
    /**
     * Distributes the self-loops equally around the node.
     *
     * @param components
     */
    public void calculate(final List<ConnectedSelfLoopComponent> components) {
      final List<ConnectedSelfLoopComponent> withLongText = Lists.newArrayList();
      final List<ConnectedSelfLoopComponent> withShortText = Lists.newArrayList();
      final List<ConnectedSelfLoopComponent> withoutText = Lists.newArrayList();

      // first we are going to check for the size of possible edge labels
      for (final ConnectedSelfLoopComponent component : components) {
        if (component.getTextWidth() > MAX_TEXT_LENGTH) {
          withLongText.add(component);
        } else if (component.getTextWidth() > 0.0) {
          withShortText.add(component);
        } else {
          withoutText.add(component);
        }
      }

      // if there is only one loop with text, handle this loop as a long text loop
      if (withShortText.size() == 1 && withLongText.isEmpty()) {
        withLongText.addAll(withShortText);
        withShortText.clear();
      }

      // try to put the components with long text to one of the horizontal across loop sides.
      // if there are no such sides available, handle long text loops as short text loops.
      if (!withLongText.isEmpty()
          && loopSides.availableSides().contains(LoopSide.N)
          && loopSides.availableSides().contains(LoopSide.S)) {
        assignAcrossSide(withLongText);
      } else {
        withShortText.addAll(withLongText);
      }

      // put the components with short text to one of the corner loop sides
      if (!withShortText.isEmpty()) {
        assignCornerSide(withShortText);
      }

      // Last but not least: loops without text get spread equally.
      if (!withoutText.isEmpty()) {
        // ////////////////////////////////////////////////////////////
        // First of all we try to put those ConnectedSelfLoops who have more than one edge,
        // to one of the available straight sides.
        // ////////////////////////////////////////////////////////////

        final Set<LoopSide> availableStraights = loopSides.availableStraightSides();
        if (!availableStraights.isEmpty()) {
          final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator();
          final Iterator<LoopSide> itrAvailable = Iterables.cycle(availableStraights).iterator();

          while (itrComponent.hasNext()) {

            // look for a component with more than one edge
            ConnectedSelfLoopComponent component = itrComponent.next();
            while (itrComponent.hasNext() && component.getEdges().size() < 2) {
              component = itrComponent.next();
            }

            // If we have found a component with more than one edge, assign the next
            // straight side to the component.
            if (component.getEdges().size() > 1) {
              final LoopSide side = itrAvailable.next();
              component.setLoopSide(side, true);
              itrComponent.remove();
              loopSides.removeSide(side);
            }
          }
        }

        // ////////////////////////////////////////////////////////////
        // Now we can distribute the remaining components around the node.
        // ////////////////////////////////////////////////////////////

        // number of required sides
        final int number = withoutText.size();

        // Center of the assigned pattern.
        final LoopSide center = findCenter();

        // The list of portSides we assign to the set of connected components.
        final List<LoopSide> portSides = Lists.newArrayList();

        // How many times must a "full set" of LoopSide-elements be constructed?
        final int fullSets = number / loopSides.availableNotAcrossSides().size();

        // Construct the full sets.
        for (int i = 0; i < fullSets; i++) {
          portSides.addAll(loopSides.availableNotAcrossSides());
        }

        // How many elements are remaining to be constructed after the full sets have been
        // constructed?
        int remainer = number % loopSides.availableNotAcrossSides().size();

        // If more than three elements are remaining, the pattern includes all four corners
        // and the sides that would be included in the pattern for (n-4) elements.
        if (remainer > 3) {
          portSides.addAll(LoopSide.getAllCornerSides());
          remainer -= 4;
        }

        // Create the pattern for 1-3 elements.
        switch (remainer) {
          case 3:
            // opposed side (from center) and the the same sides as for two elements
            portSides.add(center.opposite());
          case 2:
            // the first AVAILABLE neighbors of the neighbors of the opposed side of the
            // center
            LoopSide tmpSide = center.opposite().left();
            do {
              tmpSide = tmpSide.left();
            } while (!loopSides.availableSides().contains(tmpSide));
            portSides.add(tmpSide);

            tmpSide = center.opposite().right();
            do {
              tmpSide = tmpSide.right();
            } while (!loopSides.availableSides().contains(tmpSide));
            portSides.add(tmpSide);
            break;
          case 1:
            // opposed side (from center)
            portSides.add(center.opposite());
            break;
          default:
            break;
        }

        // Now assign the remaining sides to the set of portSides we just have constructed.
        final Iterator<LoopSide> itrSides = portSides.iterator();
        final Iterator<ConnectedSelfLoopComponent> itrComponent = withoutText.iterator();
        while (itrSides.hasNext() && itrComponent.hasNext()) {
          itrComponent.next().setLoopSide(itrSides.next(), true);
        }
      }
    }