/**
   * Evaluates the calculation
   *
   * @should calculate false for deceased patients
   * @should calculate false for patients not in HIV program
   * @should calculate false for patients with an encounter in last LOST_TO_FOLLOW_UP_THRESHOLD_DAYS
   *     days days since appointment date
   * @should calculate true for patient with no encounter in last LOST_TO_FOLLOW_UP_THRESHOLD_DAYS
   *     days days since appointment date
   */
  @Override
  public CalculationResultMap evaluate(
      Collection<Integer> cohort, Map<String, Object> arg1, PatientCalculationContext context) {

    Program hivProgram = MetadataUtils.existing(Program.class, HivMetadata._Program.HIV);
    Concept reasonForDiscontinuation =
        Dictionary.getConcept(Dictionary.REASON_FOR_PROGRAM_DISCONTINUATION);
    Concept transferout = Dictionary.getConcept(Dictionary.TRANSFERRED_OUT);

    Set<Integer> alive = Filters.alive(cohort, context);
    Set<Integer> inHivProgram = Filters.inProgram(hivProgram, alive, context);

    // CalculationResultMap lastEncounters = Calculations.lastEncounter(null, inHivProgram,
    // context);
    CalculationResultMap lastReturnDateObss =
        Calculations.lastObs(
            Dictionary.getConcept(Dictionary.RETURN_VISIT_DATE), inHivProgram, context);
    CalculationResultMap lastProgramDiscontinuation =
        Calculations.lastObs(reasonForDiscontinuation, cohort, context);

    CalculationResultMap ret = new CalculationResultMap();
    for (Integer ptId : cohort) {
      boolean lost = false;

      // Is patient alive and in the HIV program
      if (alive.contains(ptId)) {

        // Patient is lost if no encounters in last X days
        // Encounter lastEncounter = EmrCalculationUtils.encounterResultForPatient(lastEncounters,
        // ptId);
        Date lastScheduledReturnDate =
            EmrCalculationUtils.datetimeObsResultForPatient(lastReturnDateObss, ptId);
        Obs discontuation =
            EmrCalculationUtils.obsResultForPatient(lastProgramDiscontinuation, ptId);
        if (lastScheduledReturnDate != null) {
          if (daysSince(lastScheduledReturnDate, context)
              > HivConstants.LOST_TO_FOLLOW_UP_THRESHOLD_DAYS) {
            lost = true;
          }
          if (discontuation != null && discontuation.getValueCoded().equals(transferout)) {
            lost = false;
          }
        }
      }
      ret.put(ptId, new SimpleResult(lost, this, context));
    }
    return ret;
  }
  /**
   * @see org.openmrs.calculation.patient.PatientCalculation#evaluate(java.util.Collection,
   *     java.util.Map, org.openmrs.calculation.patient.PatientCalculationContext)
   */
  @Override
  public CalculationResultMap evaluate(
      Collection<Integer> cohort,
      Map<String, Object> parameterValues,
      PatientCalculationContext context) {

    Program mchcsProgram = MetadataUtils.getProgram(MchMetadata._Program.MCHCS);

    // Get all patients who are alive and in MCH-CS program
    Set<Integer> alive = Filters.alive(cohort, context);
    Set<Integer> inMchcsProgram = Filters.inProgram(mchcsProgram, alive, context);

    // get wheather the child is HIV Exposed
    CalculationResultMap lastChildHivStatus =
        Calculations.lastObs(
            Dictionary.getConcept(Dictionary.CHILDS_CURRENT_HIV_STATUS), inMchcsProgram, context);
    CalculationResultMap medOrdersObss =
        Calculations.allObs(Dictionary.getConcept(Dictionary.MEDICATION_ORDERS), cohort, context);

    // Get concepts for  medication prophylaxis
    Concept nvp = Dictionary.getConcept(Dictionary.NEVIRAPINE);
    Concept nvpazt3tc = Dictionary.getConcept(Dictionary.LAMIVUDINE_NEVIRAPINE_ZIDOVUDINE);
    Concept hivExposed = Dictionary.getConcept(Dictionary.EXPOSURE_TO_HIV);

    CalculationResultMap ret = new CalculationResultMap();

    for (Integer ptId : cohort) {
      boolean notOnPcp = false;

      // checking wheather the infant is in mchcs program, alive and HEI
      Obs hivStatusObs = EmrCalculationUtils.obsResultForPatient(lastChildHivStatus, ptId);
      if (inMchcsProgram.contains(ptId)
          && lastChildHivStatus != null
          && hivStatusObs != null
          && (hivStatusObs.getValueCoded().equals(hivExposed))) {
        notOnPcp = true;
        ListResult patientMedOrders = (ListResult) medOrdersObss.get(ptId);
        if (patientMedOrders != null) {
          // Look through list of medication order obs for any  CTX
          List<Obs> medOrderObsList = CalculationUtils.extractResultValues(patientMedOrders);
          for (Obs medOrderObs : medOrderObsList) {
            if (medOrderObs.getValueCoded().equals(nvp)
                || medOrderObs.getValueCoded().equals(nvpazt3tc)) {
              notOnPcp = false;
              break;
            }
          }
        }
      }
      ret.put(ptId, new BooleanResult(notOnPcp, this, context));
    }

    return ret;
  }
  @Override
  public CalculationResultMap evaluate(
      Collection<Integer> cohort,
      Map<String, Object> parameterValues,
      PatientCalculationContext context) {
    Program hivProgram = MetadataUtils.existing(Program.class, HivMetadata._Program.HIV);

    Set<Integer> alive = Filters.alive(cohort, context);
    Set<Integer> inHivProgram = Filters.inProgram(hivProgram, alive, context);

    Set<Integer> aliveAndFemale = Filters.female(Filters.alive(cohort, context), context);

    CalculationResultMap ret = new CalculationResultMap();

    // need to exclude those on ART already
    Set<Integer> onArt =
        CalculationUtils.patientsThatPass(calculate(new OnArtCalculation(), cohort, context));
    // find the observation for viral load recorded
    CalculationResultMap viralLoad =
        Calculations.lastObs(Dictionary.getConcept(Dictionary.HIV_VIRAL_LOAD), cohort, context);
    // get a list of all the viral load
    CalculationResultMap viralLoadList =
        Calculations.allObs(Dictionary.getConcept(Dictionary.HIV_VIRAL_LOAD), cohort, context);
    // check for non detectables
    CalculationResultMap ldlViralLoad =
        Calculations.allObs(
            Dictionary.getConcept(Dictionary.HIV_VIRAL_LOAD_QUALITATIVE), cohort, context);

    // check for test orders
    CalculationResultMap testOrders =
        Calculations.allObs(Dictionary.getConcept(Dictionary.TESTS_ORDERED), cohort, context);

    // check for last ldl
    CalculationResultMap ldlLast =
        Calculations.lastObs(
            Dictionary.getConcept(Dictionary.HIV_VIRAL_LOAD_QUALITATIVE), cohort, context);

    // find for prgnant females

    CalculationResultMap pregStatusObss =
        Calculations.lastObs(
            Dictionary.getConcept(Dictionary.PREGNANCY_STATUS), aliveAndFemale, context);

    // get the initial art start date
    CalculationResultMap artStartDate =
        calculate(new InitialArtStartDateCalculation(), cohort, context);

    for (Integer ptId : cohort) {
      boolean needsViralLoadTest = false;
      Obs viralLoadObs = EmrCalculationUtils.obsResultForPatient(viralLoad, ptId);
      Date dateInitiated = EmrCalculationUtils.datetimeResultForPatient(artStartDate, ptId);
      ListResult listResult = (ListResult) viralLoadList.get(ptId);
      ListResult testOrdersList = (ListResult) testOrders.get(ptId);
      List<Obs> testObsList = CalculationUtils.extractResultValues(testOrdersList);
      List<Obs> listObsViralLoads = CalculationUtils.extractResultValues(listResult);
      ListResult ldlList = (ListResult) ldlViralLoad.get(ptId);
      List<List> listLdl = CalculationUtils.extractResultValues(ldlList);
      Obs lastLdlObs = EmrCalculationUtils.obsResultForPatient(ldlLast, ptId);
      // find pregnancy obs
      Obs pregnantEdd = EmrCalculationUtils.obsResultForPatient(pregStatusObss, ptId);

      // Find latest viral load test order
      Obs lastVlTestOrder = getLatestVlOrder(testObsList);

      if (inHivProgram.contains(ptId) && onArt.contains(ptId)) {
        if (listObsViralLoads.size() == 0
            && listLdl.size() == 0
            && dateInitiated != null
            && (daysSince(dateInitiated, context) >= 180)) {
          needsViralLoadTest = true;
        }
        // need to check here if patient has had any viral load
        /*if(dateInitiated != null && (daysSince(dateInitiated, context) >= 360)) {
            needsViralLoadTest = true;
        }*/

        // those continuing should receive one VL every year
        // pick the date of the last viral load numeric values
        if (viralLoadObs != null && (daysSince(viralLoadObs.getObsDatetime(), context) >= 360)) {
          needsViralLoadTest = true;
        }
        // pick the date of the ldl
        if (lastLdlObs != null && (daysSince(lastLdlObs.getObsDatetime(), context) >= 360)) {
          needsViralLoadTest = true;
        }

        // if vl more than
        if (viralLoadObs != null
            && viralLoadObs.getValueNumeric() > 1000
            && (daysSince(viralLoadObs.getObsDatetime(), context) > 90)) {
          needsViralLoadTest = true;

          if (lastLdlObs != null && (daysSince(lastLdlObs.getObsDatetime(), context) < 360)) {
            needsViralLoadTest = false;
          }
        }

        // check for pregnancy
        if (pregnantEdd != null
            && pregnantEdd.getValueCoded().equals(Dictionary.getConcept(Dictionary.YES))
            && dateInitiated != null) {
          Date whenVLWillBeDue =
              DateUtil.adjustDate(
                  DateUtil.adjustDate(dateInitiated, 6, DurationUnit.MONTHS),
                  -1,
                  DurationUnit.DAYS);
          // if last vl is 6 months older then the patient is due for vl
          if (viralLoadObs == null && lastLdlObs == null) {
            needsViralLoadTest = true;
          }
          if (viralLoadObs == null
              && lastLdlObs == null
              && (context.getNow().after(whenVLWillBeDue))) {
            needsViralLoadTest = true;
          }
          if (viralLoadObs != null
              && viralLoadObs.getValueNumeric() > 1000
              && (monthsBetween(viralLoadObs.getObsDatetime(), context.getNow()) >= 3)) {
            needsViralLoadTest = true;
            if (lastLdlObs != null && (daysSince(lastLdlObs.getObsDatetime(), context) < 360)) {
              needsViralLoadTest = false;
            }
          }
          if (viralLoadObs != null && daysSince(viralLoadObs.getObsDatetime(), context) >= 180) {
            needsViralLoadTest = true;
          }
          if (lastLdlObs != null && daysSince(lastLdlObs.getObsDatetime(), context) >= 180) {
            needsViralLoadTest = true;
          }
        }

        // check if has vl test order within 6 months
        if (lastVlTestOrder != null
            && daysSince(lastVlTestOrder.getObsDatetime(), context) >= 180) {
          needsViralLoadTest = true;
        }

        if (lastVlTestOrder != null && daysSince(lastVlTestOrder.getObsDatetime(), context) < 180) {
          needsViralLoadTest = false;
        }
      }

      ret.put(ptId, new BooleanResult(needsViralLoadTest, this));
    }
    return ret;
  }