/**
   * Displays an information bubble that alerts the user that the attribute specified in the
   * operator parameters was not found. The bubble is located at the operator and the process view
   * will change to said operator. This method is used after the error occurred during process
   * execution.
   *
   * @param error the error containing all the information about the operator, the parameter and the
   *     name of the attribute which was not found
   * @param i18nKey the i18n key which defines the title, text and button label for the bubble.
   *     Format is "gui.bubble.{i18nKey}.title", "gui.bubble.{i18nKey}.body" and
   *     "gui.bubble.{i18nKey}.button.label".
   * @param isError if {@code true}, an error bubble will be shown; otherwise a warning bubble is
   *     displayed
   * @param arguments optional i18n arguments
   * @return the {@link OperatorInfoBubble} instance, never {@code null}
   */
  private static OperatorInfoBubble displayAttributeNotFoundParameterInformation(
      final AttributeNotFoundError error,
      final boolean isError,
      final String i18nKey,
      final Object... arguments) {
    final Operator op = error.getOperator();
    final ParameterType param = op.getParameterType(error.getKey());
    final JButton ackButton =
        new JButton(I18N.getGUIMessage("gui.bubble." + i18nKey + ".button.label", arguments));
    ackButton.setToolTipText(I18N.getGUIMessage("gui.bubble." + i18nKey + ".button.tip"));

    String decoratorKey =
        param instanceof CombinedParameterType || param instanceof ParameterTypeAttributes
            ? "attributes_not_found_decoration"
            : "attribute_not_found_decoration";

    ParameterErrorBubbleBuilder builder =
        new ParameterErrorBubbleBuilder(
            RapidMinerGUI.getMainFrame(), op, param, decoratorKey, i18nKey, arguments);
    final ParameterErrorInfoBubble attributeNotFoundParameterBubble =
        builder
            .setHideOnDisable(true)
            .setAlignment(AlignedSide.BOTTOM)
            .setStyle(isError ? BubbleStyle.ERROR : BubbleStyle.WARNING)
            .setEnsureVisible(true)
            .hideCloseButton()
            .setHideOnProcessRun(true)
            .setAdditionalComponents(new JComponent[] {ackButton})
            .build();

    ackButton.addActionListener(
        new ActionListener() {

          @Override
          public void actionPerformed(ActionEvent e) {
            attributeNotFoundParameterBubble.killBubble(true);
          }
        });

    attributeNotFoundParameterBubble.setVisible(true);
    return attributeNotFoundParameterBubble;
  }
  /**
   * Displays an error bubble that alerts the user that the attribute specified in the parameters of
   * an operator was not found. The bubble is located at the operator and the process view will
   * change to said port. This method is used after the error occurred during process execution.
   *
   * @param error the error containing all the information about the operator, the parameter and the
   *     name of the attribute which was not found
   * @return the {@link OperatorInfoBubble} instance, never {@code null}
   */
  public static OperatorInfoBubble displayAttributeNotFoundParameterInformation(
      final AttributeNotFoundError error) {
    if (error == null) {
      throw new IllegalArgumentException("error must not be null!");
    }
    if (error.getOperator() == null) {
      throw new IllegalArgumentException("error operator must not be null!");
    }
    if (error.getKey() == null) {
      throw new IllegalArgumentException("error parameter key must not be null!");
    }

    String key;
    switch (error.getCode()) {
      case AttributeNotFoundError.ATTRIBUTE_NOT_FOUND_IN_REGULAR:
        key = "process_regular_attribute_not_found_parameter";
        break;
      case AttributeNotFoundError.ATTRIBUTE_NOT_FOUND:
      default:
        key = "process_attribute_not_found_parameter";
    }
    return displayAttributeNotFoundParameterInformation(
        error, true, key, error.getAttributeName(), error.getKey());
  }
  /**
   * Displays the best machting error bubble depending on the given {@link UserError}. If no
   * explicit match can be found, displays a generic error bubble.
   *
   * @param error the error in question, must not be {@code null}
   * @return the bubble instance, never {@code null}
   */
  public static BubbleWindow displayBubbleForUserError(final UserError error) {
    if (error == null) {
      throw new IllegalArgumentException("userError must not be null!");
    }

    if (error instanceof PortUserError) {
      PortUserError userError = (PortUserError) error;
      if (userError.getCode() == 149) {
        // for "no data" errors we display an error bubble instead of a dialog
        return displayInputPortNoDataInformation(userError.getPort());
      } else if (userError.getCode() == 156) {
        return displayInputPortWrongTypeInformation(
            userError.getPort(), userError.getExpectedType(), userError.getActualType());
      } else {
        return displayGenericPortError(userError);
      }
    } else if (error.getClass().equals(UndefinedParameterError.class)) {
      UndefinedParameterError userError = (UndefinedParameterError) error;
      if (userError.getCode() == 205 || userError.getCode() == 217) {
        // for "missing mandatory parameter" errors we display an error bubble
        // instead of a dialog
        Operator op = userError.getOperator();
        ParameterType param = op != null ? op.getParameterType(userError.getKey()) : null;
        if (op != null && param != null) {
          return displayMissingMandatoryParameterInformation(op, param);
        } else {
          return displayGenericUserError(error);
        }
      } else {
        Operator op = userError.getOperator();
        ParameterType param = op != null ? op.getParameterType(userError.getKey()) : null;
        if (op != null && param != null) {
          return displayMissingMandatoryParameterInformation(op, param);
        } else {
          return displayGenericUserError(error);
        }
      }
    } else if (error instanceof ParameterError) {
      ParameterError userError = (ParameterError) error;
      Operator op = userError.getOperator();
      ParameterType param = op != null ? op.getParameterType(userError.getKey()) : null;
      if (op != null && param != null) {
        return displayGenericParameterError(userError);
      } else {
        return displayGenericUserError(error);
      }
    } else if (error instanceof AttributeNotFoundError) {
      AttributeNotFoundError userError = (AttributeNotFoundError) error;
      Operator op = userError.getOperator();
      ParameterType param = op != null ? op.getParameterType(userError.getKey()) : null;
      if (op != null && param != null) {
        return displayAttributeNotFoundParameterInformation(userError);
      } else {
        return displayGenericUserError(error);
      }
    } else if (error instanceof ProcessExecutionUserErrorError) {
      ProcessExecutionUserErrorError userError = (ProcessExecutionUserErrorError) error;
      if (userError.getUserError() != null && userError.getUserError().getOperator() != null) {
        return displayUserErrorInExecutedProcess(userError);
      } else {
        return displayGenericUserError(error);
      }
    } else {
      return displayGenericUserError(error);
    }
  }