private static void printMethodWithMultiType(
      ProcessingEnvironment env,
      TypeMap type_map,
      PrintWriter writer,
      TypeElement interface_decl,
      ExecutableElement method,
      Map<VariableElement, TypeInfo> typeinfos_instance,
      Mode mode,
      boolean generate_error_checks,
      boolean context_specific) {
    Utils.printDocComment(writer, method, env);
    if (method.getAnnotation(Deprecated.class) != null) {
      writer.println("\t@Deprecated");
    }
    if (interface_decl.getAnnotation(Private.class) == null
        && method.getAnnotation(Private.class) == null) {
      writer.print("\tpublic static ");
    } else {
      writer.print("\tstatic ");
    }
    writer.print(getResultType(method, false));
    StripPostfix strip_annotation = method.getAnnotation(StripPostfix.class);
    String method_name;
    Alternate alt_annotation = method.getAnnotation(Alternate.class);
    method_name =
        alt_annotation == null || alt_annotation.javaAlt()
            ? method.getSimpleName().toString()
            : alt_annotation.value();
    if (strip_annotation != null && mode == Mode.NORMAL) {
      method_name = getPostfixStrippedName(type_map, interface_decl, method);
    }
    writer.print(" " + method_name + "(");
    generateParametersJava(writer, method, typeinfos_instance, false, true, mode);
    writer.println(") {");

    final TypeMirror result_type = Utils.getMethodReturnType(method);
    boolean has_result = !result_type.equals(env.getTypeUtils().getNoType(TypeKind.VOID));

    final Reuse reuse_annotation = method.getAnnotation(Reuse.class);
    if (reuse_annotation != null) {
      writer.print("\t\t");
      if (has_result || method.getAnnotation(GLreturn.class) != null) {
        writer.print("return ");
      }

      writer.print(
          reuse_annotation.value()
              + "."
              + (reuse_annotation.method().length() > 0 ? reuse_annotation.method() : method_name)
              + "(");
      generateParametersJava(writer, method, typeinfos_instance, false, false, mode);
      writer.println(");\n\t}");
      return;
    }

    if (context_specific) {
      type_map.printCapabilitiesInit(writer);
      writer.print(
          "\t\tlong " + Utils.FUNCTION_POINTER_VAR_NAME + " = " + type_map.getCapabilities() + ".");
      writer.println(Utils.getFunctionAddressName(interface_decl, method, true) + ";");
      writer.print("\t\tBufferChecks.checkFunctionAddress(");
      writer.println(Utils.FUNCTION_POINTER_VAR_NAME + ");");
    }
    final Code code_annotation = method.getAnnotation(Code.class);
    if (code_annotation != null && code_annotation.value().length() > 0) {
      writer.println(code_annotation.value());
    }
    printBufferObjectChecks(writer, method, mode, context_specific);
    printParameterChecks(writer, method, typeinfos_instance, mode, generate_error_checks);
    printParameterCaching(writer, interface_decl, method, mode, context_specific);

    if (code_annotation != null && code_annotation.javaBeforeNative().length() > 0) {
      writer.println(code_annotation.javaBeforeNative());
    }
    writer.print("\t\t");

    final PointerWrapper pointer_wrapper_annotation = method.getAnnotation(PointerWrapper.class);
    if (has_result) {
      writer.print(getResultType(method, false) + " " + Utils.RESULT_VAR_NAME);

      if (code_annotation != null && code_annotation.tryBlock()) {
        writer.print(" = " + getDefaultResultValue(method));
        writer.println(";\n\t\ttry {");
        writer.print("\t\t\t" + Utils.RESULT_VAR_NAME);
      }

      writer.print(" = ");
      if (pointer_wrapper_annotation != null) {
        if (pointer_wrapper_annotation.factory().length() > 0) {
          writer.print(pointer_wrapper_annotation.factory() + "(");
        } else {
          writer.print("new " + getResultType(method, false) + "(");
        }
      }
    } else if (method.getAnnotation(GLreturn.class) != null) {
      has_result = true;
      Utils.printGLReturnPre(writer, method, method.getAnnotation(GLreturn.class), type_map);
    }
    writer.print(Utils.getSimpleNativeMethodName(method, generate_error_checks, context_specific));
    if (mode == Mode.BUFFEROBJECT) {
      writer.print(Utils.BUFFER_OBJECT_METHOD_POSTFIX);
    }
    writer.print("(");
    boolean first_parameter =
        printMethodCallArguments(writer, method, typeinfos_instance, mode, type_map);
    if (context_specific) {
      if (!first_parameter) {
        writer.print(", ");
      }
      writer.print(Utils.FUNCTION_POINTER_VAR_NAME);
    }
    if (has_result && pointer_wrapper_annotation != null) {
      writer.print(")");
      if (pointer_wrapper_annotation.params().length() > 0) {
        writer.print(", " + pointer_wrapper_annotation.params());
      }
    }
    writer.println(");");

    if (code_annotation != null && code_annotation.javaAfterNative().length() > 0) {
      writer.println(code_annotation.javaAfterNative());
    }

    final String tabs = code_annotation != null && code_annotation.tryBlock() ? "\t\t\t" : "\t\t";
    if (generate_error_checks && method.getAnnotation(NoErrorCheck.class) == null) {
      type_map.printErrorCheckMethod(writer, method, tabs);
    }
    // DISABLED: indirect buffer support
    // printNondirectParameterCopies(writer, method, mode);
    if (has_result) {
      if (method.getAnnotation(GLreturn.class) == null) {
        if (ByteBuffer.class.equals(Utils.getJavaType(result_type))) {
          writer.println(
              tabs
                  + "return LWJGLUtil.CHECKS && "
                  + Utils.RESULT_VAR_NAME
                  + " == null ? null : "
                  + Utils.RESULT_VAR_NAME
                  + ".order(ByteOrder.nativeOrder());"); // safeNewBuffer returns a direct
                                                         // ByteBuffer with BIG_ENDIAN order.
        } else {
          writer.println(tabs + "return " + Utils.RESULT_VAR_NAME + ";");
        }
      } else {
        Utils.printGLReturnPost(writer, method, method.getAnnotation(GLreturn.class), type_map);
      }
    }

    if (code_annotation != null && code_annotation.tryBlock()) {
      writer.println("\t\t} finally {");
      writer.println(code_annotation.javaFinally());
      writer.println("\t\t}");
    }
    writer.println("\t}");
  }
  private static boolean printMethodCallArgument(
      PrintWriter writer,
      ExecutableElement method,
      VariableElement param,
      Map<VariableElement, TypeInfo> typeinfos_instance,
      Mode mode,
      boolean first_parameter,
      TypeMap type_map) {
    if (!first_parameter) {
      writer.print(", ");
    }

    AnnotationMirror auto_annotation = Utils.getParameterAutoAnnotation(param);
    Constant constant_annotation = param.getAnnotation(Constant.class);
    if (constant_annotation != null) {
      writer.print(constant_annotation.value());
    } else if (auto_annotation != null && mode == Mode.NORMAL) {
      Class param_type = NativeTypeTranslator.getClassFromType(auto_annotation.getAnnotationType());
      if (AutoType.class.equals(param_type)) {
        final AutoType auto_type_annotation = param.getAnnotation(AutoType.class);
        final VariableElement auto_parameter =
            Utils.findParameter(method, auto_type_annotation.value());
        final String auto_type = typeinfos_instance.get(auto_parameter).getAutoType();
        if (auto_type == null) {
          throw new RuntimeException(
              "No auto type for parameter " + param.getSimpleName() + " in method " + method);
        }
        writer.print(auto_type);
      } else if (AutoSize.class.equals(param_type)) {
        final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
        if (!auto_size_annotation.useExpression()) {
          final String auto_parameter_name = auto_size_annotation.value();
          final VariableElement auto_target_param =
              Utils.findParameter(method, auto_parameter_name);
          final TypeInfo auto_target_type_info = typeinfos_instance.get(auto_target_param);
          final boolean shift_remaining =
              !hasAnyParameterAutoTypeAnnotation(method, auto_target_param)
                  && Utils.isParameterMultiTyped(auto_target_param);
          int shifting = 0;
          if (shift_remaining) {
            shifting = getBufferElementSizeExponent(auto_target_type_info.getType());
            if (shifting > 0) {
              writer.print("(");
            }
          }
          if (auto_size_annotation.canBeNull()) {
            writer.print(
                "("
                    + auto_parameter_name
                    + " == null ? 0 : "
                    + auto_parameter_name
                    + ".remaining())");
          } else {
            writer.print(auto_parameter_name + ".remaining()");
          }
          // Shift the remaining if the target parameter is multityped and there's no AutoType to
          // track type
          if (shift_remaining && shifting > 0) {
            writer.print(" << " + shifting);
            writer.print(")");
          }
        }
        writer.print(auto_size_annotation.expression());
      } else {
        throw new RuntimeException("Unknown auto annotation " + param_type);
      }
    } else {
      if (mode == Mode.BUFFEROBJECT && param.getAnnotation(BufferObject.class) != null) {
        writer.print(param.getSimpleName() + Utils.BUFFER_OBJECT_PARAMETER_POSTFIX);
      } else {
        Class type = typeinfos_instance.get(param).getType();
        Check check_annotation = param.getAnnotation(Check.class);
        boolean hide_buffer = mode == Mode.AUTOS && getAutoTypeParameter(method, param) != null;
        if (hide_buffer) {
          writer.print("0L");
        } else {
          if (type == CharSequence.class || type == CharSequence[].class) {
            final String offset = Utils.getStringOffset(method, param);

            writer.print("APIUtil.getBuffer");
            if (param.getAnnotation(NullTerminated.class) != null) {
              writer.print("NT");
            }
            writer.print('(');
            writer.print(type_map.getAPIUtilParam(true));
            writer.print(param.getSimpleName());
            if (offset != null) {
              writer.print(", " + offset);
            }
            writer.print(")");
          } else {
            final AutoSize auto_size_annotation = param.getAnnotation(AutoSize.class);
            if (auto_size_annotation != null) {
              writer.print(auto_size_annotation.value() + "_");
            }

            final Class buffer_type = Utils.getNIOBufferType(param.asType());
            if (buffer_type == null) {
              writer.print(param.getSimpleName());
            } else {
              writer.print("MemoryUtil.getAddress");
              if (check_annotation != null && check_annotation.canBeNull()) {
                writer.print("Safe");
              }
              writer.print("(");
              writer.print(param.getSimpleName());
              writer.print(")");
            }
          }
        }
        if (type != long.class) {
          PointerWrapper pointer_annotation = param.getAnnotation(PointerWrapper.class);
          if (pointer_annotation != null) {
            if (pointer_annotation.canBeNull()) {
              writer.print(" == null ? 0 : " + param.getSimpleName());
            }
            writer.print(".getPointer()");
          }
        }
      }
    }
    return false;
  }
 /**
  * Ensures space for an additional pointer value, sets the specified value at the allocated offset
  * and returns that offset.
  */
 public int pointerParam(PointerWrapper value) {
   return pointerParam(value.getPointer());
 }