/**
   * @param currentElement
   * @param precedingElements
   * @param context
   * @return cellOfElement
   */
  private Cell<BPMNElement> placeElement(
      BPMNElement currentElement, List<LayoutingElement> precedingElements, GridContext context) {
    Cell<BPMNElement> newCell;
    if (precedingElements.isEmpty()) {
      // StartEvents
      context.startCell.setValue(currentElement);
      newCell = context.startCell;
      context.startCell = context.startCell.beneath();
    } else {
      Cell<BPMNElement> leftCell;
      newCell = context.grid.getCellOfItem(currentElement); // not
      // null
      // if
      // join
      if (currentElement.isJoin()) {

        Point tmp;
        boolean splitFound = false;
        BPMNElement split = (BPMNElement) currentElement.prevSplit();
        if (split != null) {
          // get all close splits
          Queue<BPMNElement> splits =
              new PriorityQueue<BPMNElement>(
                  precedingElements.size() / 2, // should be a
                  // good rule of
                  // thumb
                  new BackwardDistanceComperator(currentElement));
          splits.add(split);
          for (LayoutingElement elem : precedingElements) {
            split = (BPMNElement) elem.prevSplit();
            if (split != null && !splits.contains(split)) {
              splits.add(split);
            }
          }
          split = null;
          // get split with most connections
          int maxCon = 0;
          for (BPMNElement target : splits) {
            if (target == currentElement) {
              // beeing my own splits only makes trouble
              continue;
            } else if (target.getParent() != currentElement.getParent()) {

              continue;
            }
            int curCon = 0;
            for (LayoutingElement elem : precedingElements) {
              if (elem.backwardDistanceTo(target) < Integer.MAX_VALUE) {
                curCon++;
              }
            }
            if (curCon > maxCon) {
              maxCon = curCon;
              split = target;
            }
          }
          splitFound = split != null;
        }

        int x = 0;
        int yAcc = 0;
        int yCnt = 0;
        for (LayoutingElement el : precedingElements) {
          BPMNElement elem = (BPMNElement) el;
          tmp = context.grid.find(context.grid.getCellOfItem(elem));
          if (tmp == null) {
            Grid<BPMNElement> preGrid = getContextByElement(elem).grid;
            tmp = preGrid.find(preGrid.getCellOfItem(elem));
            if (tmp == null) {
              tmp = new Point(0, 0);
            }
          } else {
            yAcc += tmp.y;
            yCnt++;
          }
          x = Math.max(x, tmp.x);
        }
        if (splitFound) {

          leftCell = context.grid.getCellOfItem(split).getParent().get(x);
          // set path to split unpackable
          for (Cell<BPMNElement> cCell = leftCell;
              cCell.getValue() != split;
              cCell = cCell.getPrevCell()) {
            cCell.setPackable(false);
          }

        } else {
          if (yCnt == 0) {
            leftCell = context.grid.getFirstRow().above().get(x);
          } else {
            leftCell = context.grid.get(yAcc / yCnt).get(x);
          }
        }
        if (newCell != null && newCell.getValue() == currentElement) {
          newCell.setValue(null);
        }
        newCell = leftCell.after();

        // set all incoming pathes unpackable
        for (LayoutingElement e : precedingElements) {
          BPMNElement el = (BPMNElement) e;
          Cell<BPMNElement> target = context.grid.getCellOfItem(el);
          if (target == null) {
            // don't set unpackable in other grids (other edge
            // layout)
            continue;
          }
          Cell<BPMNElement> start = target.getParent().get(x + 1);
          for (Cell<BPMNElement> cCell = start; cCell != target; cCell = cCell.getPrevCell()) {
            cCell.setPackable(false);
          }
        }

        // if not prelayouted
      } else if (newCell == null) {
        BPMNElement preElem = (BPMNElement) precedingElements.get(0);
        leftCell = context.grid.getCellOfItem(preElem);
        if (leftCell == null) {
          Grid<BPMNElement> preGrid = getContextByElement(preElem).grid;
          Cell<BPMNElement> preCell = preGrid.getCellOfItem(preElem);
          if (preCell == null) {
            System.err.println("Cannot find Cell for " + preElem);
          }

          List<Grid<BPMNElement>> grids = superGrid.getGrids();
          Row<BPMNElement> newRow = null;
          if (grids.indexOf(preGrid) < grids.indexOf(context.grid)) {
            newRow = context.grid.addFirstRow();
          } else {
            newRow = context.grid.addLastRow();
          }
          leftCell = newRow.get(Math.max(0, preCell.getParent().find(preCell)));
        }
        newCell = leftCell.after();
      }
      if (newCell.isFilled() && !newCell.getValue().equals(currentElement)) {
        newCell.getParent().insertRowBeneath();
        newCell = newCell.beneath();
      }
      newCell.setValue(currentElement);
    }
    return newCell;
  }
  public void doLayout() {
    superGrid = new SuperGrid<BPMNElement>();
    parent2Context.clear();
    lane2LaneChilds.clear();
    maxLaneDepth = 0;

    if (parent == null) {
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        prepareLanes((BPMNElement) pool, 1);
      }
    }

    layoutElements();

    if (parent == null) {
      // set collapsed pools
      List<LayoutingElement> collapsedPools =
          this.diagram.getElementsOfType(BPMNType.CollapsedPool);
      Grid<BPMNElement> cpGrid = new Grid<BPMNElement>();
      superGrid.add(0, cpGrid);
      for (LayoutingElement collapsedPool : collapsedPools) {
        // make them small to not disturb finding the biggest ones in
        // each row / column

        collapsedPool.setGeometry(new LayoutingBoundsImpl(0, 0, 0, COLLAPSED_POOL_HEIGHT));
        for (Cell<BPMNElement> insertCell : cpGrid.addLastRow()) {
          insertCell.setValue((BPMNElement) collapsedPool);
        }
      }

      calcGeometry(superGrid);

      poolWidth = Math.max(poolWidth, COLLAPSED_POOL_MIN_WIDTH);

      // place Lanes
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        Grid<BPMNElement> firstGrid = findFirstGridOfPool((BPMNElement) pool);
        int firstGridFirstRowIndex = superGrid.findRow(firstGrid.getFirstRow());

        double poolY = 0;
        for (int i = 0; i < firstGridFirstRowIndex; i++) {
          poolY += heightOfRow[i];
        }
        placeLane((BPMNElement) pool, poolY, 0);
      }

      writeGeometry(superGrid);

      // set pools to start at x = CELL_MARGIN & correct size

      for (LayoutingElement collapsedPool : collapsedPools) {
        collapsedPool.setGeometry(
            new LayoutingBoundsImpl(
                CELL_MARGIN, collapsedPool.getGeometry().getY(), poolWidth, COLLAPSED_POOL_HEIGHT));
        ((BPMNElement) collapsedPool).updateDataModel();
      }

      // convert Coordinates of Elements in Lanes from absolut to
      // realitive
      for (LayoutingElement pool : diagram.getElementsOfType(BPMNType.Pool)) {
        correctLaneElements((BPMNElement) pool, pool.getGeometry().getY(), 0);
      }

    } else {
      calcGeometry(superGrid);
      writeGeometry(superGrid);
    }
  }