/**
   * @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext)
   * @should evaluate an EncounterAndObsDataSetDefinition
   */
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    EncounterAndObsDataSetDefinition definition =
        (EncounterAndObsDataSetDefinition) dataSetDefinition;
    SimpleDataSet dataSet = new SimpleDataSet(dataSetDefinition, context);
    context = ObjectUtil.nvl(context, new EvaluationContext());
    Cohort cohort = context.getBaseCohort();
    if (context.getLimit() != null) {
      CohortUtil.limitCohort(cohort, context.getLimit());
    }

    // Retrieve the rows for the dataset

    BasicEncounterQuery encounterQuery = new BasicEncounterQuery();
    encounterQuery.setWhich(definition.getWhichEncounterQualifier());
    encounterQuery.setWhichNumber(definition.getQuantity());
    encounterQuery.setEncounterTypes(definition.getEncounterTypes());
    encounterQuery.setForms(definition.getForms());
    encounterQuery.setOnOrBefore(definition.getEncounterDatetimeOnOrBefore());
    encounterQuery.setOnOrAfter(definition.getEncounterDatetimeOnOrAfter());

    EncounterQueryResult encounterIdRows = encounterQueryService.evaluate(encounterQuery, context);

    HqlQueryBuilder q = new HqlQueryBuilder();
    EncounterEvaluationContext eec = new EncounterEvaluationContext();
    eec.setBaseEncounters(encounterIdRows);
    q.from(Encounter.class, "e").whereEncounterIn("e.encounterId", eec);
    List<Encounter> encounters = evaluationService.evaluateToList(q, Encounter.class);

    // Determine what columns to display in the dataset

    List<EncounterAndObsDataSetDefinition.ObsOptionalColumn> optionalColumns =
        definition.getOptionalColumns();
    List<PatientIdentifierType> patientIdentifierTypes = definition.getPatientIdentifierTypes();
    List<EncounterAndObsDataSetDefinition.ColumnDisplayFormat> columnDisplayFormat =
        definition.getColumnDisplayFormat();
    Integer maxColumnHeaderWidth = definition.getMaxColumnHeaderWidth();

    if (patientIdentifierTypes == null) {
      patientIdentifierTypes = new ArrayList<PatientIdentifierType>();
    }

    if (columnDisplayFormat == null) {
      columnDisplayFormat = new ArrayList<ColumnDisplayFormat>();
    }

    if (columnDisplayFormat.size() == 0) {
      columnDisplayFormat.add(EncounterAndObsDataSetDefinition.ColumnDisplayFormat.ID);
    }

    // section index should be added here

    // Store all encounters within a data structure that keeps track of obs, obs groups, and their
    // occurrences
    // in order that column headers are unique and children obs are grouped by their parent
    Map<Encounter, Map<ObsColumnDescriptor, Obs>> populatedFieldMap = populateFieldMap(encounters);

    // Keeps track of the column headers for all obs-related columns
    Set<ObsColumnDescriptor> allColumns = new TreeSet<ObsColumnDescriptor>();

    for (Encounter encounter : encounters) {
      Map<ObsColumnDescriptor, Obs> obsInEncounter = populatedFieldMap.get(encounter);
      // Not all encounters will have data for all columns but
      // each encounter row should have all column headers
      // so encounters line up properly under a common set of column headers
      allColumns.addAll(obsInEncounter.keySet());
    }

    // add the data to the DataSet
    return addData(
        dataSet,
        encounters,
        patientIdentifierTypes,
        optionalColumns,
        columnDisplayFormat,
        maxColumnHeaderWidth,
        allColumns,
        populatedFieldMap);
  }