/**
   * calculates the remainder of fiscal year estimated income for pooled funds
   *
   * @param security
   * @param holdingTaxLot
   * @return amount
   */
  protected BigDecimal getRemainderOfFiscalYearEstimatedIncomeForPooledFunds(
      Security security, HoldingTaxLot holdingTaxLot) {
    BigDecimal amount = BigDecimal.ZERO;

    if (ObjectUtils.isNull(security.getIncomeNextPayDate())
        || ObjectUtils.isNull(security.getFrequencyCode())) {
      return amount;
    }

    Date nextIncomeDueDate = security.getIncomeNextPayDate();
    if (ObjectUtils.isNull(nextIncomeDueDate)) {
      return amount;
    }

    Date fiscalYearEndDate = getFiscalYearEndDate();

    // BONDS - rule 4.a
    if (nextIncomeDueDate.after(fiscalYearEndDate)) {
      return BigDecimal.ZERO;
    }

    // rule 4.b
    if (nextIncomeDueDate.before(fiscalYearEndDate)) {
      String incomePayFrequency = security.getIncomePayFrequency();
      if (ObjectUtils.isNull(incomePayFrequency)) {
        return amount;
      }

      Date lastPaymentDate = getLastPaymentDate(incomePayFrequency, fiscalYearEndDate);

      long paymentsRemaining =
          getTotalPaymentsRemaining(
              lastPaymentDate, fiscalYearEndDate, incomePayFrequency, nextIncomeDueDate);

      long totalNumberOfPayments = kEMService.getTotalNumberOfPaymentsForFiscalYear();

      amount =
          KEMCalculationRoundingHelper.multiply(
              holdingTaxLot.getUnits(),
              security.getIncomeRate(),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
      amount = amount.multiply(BigDecimal.valueOf(paymentsRemaining));
      amount =
          KEMCalculationRoundingHelper.divide(
              amount,
              BigDecimal.valueOf(totalNumberOfPayments),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
      amount = amount.add(holdingTaxLot.getCurrentAccrual());
    }

    return amount;
  }
  /**
   * calculates the remainder of fiscal year estimated income for stocks
   *
   * @param security
   * @param holdingTaxLot
   * @return amount
   */
  protected BigDecimal getRemainderOfFiscalYearEstimatedIncomeForStocks(
      Security security, HoldingTaxLot holdingTaxLot) {
    BigDecimal amount = BigDecimal.ZERO;

    if (ObjectUtils.isNull(security.getIncomeRate())
        || security.getIncomeRate().compareTo(BigDecimal.ZERO) == 0) {
      return amount;
    }

    String incomePayFrequency = security.getIncomePayFrequency();
    Date nextIncomeDueDate = security.getIncomeNextPayDate();

    if (ObjectUtils.isNull(nextIncomeDueDate)) {
      return amount;
    }

    Date fiscalYearEndDate = getFiscalYearEndDate();

    // BONDS - rule 4.a
    if (nextIncomeDueDate.after(fiscalYearEndDate)) {
      return BigDecimal.ZERO;
    }

    int numberOfMonthsRemaing = getNumberOfMonthsRemaining(fiscalYearEndDate, nextIncomeDueDate);

    if (nextIncomeDueDate.before(fiscalYearEndDate) && numberOfMonthsRemaing < 4) {
      return BigDecimal.ZERO;
    }

    long quartersLeftToFiscalYear = getQuartersLeftToFiscalYear(fiscalYearEndDate);

    // calculate holding units times security rate....
    amount =
        KEMCalculationRoundingHelper.multiply(
            holdingTaxLot.getUnits(),
            security.getIncomeRate(),
            EndowConstants.Scale.SECURITY_MARKET_VALUE);

    // now multiply the above amount by 4 to get amount for 4 quarters or for the year...
    amount =
        KEMCalculationRoundingHelper.divide(
            amount, BigDecimal.valueOf(4), EndowConstants.Scale.SECURITY_MARKET_VALUE);

    // now compute the amount for the quarters remaining in the fiscal year....
    amount =
        KEMCalculationRoundingHelper.multiply(
            amount,
            BigDecimal.valueOf(quartersLeftToFiscalYear),
            EndowConstants.Scale.SECURITY_MARKET_VALUE);

    return amount;
  }
  /**
   * calculates the remainder of fiscal year estimated income for cash
   *
   * @param security
   * @param holdingTaxLot
   * @return amount
   */
  protected BigDecimal getRemainderOfFiscalYearEstimatedIncomeForCash(
      Security security, HoldingTaxLot holdingTaxLot) {
    BigDecimal amount = BigDecimal.ZERO;

    if (ObjectUtils.isNull(security.getIncomeRate())
        || security.getIncomeRate().compareTo(BigDecimal.ZERO) == 0) {
      return amount;
    }

    Date nextIncomeDueDate = security.getIncomeNextPayDate();
    Date fiscalYearEndDate = getFiscalYearEndDate();
    String incomePayFrequency = security.getIncomePayFrequency();

    if (ObjectUtils.isNull(nextIncomeDueDate) || ObjectUtils.isNull(incomePayFrequency)) {
      return amount;
    }

    // BONDS - rule 3.a
    if (nextIncomeDueDate.after(fiscalYearEndDate)) {
      return BigDecimal.ZERO;
    }

    // rule 3.b
    if (nextIncomeDueDate.before(fiscalYearEndDate)) {
      Date lastPaymentDate = getLastPaymentDate(incomePayFrequency, fiscalYearEndDate);
      long daysToLastPayment = getTotalDaysToLastPayment(lastPaymentDate, nextIncomeDueDate);

      amount =
          KEMCalculationRoundingHelper.multiply(
              holdingTaxLot.getUnits(),
              security.getIncomeRate(),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
      amount = amount.multiply(BigDecimal.valueOf(daysToLastPayment));
      amount =
          KEMCalculationRoundingHelper.divide(
              amount,
              BigDecimal.valueOf(EndowConstants.NUMBER_OF_DAYS_IN_YEAR),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
      amount = amount.add(holdingTaxLot.getCurrentAccrual());
    }

    return amount;
  }
  /**
   * calculates the remainder of fiscal year estimated income for bonds
   *
   * @param security
   * @param holdingTaxLot
   * @return amount
   */
  protected BigDecimal getRemainderOfFiscalYearEstimatedIncomeForBonds(
      Security security, HoldingTaxLot holdingTaxLot) {
    BigDecimal amount = BigDecimal.ZERO;

    if (ObjectUtils.isNull(security.getIncomeRate())
        || security.getIncomeRate().compareTo(BigDecimal.ZERO) == 0) {
      return amount;
    }

    Date nextIncomeDueDate = security.getIncomeNextPayDate();
    if (ObjectUtils.isNull(nextIncomeDueDate)) {
      return amount;
    }

    Date fiscalYearEndDate = getFiscalYearEndDate();

    // BONDS - rule 2.a
    if (nextIncomeDueDate.after(fiscalYearEndDate)) {
      return BigDecimal.ZERO;
    }

    int numberOfMonthsRemaining = getNumberOfMonthsRemaining(fiscalYearEndDate, nextIncomeDueDate);
    // rule 2.b
    if (nextIncomeDueDate.before(fiscalYearEndDate)
        && numberOfMonthsRemaining < EndowConstants.NUMBER_OF_MONTHS_REMAINING) {
      amount =
          KEMCalculationRoundingHelper.multiply(
              holdingTaxLot.getUnits(),
              security.getIncomeRate(),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
      amount =
          KEMCalculationRoundingHelper.divide(
              amount, BigDecimal.valueOf(2), EndowConstants.Scale.SECURITY_MARKET_VALUE);
    } else {
      amount =
          KEMCalculationRoundingHelper.multiply(
              holdingTaxLot.getUnits(),
              security.getIncomeRate(),
              EndowConstants.Scale.SECURITY_MARKET_VALUE);
    }

    return amount;
  }