/**
     *
     * Generates all of the data rows
     *
     * @param form
     * @param extraCols
     * @param sb
     * @return
     * @throws Exception
     */
    public static String generateColumnDataFromHtmlForm(List<Encounter> encounters, HtmlForm form, List<String> extraCols, StringBuffer sb, Locale locale,List<PatientIdentifierType> pitList) throws Exception {
        for (Encounter e: encounters){

            sb.append(DEFAULT_QUOTE).append(e.getEncounterId()).append(DEFAULT_QUOTE).append(DEFAULT_COLUMN_SEPARATOR);
            sb.append(DEFAULT_QUOTE).append(DATE_FORMATTER.format(e.getEncounterDatetime())).append(DEFAULT_QUOTE).append(DEFAULT_COLUMN_SEPARATOR);
            sb.append(DEFAULT_QUOTE).append(e.getLocation().getName()).append(DEFAULT_QUOTE).append(DEFAULT_COLUMN_SEPARATOR);
            sb.append(DEFAULT_QUOTE).append(EncounterCompatibility.getProvider(e).getGivenName()+ " " + EncounterCompatibility.getProvider(e).getFamilyName()).append(DEFAULT_QUOTE).append(DEFAULT_COLUMN_SEPARATOR);
            sb.append(DEFAULT_QUOTE).append((e.getPatient() != null ? e.getPatient().getPatientId() : EMPTY)).append(DEFAULT_QUOTE).append(DEFAULT_COLUMN_SEPARATOR);       
            int index = 1;
            for (PatientIdentifierType pit :  pitList){
                sb.append(DEFAULT_QUOTE).append(e.getPatient().getPatientIdentifier(pit)).append(DEFAULT_QUOTE);
                if (index < pitList.size())
                    sb.append(DEFAULT_COLUMN_SEPARATOR);
                index ++;
            }

            FormEntrySession session = new FormEntrySession(e.getPatient(), e, Mode.VIEW, form, null); // session doesn't get HttpSession
            session.getHtmlToDisplay();
            FormSubmissionController  fsa = session.getSubmissionController();
            List<FormSubmissionControllerAction> actions = fsa.getActions();
            for (FormSubmissionControllerAction fsca : actions){
                if (fsca instanceof ObsSubmissionElement){
                    ObsSubmissionElement ose = (ObsSubmissionElement) fsca;
                    sb = appendObsToRow(ose, sb, extraCols, locale);
                } else {
                    //TODO: add programs, orders, logic, etc...
                    // just make sure these are in the headers too...
                }
            }
            session = null;
            sb.append(DEFAULT_LINE_SEPARATOR);
        }
        return sb.toString();
    }
  public QueueItem saveQueueItem(QueueItem queueItem) {
    log.debug("saveQueueItem(). Entering");
    boolean isNew = false;
    Date newDate = queueItem.getEncounter().getEncounterDatetime();
    log.debug("Encounter date: " + newDate);
    Date originalDate = null;

    if (queueItem.getQueueItemId() == null) {
      isNew = true;
      log.debug("Got a  new queue item");
    }

    // Not new we update some of the Encounter info
    if (!isNew) {
      log.info("Updating previously queued encounter!");
      Encounter encounter = queueItem.getEncounter();
      Patient p = encounter.getPatient();
      originalDate = encounter.getEncounterDatetime();
      if (OpenmrsUtil.compare(originalDate, newDate) != 0) {

        // if the obs datetime is the same as the
        // original encounter datetime, fix it
        if (OpenmrsUtil.compare(queueItem.getDateCreated(), originalDate) == 0) {
          encounter.setEncounterDatetime(newDate);
        }
      }

      // if the Person in the encounter doesn't match the Patient in the ,
      // fix it
      if (!encounter.getPatient().getPersonId().equals(p.getPatientId())) {
        encounter.setPatient(p);
      }

      for (Obs obs : encounter.getAllObs(true)) {
        // if the date was changed
        if (OpenmrsUtil.compare(originalDate, newDate) != 0) {

          // if the obs datetime is the same as the
          // original encounter datetime, fix it
          if (OpenmrsUtil.compare(obs.getObsDatetime(), originalDate) == 0) {
            obs.setObsDatetime(newDate);
          }
        }

        // if the Person in the obs doesn't match the Patient in the
        // encounter, fix it
        if (!obs.getPerson().getPersonId().equals(p.getPatientId())) {
          obs.setPerson(p);
        }
      }
    }
    log.debug("Saving queu item.");
    dao.saveQueueItem(queueItem);
    return queueItem;
  }
  public void saveAndTransferFileComplexObs() {

    try {
      List<Encounter> encounters =
          Context.getEncounterService().getEncounters(null, null, null, null, null, null, true);
      Encounter lastEncounter = encounters.get(encounters.size() - 1);

      Person patient = lastEncounter.getPatient();
      ConceptComplex conceptComplex = Context.getConceptService().getConceptComplex(17);
      Location location = Context.getLocationService().getDefaultLocation();
      Obs obs = new Obs(patient, conceptComplex, new Date(), location);

      String mergedUrl = tempFile.getCanonicalPath();
      InputStream out1 = new FileInputStream(new File(mergedUrl));

      ComplexData complexData = new ComplexData(tempFile.getName(), out1);
      obs.setComplexData(complexData);
      obs.setEncounter(lastEncounter);

      Context.getObsService().saveObs(obs, null);
      tempFile.delete();

    } catch (Exception e) {
      log.error(e);
    }
  }
  @Test
  public void shouldCreateNewEncounter() throws Exception {
    executeDataSet("shouldCreateMatchingEncounter.xml");

    String encounterDateTimeString = "2011-05-01T12:10:06.000+0530";
    Date encounterDateTime = new SimpleDateFormat(dateTimeFormat).parse(encounterDateTimeString);

    String json =
        "{ \"patientUuid\" : \"a76e8d23-0c38-408c-b2a8-ea5540f01b51\", "
            + "\"visitTypeUuid\" : \"b45ca846-c79a-11e2-b0c0-8e397087571c\", "
            + "\"encounterDateTime\" : \""
            + encounterDateTimeString
            + "\", "
            + "\"encounterTypeUuid\": \"2b377dba-62c3-4e53-91ef-b51c68899890\" }";

    EncounterTransaction response =
        deserialize(
            handle(newPostRequest("/rest/emrapi/encounter", json)), EncounterTransaction.class);

    assertEquals("1e5d5d48-6b78-11e0-93c3-18a905e044dc", response.getVisitUuid());

    Visit visit = visitService.getVisitByUuid(response.getVisitUuid());
    assertEquals(1, visit.getEncounters().size());
    Encounter encounter = visit.getEncounters().iterator().next();

    assertEquals("a76e8d23-0c38-408c-b2a8-ea5540f01b51", encounter.getPatient().getUuid());
    assertEquals("2b377dba-62c3-4e53-91ef-b51c68899890", encounter.getEncounterType().getUuid());
    assertEquals(encounterDateTime, encounter.getEncounterDatetime());
  }
  public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
      throws Throwable {
    log.debug("Intercepted an Encounter Save");
    if (method.getName().equals("saveEncounter")) {
      User autheticatedUser = Context.getAuthenticatedUser();

      if (autheticatedUser != null) {
        Collection<Role> userRoles = autheticatedUser.getAllRoles();

        if (userRoles != null && userRoles.size() > 0) {
          String prop =
              Context.getAdministrationService()
                  .getGlobalProperty(OncologyPOCConstants.GP_INTERCEPTROLES);

          if (prop != null) {
            String[] interceptRoles = prop.split(",");
            boolean interceptThisOne = false;
            Role interceptedRole = null;

            for (String role : interceptRoles) {
              for (Role userRole : userRoles) {
                if (role.equalsIgnoreCase(userRole.getRole())) {
                  interceptThisOne = true;
                  interceptedRole = userRole;
                  break;
                }
              }
              if (interceptThisOne) break;
            }

            if (interceptThisOne) {
              Encounter encounter = (Encounter) returnValue;
              if (encounter != null
                  && encounter.getPatient() != null
                  && encounter.getForm() != null) {
                OncologyPOCService service =
                    (OncologyPOCService) Context.getService(OncologyPOCService.class);
                SubEncounter subEncounter = service.getSubEncounter(encounter.getEncounterId());

                if (interceptedRole.hasPrivilege(OncologyPOCConstants.PRIV_CLINICIAN)) {
                  log.debug("Deleting subEncounter");
                  service.deleteSubEncounter(subEncounter);
                } else {
                  log.debug("Saving subEncounter");
                  if (subEncounter == null) {
                    subEncounter = new SubEncounter();
                    subEncounter.setEncounterId(encounter.getEncounterId());
                  }
                  service.saveSubEncounter(subEncounter);
                }
              }
            }
          }
        }
      }
    }
  }
  @RequestMapping("/module/hirifxray/deleteXray.form")
  public String deleteXray(
      ModelMap model, @RequestParam(value = "xrayId", required = true) Encounter encounter)
      throws Exception {

    Integer pId = encounter.getPatient().getPatientId();
    Context.getEncounterService().voidEncounter(encounter, "Deleting xray");

    return "redirect:/module/hirifxray/participant.form?id=" + pId;
  }
  public EncounterListItem(Encounter encounter) {

    if (encounter != null) {
      encounterId = encounter.getEncounterId();
      encounterDateTime = encounter.getEncounterDatetime();
      encounterDateString = Format.format(encounter.getEncounterDatetime());
      PersonName pn = encounter.getPatient().getPersonName();
      if (pn != null) {
        personName = "";
        if (pn.getGivenName() != null) {
          personName += pn.getGivenName();
        }
        if (pn.getMiddleName() != null) {
          personName += " " + pn.getMiddleName();
        }
        if (pn.getFamilyName() != null) {
          personName += " " + pn.getFamilyName();
        }
      }
      if (encounter.getProvider() != null) {
        providerName = encounter.getProvider().getPersonName().getFullName();
      }
      if (encounter.getLocation() != null) {
        location = encounter.getLocation().getName();
      }
      if (encounter.getEncounterType() != null) {
        encounterType = encounter.getEncounterType().getName();
      }
      if (encounter.getForm() != null) {
        formName = encounter.getForm().getName();
        formId = encounter.getForm().getFormId();
      }
      voided = encounter.isVoided();
      if (encounter.getCreator() != null) {
        PersonName entererPersonName = encounter.getCreator().getPersonName();
        if (entererPersonName != null) {
          entererName = "";
          if (entererPersonName.getGivenName() != null) {
            entererName += entererPersonName.getGivenName();
          }
          if (entererPersonName.getMiddleName() != null) {
            entererName += " " + entererPersonName.getMiddleName();
          }
          if (entererPersonName.getFamilyName() != null) {
            entererName += " " + entererPersonName.getFamilyName();
          }
        }
      }
    }
  }
  /**
   * Validates the given Encounter. Currently checks if the patient has been set and also ensures
   * that the patient for an encounter and the visit it is associated to if any, are the same.
   *
   * @param obj The encounter to validate.
   * @param errors Errors
   * @see org.springframework.validation.Validator#validate(java.lang.Object,
   *     org.springframework.validation.Errors)
   * @should fail if the patients for the visit and the encounter dont match
   * @should fail if patient is not set
   * @should fail if encounter dateTime is after current dateTime
   * @should fail if encounter dateTime is before visit startDateTime
   * @should fail if encounter dateTime is after visit stopDateTime
   */
  public void validate(Object obj, Errors errors) throws APIException {
    if (log.isDebugEnabled()) {
      log.debug(this.getClass().getName() + ".validate...");
    }

    if (obj == null || !(obj instanceof Encounter)) {
      throw new IllegalArgumentException(
          "The parameter obj should not be null and must be of type " + Encounter.class);
    }

    Encounter encounter = (Encounter) obj;

    ValidationUtils.rejectIfEmpty(
        errors, "patient", "Encounter.error.patient.required", "Patient is required");
    if (encounter.getVisit() != null
        && !ObjectUtils.equals(encounter.getVisit().getPatient(), encounter.getPatient())) {
      errors.rejectValue(
          "visit",
          "Encounter.visit.patients.dontMatch",
          "The patient for the encounter and visit should be the same");
    }

    Date encounterDateTime = encounter.getEncounterDatetime();

    if (encounterDateTime != null && encounterDateTime.after(new Date())) {
      errors.rejectValue(
          "encounterDatetime",
          "Encounter.datetimeShouldBeBeforeCurrent",
          "The encounter datetime should be before the current date.");
    }

    Visit visit = encounter.getVisit();
    if (visit != null && encounterDateTime != null) {
      if (visit.getStartDatetime() != null && encounterDateTime.before(visit.getStartDatetime())) {
        errors.rejectValue(
            "encounterDatetime",
            "Encounter.datetimeShouldBeInVisitDatesRange",
            "The encounter datetime should be between the visit start and stop dates.");
      }

      if (visit.getStopDatetime() != null && encounterDateTime.after(visit.getStopDatetime())) {
        errors.rejectValue(
            "encounterDatetime",
            "Encounter.datetimeShouldBeInVisitDatesRange",
            "The encounter datetime should be between the visit start and stop dates.");
      }
    }
  }
  /**
   * Adds the column headers and column data to the DataSet
   *
   * @param dataSet
   * @param encounters
   * @param patientIdentifierTypes
   * @param optionalColumns
   * @param columnDisplayFormat
   * @param maxColumnHeaderWidth
   * @param allColumns
   * @param fieldMap
   * @return
   */
  public DataSet addData(
      SimpleDataSet dataSet,
      List<Encounter> encounters,
      List<PatientIdentifierType> patientIdentifierTypes,
      List<EncounterAndObsDataSetDefinition.ObsOptionalColumn> optionalColumns,
      List<EncounterAndObsDataSetDefinition.ColumnDisplayFormat> columnDisplayFormat,
      Integer maxColumnHeaderWidth,
      Set<ObsColumnDescriptor> allColumns,
      Map<Encounter, Map<ObsColumnDescriptor, Obs>> fieldMap) {
    for (Encounter encounter : encounters) {

      DataSetRow row = new DataSetRow();

      List<String> providerNames = new ArrayList<String>();
      for (EncounterProvider ep : encounter.getEncounterProviders()) {
        providerNames.add(ep.getProvider().getName());
      }

      // Add the standard columns for encounters
      DataSetColumn c1 =
          new DataSetColumn(
              ObjectUtil.trimStringIfNeeded("ENCOUNTER_ID", maxColumnHeaderWidth),
              ObjectUtil.trimStringIfNeeded("ENCOUNTER_ID", maxColumnHeaderWidth),
              Integer.class);
      row.addColumnValue(c1, encounter.getEncounterId());
      DataSetColumn c2 =
          new DataSetColumn(
              ObjectUtil.trimStringIfNeeded("ENCOUNTER_DATETIME", maxColumnHeaderWidth),
              ObjectUtil.trimStringIfNeeded("ENCOUNTER_DATETIME", maxColumnHeaderWidth),
              String.class);
      row.addColumnValue(c2, encounter.getEncounterDatetime().toString());
      DataSetColumn c3 =
          new DataSetColumn(
              ObjectUtil.trimStringIfNeeded("LOCATION", maxColumnHeaderWidth),
              ObjectUtil.trimStringIfNeeded("LOCATION", maxColumnHeaderWidth),
              String.class);
      row.addColumnValue(
          c3, (encounter.getLocation() != null) ? encounter.getLocation().getName() : EMPTY);
      DataSetColumn c4 =
          new DataSetColumn(
              ObjectUtil.trimStringIfNeeded("PROVIDER", maxColumnHeaderWidth),
              ObjectUtil.trimStringIfNeeded("PROVIDER", maxColumnHeaderWidth),
              String.class);
      row.addColumnValue(c4, OpenmrsUtil.join(providerNames, ", "));
      DataSetColumn c5 =
          new DataSetColumn(
              ObjectUtil.trimStringIfNeeded("INTERNAL_PATIENT_ID", maxColumnHeaderWidth),
              ObjectUtil.trimStringIfNeeded("INTERNAL_PATIENT_ID", maxColumnHeaderWidth),
              Integer.class);
      row.addColumnValue(
          c5, encounter.getPatient() != null ? encounter.getPatient().getPatientId() : EMPTY);

      if (patientIdentifierTypes != null) {
        for (PatientIdentifierType pit : patientIdentifierTypes) {
          List<PatientIdentifier> patientIdentifiers =
              encounter.getPatient().getPatientIdentifiers(pit);

          StringBuffer sbPatientIdentifiers = new StringBuffer();
          int count = 0;
          for (PatientIdentifier patientIdentifier : patientIdentifiers) {
            if (count > 0) {
              sbPatientIdentifiers.append(", ");
            }
            sbPatientIdentifiers.append(patientIdentifier.toString());
            count++;
          }

          DataSetColumn c6 =
              new DataSetColumn(
                  pit.getName(),
                  ObjectUtil.trimStringIfNeeded(pit.getName(), maxColumnHeaderWidth),
                  String.class);
          row.addColumnValue(c6, sbPatientIdentifiers.toString());
        }
      }

      Map<ObsColumnDescriptor, Obs> obsInEncounter = fieldMap.get(encounter);

      // Look up all obs for a given encounter based on column headers for all encounters
      for (ObsColumnDescriptor columnKey : allColumns) {

        Obs obs = obsInEncounter.get(columnKey);
        String columnName = columnKey.format(columnDisplayFormat, maxColumnHeaderWidth);
        DataSetColumn obsDsc = new DataSetColumn(columnName, columnName, String.class);

        StringBuffer columnValue = new StringBuffer();
        if (obs != null && obs.getValueCoded() != null) {
          if (columnDisplayFormat.contains(
              EncounterAndObsDataSetDefinition.ColumnDisplayFormat.ID)) {
            columnValue.append(obs.getValueCoded());
          }

          if (columnDisplayFormat.contains(EncounterAndObsDataSetDefinition.ColumnDisplayFormat.ID)
              && columnDisplayFormat.contains(
                  EncounterAndObsDataSetDefinition.ColumnDisplayFormat.BEST_SHORT_NAME)) {
            columnValue.append("_");
          }

          if (columnDisplayFormat.contains(
              EncounterAndObsDataSetDefinition.ColumnDisplayFormat.BEST_SHORT_NAME)) {
            String conceptName = obs.getValueAsString(Context.getLocale());
            columnValue.append(
                maxColumnHeaderWidth != null
                        && conceptName.length() > maxColumnHeaderWidth - columnValue.length()
                    ? conceptName.substring(0, maxColumnHeaderWidth - columnValue.length() - 1)
                    : conceptName);
          }
          row.addColumnValue(obsDsc, (obs != null) ? columnValue.toString() : EMPTY);
        } else {
          row.addColumnValue(
              obsDsc, (obs != null) ? obs.getValueAsString(Context.getLocale()) : EMPTY);
        }

        String dateColumnName =
            columnKey.format(
                columnDisplayFormat,
                maxColumnHeaderWidth != null ? maxColumnHeaderWidth - 5 : null);
        DataSetColumn obsDscDate =
            new DataSetColumn(dateColumnName + "_DATE", dateColumnName + "_DATE", String.class);
        row.addColumnValue(obsDscDate, (obs != null) ? obs.getObsDatetime().toString() : EMPTY);

        String parentColumnName =
            columnKey.format(
                columnDisplayFormat,
                maxColumnHeaderWidth != null ? maxColumnHeaderWidth - 7 : null);
        DataSetColumn obsDscParent =
            new DataSetColumn(
                parentColumnName + "_PARENT", parentColumnName + "_PARENT", String.class);
        row.addColumnValue(
            obsDscParent,
            (obs != null && obs.getObsGroup() != null) ? obs.getObsGroup().getId() : EMPTY);

        if (optionalColumns != null) {

          if (optionalColumns.contains(
              EncounterAndObsDataSetDefinition.ObsOptionalColumn.VALUE_MODIFIER)) {
            String valModColumnName =
                columnKey.format(
                    columnDisplayFormat,
                    maxColumnHeaderWidth != null ? maxColumnHeaderWidth - 10 : null);
            DataSetColumn obsDscValueModifier =
                new DataSetColumn(
                    valModColumnName + "_VALUE_MOD", valModColumnName + "_VALUE_MOD", String.class);
            row.addColumnValue(obsDscValueModifier, (obs != null) ? obs.getValueModifier() : EMPTY);
          }
          if (optionalColumns.contains(
              EncounterAndObsDataSetDefinition.ObsOptionalColumn.ACCESSION_NUMBER)) {
            String accessionNumColumnName =
                columnKey.format(
                    columnDisplayFormat,
                    maxColumnHeaderWidth != null ? maxColumnHeaderWidth - 14 : null);
            DataSetColumn obsDscAccessionNumber =
                new DataSetColumn(
                    accessionNumColumnName + "_ACCESSION_NUM",
                    accessionNumColumnName + "_ACCESSION_NUM",
                    String.class);
            row.addColumnValue(
                obsDscAccessionNumber, (obs != null) ? obs.getAccessionNumber() : EMPTY);
          }
          if (optionalColumns.contains(
              EncounterAndObsDataSetDefinition.ObsOptionalColumn.COMMENT)) {
            String commentColumnName =
                columnKey.format(
                    columnDisplayFormat,
                    maxColumnHeaderWidth != null ? maxColumnHeaderWidth - 8 : null);
            DataSetColumn obsDscComment =
                new DataSetColumn(
                    commentColumnName + "_COMMENT", commentColumnName + "_COMMENT", String.class);
            row.addColumnValue(obsDscComment, (obs != null) ? obs.getComment() : EMPTY);
          }
        }
      }

      dataSet.addRow(row);
    }
    return dataSet;
  }
    /**
     *
     * Returns the encounter with the obs that aren't used when populating form are removed.
     * This *doesn't* save the encounter.
     * TODO: handle Orders?
     *
     * @param e
     * @param htmlform
     * @return
     * @throws Exception
     */
    public static Encounter trimEncounterToMatchForm(Encounter e, HtmlForm htmlform) throws Exception {

       //this should move existing obs from session to tag handlers.
        FormEntrySession session = new FormEntrySession(e.getPatient(), e, FormEntryContext.Mode.VIEW, htmlform, null); // session gets a null HttpSession
        session.getHtmlToDisplay();

        if (log.isDebugEnabled()){
            Map<Concept, List<Obs>>  map = session.getContext().getExistingObs();
            if (map != null){
                for (Map.Entry<Concept, List<Obs>> existingObs : map.entrySet()){
                    List<Obs> oList = existingObs.getValue();
                    for (Obs oInner : oList)
                        log.debug("Obs in existingObs " + existingObs.getKey() + " " + oInner.getConcept());
                }
            }
            Map<Obs, Set<Obs>> map2 = session.getContext().getExistingObsInGroups();
            if (map2 != null){
                for (Map.Entry<Obs, Set<Obs>> existingObsInGroups : map2.entrySet()){
                    Set<Obs> oList = existingObsInGroups.getValue();
                    for (Obs oInner : oList)
                        log.debug("Obs in existingObsInGroups " + existingObsInGroups.getKey().getConcept() + " " + oInner.getConcept());
                }
            }
        }
        Encounter ret = new Encounter();
        ret.setCreator(e.getCreator());
        ret.setEncounterDatetime(e.getEncounterDatetime());
        EncounterCompatibility.setProvider(ret, EncounterCompatibility.getProvider(e));
        ret.setLocation(e.getLocation());
        ret.setDateCreated(e.getDateCreated());
        ret.setPatient(e.getPatient());
        //renders new encounter unsave-able:
        ret.setEncounterId(e.getEncounterId());

        for (Obs oTest : e.getAllObs()){
            boolean found = false;
            if (session.getContext().getExistingObs() != null && !oTest.isObsGrouping()){
                List<Obs> obsList = session.getContext().getExistingObs().get(oTest.getConcept());
                if (obsList != null && obsList.size() > 0){
                    for (Obs o : obsList){
                        if (o.getObsId().equals(oTest.getObsId())){
                            found = true;
                            continue;
                        }
                    }
                }
            }
            if (!found && session.getContext().getExistingObsInGroups() != null){
                for (Map.Entry<Obs, Set<Obs>> mapEntry : session.getContext().getExistingObsInGroups().entrySet()){
                    if (mapEntry.getKey().equals(oTest)){
                        found = true;
                        continue;
                    } else {
                        Set<Obs> oSet = mapEntry.getValue();
                        //note: oSet.contains fails for some reason
                        for (Obs o:oSet){
                              if (o.getObsId().equals(oTest.getObsId())){
                                  found = true;
                                  continue;
                              }
                        }
                    }
                }
            }
            if (!found)
                ret.addObs(oTest);
        }
        session = null;
        return ret;
    }
  @Override
  protected FormEntrySession formBackingObject(HttpServletRequest request) throws Exception {
    long ts = System.currentTimeMillis();

    Integer encounterId = null;
    Encounter encounter = null;
    if (request.getParameter("encounterId") != null
        && !"".equals(request.getParameter("encounterId"))) {
      encounterId = Integer.valueOf(request.getParameter("encounterId"));
      encounter = Context.getEncounterService().getEncounter(encounterId);
    }

    Integer personId = null;
    Patient patient = null;
    if (encounter != null) {
      patient = encounter.getPatient();
      personId = patient.getPersonId();
    } else {
      personId = Integer.valueOf(request.getParameter("personId"));
      patient = Context.getPatientService().getPatient(personId);
      if (patient == null) throw new IllegalArgumentException("No patient with id " + personId);
    }

    HtmlForm htmlForm = null;

    if (encounter != null) {
      htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(encounter.getForm());
      if (htmlForm == null) {
        throw new IllegalArgumentException(
            "The form for the specified encounter ("
                + encounter.getForm()
                + ") does not have an HtmlForm associated with it");
      }
    } else {
      String htmlFormIdParam = request.getParameter("htmlFormId");
      if (StringUtils.hasText(htmlFormIdParam)) {
        htmlForm = HtmlFormEntryUtil.getService().getHtmlForm(Integer.valueOf(htmlFormIdParam));
      }
      String formIdParam = request.getParameter("formId");
      if (StringUtils.hasText(formIdParam)) {
        Form form = Context.getFormService().getForm(Integer.parseInt(formIdParam));
        htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(form);
      }
      if (htmlForm == null) {
        throw new IllegalArgumentException("You must specify either an htmlFormId or a formId");
      }
    }

    FormEntrySession session;
    if (encounter != null) {
      Mode mode = Mode.VIEW;
      if ("EDIT".equals(request.getParameter("mode"))) {
        mode = Mode.EDIT;
      }
      session = new FormEntrySession(patient, encounter, mode, htmlForm);
    } else {
      session = new FormEntrySession(patient, htmlForm);
    }

    String returnUrl = request.getParameter("returnUrl");
    if (StringUtils.hasText(returnUrl)) {
      session.setReturnUrl(returnUrl);
    }

    // In case we're not using a sessionForm, we need to check for the case where the underlying
    // form was modified while a user was filling a form out
    if (StringUtils.hasText(request.getParameter("formModifiedTimestamp"))) {
      long submittedTimestamp = Long.valueOf(request.getParameter("formModifiedTimestamp"));
      if (submittedTimestamp != session.getFormModifiedTimestamp()) {
        throw new RuntimeException(
            Context.getMessageSourceService()
                .getMessage("htmlformentry.error.formModifiedBeforeSubmission"));
      }
    }

    // In case we're not using a sessionForm, we need to make sure this encounter hasn't been
    // modified since the user opened it
    if (encounter != null) {
      try {
        long submittedTimestamp = Long.valueOf(request.getParameter("encounterModifiedTimestamp"));
        if (submittedTimestamp != session.getEncounterModifiedTimestamp()) {
          throw new RuntimeException(
              Context.getMessageSourceService()
                  .getMessage("htmlformentry.error.encounterModifiedBeforeSubmission"));
        }
      } catch (NumberFormatException ex) {
        // this is being opened for the first time, no worries
      }
    }

    Context.setVolatileUserData(FORM_IN_PROGRESS_KEY, session);

    log.warn("Took " + (System.currentTimeMillis() - ts) + " ms");
    return session;
  }
  /** @see org.openmrs.api.EncounterService#saveEncounter(org.openmrs.Encounter) */
  public Encounter saveEncounter(Encounter encounter) throws APIException {

    // if authenticated user is not supposed to edit encounter of certain type
    if (!canEditEncounter(encounter, null)) {
      throw new APIException(
          "Encounter.error.privilege.required.edit",
          new Object[] {encounter.getEncounterType().getEditPrivilege()});
    }

    // If new encounter, try to assign a visit using the registered visit assignment handler.
    if (encounter.getEncounterId() == null) {

      // Am using Context.getEncounterService().getActiveEncounterVisitHandler() instead of just
      // getActiveEncounterVisitHandler() for modules which may want to AOP around this call.
      EncounterVisitHandler encounterVisitHandler =
          Context.getEncounterService().getActiveEncounterVisitHandler();
      if (encounterVisitHandler != null) {
        encounterVisitHandler.beforeCreateEncounter(encounter);

        // If we have been assigned a new visit, persist it.
        if (encounter.getVisit() != null && encounter.getVisit().getVisitId() == null) {
          Context.getVisitService().saveVisit(encounter.getVisit());
        }
      }
    }

    boolean isNewEncounter = false;
    Date newDate = encounter.getEncounterDatetime();
    Date originalDate = null;
    Location newLocation = encounter.getLocation();
    Location originalLocation = null;
    // check permissions
    if (encounter.getEncounterId() == null) {
      isNewEncounter = true;
      Context.requirePrivilege(PrivilegeConstants.ADD_ENCOUNTERS);
    } else {
      Context.requirePrivilege(PrivilegeConstants.EDIT_ENCOUNTERS);
    }

    // This must be done after setting dateCreated etc on the obs because
    // of the way the ORM tools flush things and check for nullity
    // This also must be done before the save encounter so we can use the
    // orig date
    // after the save
    Patient p = encounter.getPatient();

    if (!isNewEncounter) {
      // fetch the datetime from the database prior to saving for this
      // encounter
      // to see if it has changed and change all obs after saving if so
      originalDate = dao.getSavedEncounterDatetime(encounter);
      if (encounter.getLocation() != null) {
        originalLocation = dao.getSavedEncounterLocation(encounter);
      }
      // Our data model duplicates the patient column to allow for
      // observations to
      // not have to look up the parent Encounter to find the patient
      // Therefore, encounter.patient must always equal
      // encounter.observations[0-n].patient

      // If we are changing encounter.encounterDatetime, then we need to
      // also apply that
      // to Obs that inherited their obsDatetime from the encounter in the
      // first place

      for (Obs obs : encounter.getAllObs(true)) {
        // if the date was changed
        if (OpenmrsUtil.compare(originalDate, newDate) != 0
            && OpenmrsUtil.compare(obs.getObsDatetime(), originalDate) == 0) {

          // if the obs datetime is the same as the
          // original encounter datetime, fix it
          obs.setObsDatetime(newDate);
        }

        if (!OpenmrsUtil.nullSafeEquals(newLocation, originalLocation)
            && obs.getLocation().equals(originalLocation)) {
          obs.setLocation(newLocation);
        }

        // if the Person in the obs doesn't match the Patient in the
        // encounter, fix it
        if (!obs.getPerson().getPersonId().equals(p.getPatientId())) {
          obs.setPerson(p);
        }
      }
    }
    // same goes for Orders
    for (Order o : encounter.getOrders()) {
      if (!p.equals(o.getPatient())) {
        o.setPatient(p);
      }
    }

    // do the actual saving to the database
    dao.saveEncounter(encounter);

    // save the new orders
    for (Order o : encounter.getOrders()) {
      if (o.getOrderId() == null) {
        Context.getOrderService().saveOrder(o, null);
      }
    }
    return encounter;
  }
  @ModelAttribute("command")
  public FormEntrySession getFormEntrySession(
      HttpServletRequest request,
      // @RequestParam doesn't pick up query parameters (in the url) in a POST, so I'm handling
      // encounterId, modeParam, and which specially
      /*@RequestParam(value="mode", required=false) String modeParam,*/
      /*@RequestParam(value="encounterId", required=false) Integer encounterId,*/
      /*@RequestParam(value="which", required=false) String which,*/
      @RequestParam(value = "patientId", required = false) Integer patientId,
      /*@RequestParam(value="personId", required=false) Integer personId,*/
      @RequestParam(value = "formId", required = false) Integer formId,
      @RequestParam(value = "htmlformId", required = false) Integer htmlFormId,
      @RequestParam(value = "returnUrl", required = false) String returnUrl,
      @RequestParam(value = "formModifiedTimestamp", required = false) Long formModifiedTimestamp,
      @RequestParam(value = "encounterModifiedTimestamp", required = false)
          Long encounterModifiedTimestamp)
      throws Exception {

    long ts = System.currentTimeMillis();

    Mode mode = Mode.VIEW;

    Integer personId = null;

    if (StringUtils.hasText(request.getParameter("personId"))) {
      personId = Integer.valueOf(request.getParameter("personId"));
    }

    String modeParam = request.getParameter("mode");
    if ("enter".equalsIgnoreCase(modeParam)) {
      mode = Mode.ENTER;
    } else if ("edit".equalsIgnoreCase(modeParam)) {
      mode = Mode.EDIT;
    }

    Patient patient = null;
    Encounter encounter = null;
    Form form = null;
    HtmlForm htmlForm = null;

    if (StringUtils.hasText(request.getParameter("encounterId"))) {

      Integer encounterId = Integer.valueOf(request.getParameter("encounterId"));
      encounter = Context.getEncounterService().getEncounter(encounterId);
      if (encounter == null)
        throw new IllegalArgumentException("No encounter with id=" + encounterId);
      patient = encounter.getPatient();
      patientId = patient.getPatientId();
      personId = patient.getPersonId();

      if (formId
          != null) { // I think formId is allowed to differ from encounter.form.id because of
                     // HtmlFormFlowsheet
        form = Context.getFormService().getForm(formId);
        htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(form);
        if (htmlForm == null)
          throw new IllegalArgumentException("No HtmlForm associated with formId " + formId);
      } else {
        form = encounter.getForm();
        htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(encounter.getForm());
        if (htmlForm == null)
          throw new IllegalArgumentException(
              "The form for the specified encounter ("
                  + encounter.getForm()
                  + ") does not have an HtmlForm associated with it");
      }

    } else { // no encounter specified

      // get person from patientId/personId (register module uses patientId, htmlformentry uses
      // personId)
      if (patientId != null) {
        personId = patientId;
      }
      if (personId != null) {
        patient = Context.getPatientService().getPatient(personId);
      }

      // determine form
      if (htmlFormId != null) {
        htmlForm = HtmlFormEntryUtil.getService().getHtmlForm(htmlFormId);
      } else if (formId != null) {
        form = Context.getFormService().getForm(formId);
        htmlForm = HtmlFormEntryUtil.getService().getHtmlFormByForm(form);
      }
      if (htmlForm == null) {
        throw new IllegalArgumentException(
            "You must specify either an htmlFormId or a formId for a valid html form");
      }

      String which = request.getParameter("which");
      if (StringUtils.hasText(which)) {
        if (patient == null)
          throw new IllegalArgumentException(
              "Cannot specify 'which' without specifying a person/patient");
        List<Encounter> encs =
            Context.getEncounterService()
                .getEncounters(
                    patient, null, null, null, Collections.singleton(form), null, null, false);
        if (which.equals("first")) {
          encounter = encs.get(0);
        } else if (which.equals("last")) {
          encounter = encs.get(encs.size() - 1);
        } else {
          throw new IllegalArgumentException("which must be 'first' or 'last'");
        }
      }
    }

    if (mode != Mode.ENTER && patient == null)
      throw new IllegalArgumentException(
          "No patient with id of personId=" + personId + " or patientId=" + patientId);

    FormEntrySession session = null;
    if (mode == Mode.ENTER && patient == null) {
      patient = new Patient();
    }
    if (encounter != null) {
      session = new FormEntrySession(patient, encounter, mode, htmlForm);
    } else {
      session = new FormEntrySession(patient, htmlForm);
    }

    if (StringUtils.hasText(returnUrl)) {
      session.setReturnUrl(returnUrl);
    }

    // Since we're not using a sessionForm, we need to check for the case where the underlying form
    // was modified while a user was filling a form out
    if (formModifiedTimestamp != null) {
      if (!OpenmrsUtil.nullSafeEquals(formModifiedTimestamp, session.getFormModifiedTimestamp())) {
        throw new RuntimeException(
            Context.getMessageSourceService()
                .getMessage("htmlformentry.error.formModifiedBeforeSubmission"));
      }
    }

    // Since we're not using a sessionForm, we need to make sure this encounter hasn't been modified
    // since the user opened it
    if (encounter != null) {
      if (encounterModifiedTimestamp != null
          && !OpenmrsUtil.nullSafeEquals(
              encounterModifiedTimestamp, session.getEncounterModifiedTimestamp())) {
        throw new RuntimeException(
            Context.getMessageSourceService()
                .getMessage("htmlformentry.error.encounterModifiedBeforeSubmission"));
      }
    }

    Context.setVolatileUserData(FORM_IN_PROGRESS_KEY, session);

    log.info("Took " + (System.currentTimeMillis() - ts) + " ms");

    return session;
  }
  /**
   * @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;
  }
  /**
   * The onSubmit function receives the form/command object that was modified by the input form and
   * saves it to the db
   *
   * @see
   *     org.springframework.web.servlet.mvc.SimpleFormController#onSubmit(javax.servlet.http.HttpServletRequest,
   *     javax.servlet.http.HttpServletResponse, java.lang.Object,
   *     org.springframework.validation.BindException)
   * @should transfer encounter to another patient when encounter patient was changed
   */
  @Override
  protected ModelAndView onSubmit(
      HttpServletRequest request, HttpServletResponse response, Object obj, BindException errors)
      throws Exception {

    HttpSession httpSession = request.getSession();

    String view = getFormView();

    try {
      Context.addProxyPrivilege(PrivilegeConstants.VIEW_USERS);
      Context.addProxyPrivilege(PrivilegeConstants.VIEW_PATIENTS);

      if (Context.isAuthenticated()) {
        Encounter encounter = (Encounter) obj;

        Integer patientId = null;

        if (request.getParameter("patientId") != null) {
          patientId = Integer.valueOf(request.getParameter("patientId"));
        }

        if (encounter.getEncounterId() == null) {
          // if this is a new encounter, they can specify a patient.  add it
          if (patientId != null) {
            encounter.setPatient(Context.getPatientService().getPatient(patientId));
          }
        } else {
          Patient oldPatient = encounter.getPatient();
          Patient newPatient = Context.getPatientService().getPatient(patientId);
          if (newPatient != null && oldPatient != null && !newPatient.equals(oldPatient)) {
            encounter = Context.getEncounterService().transferEncounter(encounter, newPatient);
          }
        }

        if (encounter.isVoided() && encounter.getVoidedBy() == null) {
          // if this is a "new" voiding, call voidEncounter to set appropriate attributes
          Context.getEncounterService().voidEncounter(encounter, encounter.getVoidReason());
        } else if (!encounter.isVoided() && encounter.getVoidedBy() != null) {
          // if this was just unvoided, call unvoidEncounter to unset appropriate attributes
          Context.getEncounterService().unvoidEncounter(encounter);
        } else {
          Context.getEncounterService().saveEncounter(encounter);
        }

        view = getSuccessView();
        view = view + "?encounterId=" + encounter.getEncounterId();

        httpSession.setAttribute(WebConstants.OPENMRS_MSG_ATTR, "Encounter.saved");
      }
    } catch (APIException e) {
      log.error("Error while trying to save the encounter", e);
      httpSession.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "Encounter.cannot.save");
      errors.reject("encounter", "Encounter.cannot.save");
      // return to the form because an exception was thrown
      return showForm(request, response, errors);
    } finally {
      Context.removeProxyPrivilege(PrivilegeConstants.VIEW_USERS);
      Context.removeProxyPrivilege(PrivilegeConstants.VIEW_PATIENTS);
    }

    return new ModelAndView(new RedirectView(view));
  }