/**
   * Provides a way to determine the next visually represented model location at which one might
   * place a caret. Some views may not be visible, they might not be in the same order found in the
   * model, or they just might not allow access to some of the locations in the model.
   *
   * <p>NOTE: You should only call this method if the passed-in <code>javax.swing.text.View</code>
   * is an instance of {@link TokenOrientedView} and <code>javax.swing.text.TabExpander</code>;
   * otherwise, a <code>ClassCastException</code> could be thrown.
   *
   * @param pos the position to convert >= 0
   * @param a the allocated region in which to render
   * @param direction the direction from the current position that can be thought of as the arrow
   *     keys typically found on a keyboard. This will be one of the following values:
   *     <ul>
   *       <li>SwingConstants.WEST
   *       <li>SwingConstants.EAST
   *       <li>SwingConstants.NORTH
   *       <li>SwingConstants.SOUTH
   *     </ul>
   *
   * @return the location within the model that best represents the next location visual position
   * @exception BadLocationException
   * @exception IllegalArgumentException if <code>direction</code> doesn't have one of the legal
   *     values above
   */
  public static int getNextVisualPositionFrom(
      int pos, Position.Bias b, Shape a, int direction, Position.Bias[] biasRet, View view)
      throws BadLocationException {

    biasRet[0] = Position.Bias.Forward;

    // Do we want the "next position" above, below, to the left or right?
    switch (direction) {
      case NORTH:
      case SOUTH:
        if (pos == -1) {
          pos = (direction == NORTH) ? Math.max(0, view.getEndOffset() - 1) : view.getStartOffset();
          break;
        }
        RSyntaxTextArea target = (RSyntaxTextArea) view.getContainer();
        Caret c = (target != null) ? target.getCaret() : null;
        // YECK! Ideally, the x location from the magic caret
        // position would be passed in.
        Point mcp;
        if (c != null) mcp = c.getMagicCaretPosition();
        else mcp = null;
        int x;
        if (mcp == null) {
          Rectangle loc = target.modelToView(pos);
          x = (loc == null) ? 0 : loc.x;
        } else {
          x = mcp.x;
        }
        if (direction == NORTH) pos = getPositionAbove(target, pos, x, (TabExpander) view);
        else pos = getPositionBelow(target, pos, x, (TabExpander) view);
        break;

      case WEST:
        if (pos == -1) pos = Math.max(0, view.getEndOffset() - 1);
        else pos = Math.max(0, pos - 1);
        break;

      case EAST:
        if (pos == -1) pos = view.getStartOffset();
        else pos = Math.min(pos + 1, view.getDocument().getLength());
        break;

      default:
        throw new IllegalArgumentException("Bad direction: " + direction);
    }

    return pos;
  }