/**
   * @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);
  }
  /** @see DataSetEvaluator#evaluate(DataSetDefinition, EvaluationContext) */
  @SuppressWarnings("unchecked")
  public DataSet evaluate(DataSetDefinition dataSetDefinition, EvaluationContext context)
      throws EvaluationException {

    PatientDataSetDefinition dsd = (PatientDataSetDefinition) dataSetDefinition;
    context = ObjectUtil.nvl(context, new EvaluationContext());

    SimpleDataSet dataSet = new SimpleDataSet(dsd, context);
    dataSet.setSortCriteria(dsd.getSortCriteria());

    // Construct a new EvaluationContext based on the passed filters
    Cohort c = context.getBaseCohort();
    if (dsd.getRowFilters() != null) {
      for (Mapped<? extends CohortDefinition> q : dsd.getRowFilters()) {
        Cohort s = Context.getService(CohortDefinitionService.class).evaluate(q, context);
        c = CohortUtil.intersectNonNull(c, s);
      }
    }
    if (c == null) {
      c =
          Context.getService(CohortDefinitionService.class)
              .evaluate(new AllPatientsCohortDefinition(), context);
    }

    EvaluationContext ec = context.shallowCopy();
    if (!CohortUtil.areEqual(ec.getBaseCohort(), c)) {
      ec.setBaseCohort(c);
    }

    // Evaluate each specified ColumnDefinition for all of the included rows and add these to the
    // dataset
    for (RowPerObjectColumnDefinition cd : dsd.getColumnDefinitions()) {

      if (log.isDebugEnabled()) {
        log.debug("Evaluating column: " + cd.getName());
        log.debug(
            "With Data Definition: "
                + DefinitionUtil.format(cd.getDataDefinition().getParameterizable()));
        log.debug("With Mappings: " + cd.getDataDefinition().getParameterMappings());
        log.debug("With Parameters: " + ec.getParameterValues());
      }
      StopWatch sw = new StopWatch();
      sw.start();

      MappedData<? extends PatientDataDefinition> dataDef =
          (MappedData<? extends PatientDataDefinition>) cd.getDataDefinition();
      EvaluatedPatientData data =
          Context.getService(PatientDataService.class).evaluate(dataDef, ec);

      for (Integer id : c.getMemberIds()) {
        for (DataSetColumn column : cd.getDataSetColumns()) {
          Object val = data.getData().get(id);
          val = DataUtil.convertData(val, dataDef.getConverters());
          dataSet.addColumnValue(id, column, val);
        }
      }

      sw.stop();
      if (log.isDebugEnabled()) {
        log.debug("Evaluated column. Duration: " + sw.toString());
      }
    }

    return dataSet;
  }