/**
   * @see CohortDefinitionEvaluator#evaluate(CohortDefinition, EvaluationContext)
   * @should return patients in the specified states before the start date
   * @should return patients in the specified states after the start date
   * @should return patients in the specified states before the end date
   * @should return patients in the specified states after the end date
   * @should find patients in specified states on the before start date if passed in time is at
   *     midnight
   * @should find patients in specified states on the before end date if passed in time is at
   *     midnight
   */
  public EvaluatedCohort evaluate(CohortDefinition cohortDefinition, EvaluationContext context) {

    PatientStateCohortDefinition def = (PatientStateCohortDefinition) cohortDefinition;

    HqlQueryBuilder qb = new HqlQueryBuilder();
    qb.select("ps.patientProgram.patient.patientId");
    qb.from(PatientState.class, "ps");
    qb.whereIn("ps.state", def.getStates());
    qb.whereGreaterOrEqualTo("ps.startDate", def.getStartedOnOrAfter());
    qb.whereLessOrEqualTo("ps.startDate", def.getStartedOnOrBefore());
    qb.whereGreaterOrEqualTo("ps.endDate", def.getEndedOnOrAfter());
    qb.whereLessOrEqualTo("ps.endDate", def.getEndedOnOrBefore());
    qb.whereIn("ps.patientProgram.location", def.getLocationList());
    qb.wherePatientIn("ps.patientProgram.patient.patientId", context);

    List<Integer> pIds = evaluationService.evaluateToList(qb, Integer.class, context);

    return new EvaluatedCohort(new Cohort(pIds), cohortDefinition, context);
  }
  @Override
  public EncounterQueryResult evaluate(EncounterQuery definition, EvaluationContext context)
      throws EvaluationException {
    context = ObjectUtil.nvl(context, new EvaluationContext());

    BasicEncounterQuery query = (BasicEncounterQuery) definition;
    EncounterQueryResult result = new EncounterQueryResult(query, context);

    if (context.getBaseCohort() != null && context.getBaseCohort().isEmpty()) {
      return result;
    }
    if (context instanceof EncounterEvaluationContext) {
      EncounterIdSet baseEncounters = ((EncounterEvaluationContext) context).getBaseEncounters();
      if (baseEncounters != null && baseEncounters.getMemberIds().isEmpty()) {
        return result;
      }
    }

    HqlQueryBuilder q = new HqlQueryBuilder();
    q.select("e.encounterId");
    q.from(Encounter.class, "e");
    q.whereIn("e.encounterType", query.getEncounterTypes());
    q.whereIn("e.form", query.getForms());
    q.whereGreaterOrEqualTo("e.encounterDatetime", query.getOnOrAfter());
    q.whereLessOrEqualTo("e.encounterDatetime", query.getOnOrBefore());
    q.whereIn("e.location", query.getLocationList());
    q.whereEncounterIn("e.encounterId", context);

    if (query.getWhich() == null || query.getWhich() == TimeQualifier.ANY) {
      List<Integer> results = evaluationService.evaluateToList(q, Integer.class);
      result.getMemberIds().addAll(results);
    } else {
      q.innerJoin("e.patient", "p");
      q.select("p.patientId");
      if (query.getWhich() == TimeQualifier.LAST) {
        q.orderDesc("e.encounterDatetime");
      } else {
        q.orderAsc("e.encounterDatetime");
      }

      ListMap<Integer, Integer> foundEncountersForPatients = new ListMap<Integer, Integer>();
      int maxNumPerPatient = ObjectUtil.nvl(query.getWhichNumber(), 1);
      for (Object[] row : evaluationService.evaluateToList(q)) {
        Integer encounterId = (Integer) row[0];
        Integer patientId = (Integer) row[1];
        foundEncountersForPatients.putInList(patientId, encounterId);
        if (foundEncountersForPatients.get(patientId).size() <= maxNumPerPatient) {
          result.add(encounterId);
        }
      }
    }

    return result;
  }