/**
   * Calculates the postpartum end date as 6 months past the delivery date.
   *
   * <p>NOTE: In the absence of a delivery date, the EDC is used.
   *
   * @return The end date of the postpartum stage
   */
  public Date getPostpartumStageEndDate() {
    Date deliveryDate = null;
    if (Functions.observation(mcProgramObs.getObs(), MCDeliveryReportConcepts.DELIVERY_REPORT)
        != null) {
      final Obs deliveryDateObs =
          mcProgramObs.getDeliveryReport().getMember(MCDeliveryReportConcepts.DELIVERY_DATE);
      if (deliveryDateObs != null) {
        deliveryDate = deliveryDateObs.getValueDatetime();
      }
    }

    if (deliveryDate == null) {
      // use EDC
      deliveryDate = mcProgramObs.getEstimatedDateOfConfinement();
    }

    if (deliveryDate != null) {
      final Calendar sixMonthsAfterDeliveryDate = Calendar.getInstance();
      sixMonthsAfterDeliveryDate.setTime(DateUtil.stripTime(deliveryDate));
      sixMonthsAfterDeliveryDate.add(Calendar.MONTH, 6);

      // return the calculated postpartum stage end date
      return sixMonthsAfterDeliveryDate.getTime();
    }

    // postpartum stage end date is not available
    return null;
  }
  /**
   * 1.4.4 Returning to ACTIVE status: If coming from an ADMITTED status, the status of the Delivery
   * Record is checked. If it has not yet been updated (no delivery date is entered), the form is
   * displayed (refer to Section 2.1.1)
   *
   * @return
   */
  public boolean isDeliveryReportNeeded() {
    if (isProgramConcluded()) {
      // program already concluded, no need for delivery report
      return false;
    }

    final List<PatientConsultStatus> statuses = mcProgramObs.getAllPatientConsultStatus();
    if (statuses.size() > 1) {
      final PatientConsultStatus previous = statuses.get(statuses.size() - 2);
      final PatientConsultStatus current = statuses.get(statuses.size() - 1);

      // check if status was 'active' and coming from 'admitted'
      if (current.getStatus() == MaternalCareProgramStates.ACTIVE //
          && previous.getStatus() == MaternalCareProgramStates.ADMITTED) {
        // if no delivery report has been created yet, then we need to ask the user to create one
        if (Functions.observation(mcProgramObs.getObs(), MCDeliveryReportConcepts.DELIVERY_REPORT)
            == null) {
          // no delivery report created yet: we need to pop-up the delivery report dialog
          return true;
        }
      }
    }

    // delivery report doesn't need to be displayed
    return false;
  }
  /**
   * Checks if any of the 'E' risk factors are present in the group obs.
   *
   * @param grp
   * @return
   */
  private boolean anyRiskFactorsTypeE(GroupObs grp) {
    final Concept yes = Functions.trueConcept();

    // check if any of the 'E' risk factors are present
    for (CachedConceptId rfEConceptId :
        new CachedConceptId[] {
          MCMedicalHistoryConcepts.TUBERCULOSIS, //
          MCMedicalHistoryConcepts.HEART_DISEASE, //
          MCMedicalHistoryConcepts.DIABETES, //
          MCMedicalHistoryConcepts.ASTHMA, //
          MCMedicalHistoryConcepts.THYROID
        }) {
      final Obs conditionObs = grp.getMember(rfEConceptId);
      if (conditionObs != null && yes.equals(conditionObs.getValueCoded())) {
        // risk factor 'E' is present
        return true;
      }
    }

    // risk factor 'E' not found in this group observation
    return false;
  }
  /**
   * Calculates the risk factors based on the maternal care observation in the form.
   *
   * @return A comma-delimited list of risk factors (from A to E)
   */
  public String getRiskFactors() {
    // store risk factors in a list
    final List<String> riskFactors = new ArrayList<String>();

    if (getPatient() != null && mcProgramObs != null) {
      // check for risk factor 'a' using age at LMP
      final Obs lmpObs =
          mcProgramObs
              .getObstetricHistory()
              .getMember(MCObstetricHistoryConcepts.LAST_MENSTRUAL_PERIOD);
      if (lmpObs != null
          && lmpObs.getValueDatetime() != null
          && getPatient().getBirthdate() != null) {
        final int age =
            DateUtil.yearsBetween(getPatient().getBirthdate(), lmpObs.getValueDatetime());
        if (age < 18 || age > 35) {
          riskFactors.add("A");
        }
      }

      // check for risk factor 'b' using patient's height
      final Obs heightObs = Functions.observation(getPatient(), VisitConcepts.HEIGHT_CM);
      if (heightObs != null && heightObs.getValueNumeric() != null) {
        if (heightObs.getValueNumeric() < 145) {
          riskFactors.add("B");
        }
      }

      // check for risk factor 'c' using patient obstetric history
      final ObstetricHistory obHistory = mcProgramObs.getObstetricHistory();
      final Obs gravidaObs =
          obHistory.getMember(MCObstetricHistoryConcepts.OBSTETRIC_SCORE_GRAVIDA);
      if (gravidaObs != null && gravidaObs.getValueNumeric() != null) {
        if (gravidaObs.getValueNumeric() >= 4) {
          riskFactors.add("C");
        }
      }

      // check for risk factors 'd' using registration data
      final Concept yes = Functions.trueConcept();
      if (yes.equals(
              obHistory.getMember(MCObstetricHistoryConcepts.HISTORY_PREV_CSECTION).getValueCoded())
          || yes.equals(
              obHistory
                  .getMember(MCObstetricHistoryConcepts.HISTORY_3OR_MORE_MISCARRIAGES)
                  .getValueCoded())
          || yes.equals(
              obHistory
                  .getMember(MCObstetricHistoryConcepts.HISTORY_OF_POSTPARTUM_HEMORRHAGE)
                  .getValueCoded())) {
        riskFactors.add("D");
      }

      if (anyRiskFactorsTypeE(mcProgramObs.getPatientMedicalHistory())) {
        // check 'past' medical history
        riskFactors.add("E");
      } else {
        // check medical history observed during this pregnancy
        for (PrenatalVisitRecord visit : mcProgramObs.getPrenatalVisits()) {
          if (anyRiskFactorsTypeE(visit.getNewMedicalConditions())) {
            riskFactors.add("E");
            break;
          }
        }
      }
    }

    if (riskFactors.isEmpty()) {
      return "none";
    } else {
      final String riskFactorsText = riskFactors.toString();
      return riskFactorsText.substring(1, riskFactorsText.length() - 1);
    }
  }