/**
   * @see
   *     org.springframework.web.servlet.mvc.SimpleFormController#referenceData(javax.servlet.http.HttpServletRequest,
   *     java.lang.Object, org.springframework.validation.Errors)
   */
  @Override
  protected Map<String, Object> referenceData(HttpServletRequest request, Object obj, Errors error)
      throws Exception {

    Encounter encounter = (Encounter) obj;

    // the generic returned key-value pair mapping
    Map<String, Object> map = new HashMap<String, Object>();

    // obsIds of obs that were edited
    List<Integer> editedObs = new Vector<Integer>();

    // the map returned to the form
    // This is a mapping between the formfield and a list of the Obs/ObsGroup in that field
    // This mapping is sorted according to the comparator in FormField.java
    SortedMap<FormField, List<Obs>> obsMapToReturn = null;
    String sortType =
        Context.getAdministrationService()
            .getGlobalProperty(OpenmrsConstants.GLOBAL_PROPERTY_ENCOUNTER_FORM_OBS_SORT_ORDER);
    if ("weight".equals(sortType)) {
      obsMapToReturn = new TreeMap<FormField, List<Obs>>(); // use FormField.compareTo
    } else {
      obsMapToReturn =
          new TreeMap<FormField, List<Obs>>(
              new NumberingFormFieldComparator()); // use custom comparator
    }

    // this maps the obs to form field objects for non top-level obs
    // it is keyed on obs so that when looping over an exploded obsGroup
    // the formfield can be fetched easily (in order to show the field numbers etc)
    Map<Obs, FormField> otherFormFields = new HashMap<Obs, FormField>();

    if (Context.isAuthenticated()) {
      EncounterService es = Context.getEncounterService();
      FormService fs = Context.getFormService();

      // used to restrict the form field lookup
      Form form = encounter.getForm();

      List<EncounterType> encTypes = es.getAllEncounterTypes();
      // Non-retired types first
      Collections.sort(encTypes, new MetadataComparator(Context.getLocale()));
      map.put("encounterTypes", encTypes);

      map.put("encounterRoles", es.getAllEncounterRoles(false));
      map.put("forms", Context.getFormService().getAllForms());
      // loop over the encounter's observations to find the edited obs
      for (Obs o : encounter.getObsAtTopLevel(true)) {

        // only edited obs has previous version
        if (o.hasPreviousVersion()) {
          editedObs.add(o.getObsId());
        }

        // get the formfield for this obs
        FormField ff = fs.getFormField(form, o.getConcept(), obsMapToReturn.keySet(), false);
        if (ff == null) {
          ff = new FormField();
        }

        // we only put the top-level obs in the obsMap.  Those would
        // be the obs that don't have an obs grouper
        if (o.getObsGroup() == null) {
          // populate the obs map with this formfield and obs
          List<Obs> list = obsMapToReturn.get(ff);
          if (list == null) {
            list = new Vector<Obs>();
            obsMapToReturn.put(ff, list);
          }
          list.add(o);
        } else {
          // this is not a top-level obs, just put the formField
          // in a separate list and be done with it
          otherFormFields.put(o, ff);
        }
      }
    }

    if (log.isDebugEnabled()) {
      log.debug("setting obsMap in page context (size: " + obsMapToReturn.size() + ")");
    }
    map.put("obsMap", obsMapToReturn);

    map.put("otherFormFields", otherFormFields);

    map.put("locale", Context.getLocale());
    map.put("editedObs", editedObs);
    if (encounter.getPatient() != null) {
      map.put(
          "patientVisits", Context.getVisitService().getVisitsByPatient(encounter.getPatient()));
    }

    return map;
  }