/**
   * Processes requests to save/update a concept reference term
   *
   * @param request the {@link WebRequest} object
   * @param conceptReferenceTermModel the concept reference term object to save/update
   * @param result the {@link BindingResult} object
   * @return the url to redirect to
   */
  @RequestMapping(method = RequestMethod.POST, value = CONCEPT_REFERENCE_TERM_FORM_URL)
  public String saveConceptReferenceTerm(
      WebRequest request,
      @ModelAttribute(value = "conceptReferenceTermModel")
          ConceptReferenceTermModel conceptReferenceTermModel,
      BindingResult result) {

    ConceptReferenceTerm conceptReferenceTerm = conceptReferenceTermModel.getConceptReferenceTerm();
    // add all the term maps
    // store ids of already mapped terms so that we don't map a term multiple times
    Set<Integer> mappedTermIds = null;
    for (int x = 0; x < conceptReferenceTermModel.getTermMaps().size(); x++) {
      if (mappedTermIds == null) {
        mappedTermIds = new HashSet<Integer>();
      }
      ConceptReferenceTermMap map = conceptReferenceTermModel.getTermMaps().get(x);

      if (map != null && map.getTermB() != null) {
        // skip past this mapping because its term is already in use by another mapping for this
        // term
        if (!mappedTermIds.add(map.getTermB().getConceptReferenceTermId())) {
          continue;
        }

        if (request.getParameter("_termMaps[" + x + "].exists") == null) {
          // because of the _termMap[x].exists input name in the jsp, the value will be null for
          // deleted maps, remove the map.
          conceptReferenceTerm.removeConceptReferenceTermMap(map);
        } else {
          if (map.getConceptMapType() == null) {
            result.rejectValue(
                "termMaps[" + x + "]",
                "ConceptReferenceTerm.error.mapTypeRequired",
                "Concept Map Type is required");
            log.warn("Concept Map Type is required");
            break;
          } else if (map.getTermB().equals(conceptReferenceTerm)) {
            result.rejectValue(
                "termMaps[" + x + "]",
                "ConceptReferenceTerm.map.sameTerm",
                "Cannot map a concept reference term to itself");
            log.warn("Cannot map a concept reference term to itself");
            break;
          } else if (!conceptReferenceTerm.getConceptReferenceTermMaps().contains(map)) {
            conceptReferenceTerm.addConceptReferenceTermMap(map);
          }
        }
      }
    }

    // if there are errors  with the term
    if (!result.hasErrors()) {
      try {
        result.pushNestedPath("conceptReferenceTerm");
        ValidationUtils.invokeValidator(
            new ConceptReferenceTermValidator(), conceptReferenceTerm, result);
        ValidationUtils.invokeValidator(
            new ConceptReferenceTermWebValidator(), conceptReferenceTerm, result);
      } finally {
        result.popNestedPath();
      }
    }

    if (!result.hasErrors()) {
      try {
        conceptReferenceTerm =
            Context.getConceptService().saveConceptReferenceTerm(conceptReferenceTerm);
        if (log.isDebugEnabled()) {
          log.debug("Saved concept reference term: " + conceptReferenceTerm.toString());
        }
        request.setAttribute(
            WebConstants.OPENMRS_MSG_ATTR, "ConceptReferenceTerm.saved", WebRequest.SCOPE_SESSION);
        return "redirect:"
            + CONCEPT_REFERENCE_TERM_FORM_URL
            + ".form?conceptReferenceTermId="
            + conceptReferenceTerm.getConceptReferenceTermId();
      } catch (APIException e) {
        log.error("Error while saving concept reference term", e);
        request.setAttribute(
            WebConstants.OPENMRS_ERROR_ATTR,
            "ConceptReferenceTerm.save.error",
            WebRequest.SCOPE_SESSION);
      }
    }

    return CONCEPT_REFERENCE_TERM_FORM;
  }