@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;
  }
  @Override
  public EvaluatedPersonData evaluate(PersonDataDefinition definition, EvaluationContext context)
      throws EvaluationException {

    FirstEncounterAtFacilityDataDefinition def =
        (FirstEncounterAtFacilityDataDefinition) definition;
    EvaluatedPersonData c = new EvaluatedPersonData(def, context);

    if (context.getBaseCohort() == null || context.getBaseCohort().isEmpty()) {
      return c;
    }

    // find the facility number
    MOHFacility facility = (MOHFacility) context.getParameterValue("facility");

    // fail quickly if the facility does not exist
    if (facility == null) {
      log.warn("No facility provided; returning empty data.");
      return c;
    }

    // use HQL to do our bidding
    String hql =
        "from Encounter"
            + " where voided=false"
            + " and patientId in (:patientIds)"
            + " and location in (:locationList)"
            + " and encounterDatetime <= :onOrBefore"
            + " order by encounterDatetime asc";

    Map<String, Object> m = new HashMap<String, Object>();
    m.put("patientIds", context.getBaseCohort());
    m.put("locationList", facility.getLocations());
    m.put("onOrBefore", context.getEvaluationDate());

    DataSetQueryService qs = Context.getService(DataSetQueryService.class);
    List<Object> queryResult = qs.executeHqlQuery(hql, m);

    ListMap<Integer, Encounter> encForPatients = new ListMap<Integer, Encounter>();
    for (Object o : queryResult) {
      Encounter enc = (Encounter) o;
      encForPatients.putInList(enc.getPatientId(), enc);
    }

    for (Integer pId : encForPatients.keySet()) {
      List<Encounter> l = encForPatients.get(pId);
      c.addData(pId, l.get(0));
    }

    return c;
  }