/**
   * Returns the configuring forward method's options in case the given method is annotated with
   * {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be
   * selected (as per the source/target type and optionally the name given via
   * {@code @InheritConfiguration}). The method cannot be marked forward mapping itself (hence
   * 'ohter'). And neither can it contain an {@code @InheritReverseConfiguration}
   */
  private MappingOptions getTemplateMappingOptions(
      List<SourceMethod> rawMethods,
      SourceMethod method,
      List<SourceMethod> initializingMethods,
      MapperConfiguration mapperConfig) {
    SourceMethod resultMethod = null;
    InheritConfigurationPrism forwardPrism =
        InheritConfigurationPrism.getInstanceOn(method.getExecutable());

    if (forwardPrism != null) {
      reportErrorWhenInheritForwardAlsoHasInheritReverseMapping(method);

      List<SourceMethod> candidates = new ArrayList<SourceMethod>();
      for (SourceMethod oneMethod : rawMethods) {
        // method must be similar but not equal
        if (method.canInheritFrom(oneMethod) && !(oneMethod.equals(method))) {
          candidates.add(oneMethod);
        }
      }

      String name = forwardPrism.name();
      if (candidates.size() == 1) {
        // no ambiguity: if no configuredBy is specified, or configuredBy specified and match
        SourceMethod sourceMethod = first(candidates);
        if (name.isEmpty()) {
          resultMethod = sourceMethod;
        } else if (sourceMethod.getName().equals(name)) {
          resultMethod = sourceMethod;
        } else {
          reportErrorWhenNonMatchingName(sourceMethod, method, forwardPrism);
        }
      } else if (candidates.size() > 1) {
        // ambiguity: find a matching method that matches configuredBy

        List<SourceMethod> nameFilteredcandidates = new ArrayList<SourceMethod>();
        for (SourceMethod candidate : candidates) {
          if (candidate.getName().equals(name)) {
            nameFilteredcandidates.add(candidate);
          }
        }

        if (nameFilteredcandidates.size() == 1) {
          resultMethod = first(nameFilteredcandidates);
        } else if (nameFilteredcandidates.size() > 1) {
          reportErrorWhenSeveralNamesMatch(nameFilteredcandidates, method, forwardPrism);
        } else {
          reportErrorWhenAmbigousMapping(candidates, method, forwardPrism);
        }
      }
    }

    return extractInitializedOptions(resultMethod, rawMethods, mapperConfig, initializingMethods);
  }
  private void reportErrorWhenNonMatchingName(
      SourceMethod onlyCandidate, SourceMethod method, InheritConfigurationPrism prims) {

    messager.printMessage(
        method.getExecutable(),
        prims.mirror,
        Message.INHERITCONFIGURATION_NO_NAME_MATCH,
        prims.name(),
        onlyCandidate.getName());
  }
  private void reportErrorWhenSeveralNamesMatch(
      List<SourceMethod> candidates, SourceMethod method, InheritConfigurationPrism prism) {

    messager.printMessage(
        method.getExecutable(),
        prism.mirror,
        Message.INHERITCONFIGURATION_DUPLICATE_MATCHES,
        prism.name(),
        Strings.join(candidates, ", "));
  }
  private void reportErrorWhenAmbigousMapping(
      List<SourceMethod> candidates, SourceMethod method, InheritConfigurationPrism prism) {

    List<String> candidateNames = new ArrayList<String>();
    for (SourceMethod candidate : candidates) {
      candidateNames.add(candidate.getName());
    }

    String name = prism.name();
    if (name.isEmpty()) {
      messager.printMessage(
          method.getExecutable(),
          prism.mirror,
          Message.INHERITCONFIGURATION_DUPLICATES,
          Strings.join(candidateNames, "(), "));
    } else {
      messager.printMessage(
          method.getExecutable(),
          prism.mirror,
          Message.INHERITCONFIGURATION_INVALIDNAME,
          Strings.join(candidateNames, "(), "),
          name);
    }
  }