/**
   * This is the present value of the premium leg per unit of fractional spread - hence it is equal
   * to 10,000 times the RPV01 (Risky PV01). The actual PV of the leg is this multiplied by the
   * notional and the fractional spread (i.e. spread in basis points divided by 10,000)
   *
   * <p>This mimics the ISDA c function <b>JpmcdsCdsFeeLegPV</b>
   *
   * @param today The 'current' date
   * @param stepinDate Date when party assumes ownership. This is normally today + 1 (T+1). Aka
   *     assignment date or effective date.
   * @param valueDate The valuation date. The date that values are PVed to. Is is normally today + 3
   *     business days. Aka cash-settle date.
   * @param startDate The protection start date. If protectStart = true, then protections starts at
   *     the beginning of the day, otherwise it is at the end.
   * @param endDate The protection end date (the protection ends at end of day)
   * @param payAccOnDefault Is the accrued premium paid in the event of a default
   * @param tenor The nominal step between premium payments (e.g. 3 months, 6 months).
   * @param stubType stubType Options are FRONTSHORT, FRONTLONG, BACKSHORT, BACKLONG or NONE -
   *     <b>Note</b> in this code NONE is not allowed
   * @param yieldCurve Curve from which payments are discounted
   * @param hazardRateCurve Curve giving survival probability
   * @param protectStart Does protection start at the beginning of the day
   * @param priceType Clean or Dirty price. The clean price removes the accrued premium if the trade
   *     is between payment times.
   * @return 10,000 times the RPV01 (on a notional of 1)
   */
  public double pvPremiumLegPerUnitSpread(
      final LocalDate today,
      final LocalDate stepinDate,
      final LocalDate valueDate,
      final LocalDate startDate,
      final LocalDate endDate,
      final boolean payAccOnDefault,
      final Period tenor,
      final StubType stubType,
      final ISDACompliantDateYieldCurve yieldCurve,
      final ISDACompliantDateCreditCurve hazardRateCurve,
      final boolean protectStart,
      final PriceType priceType) {
    ArgumentChecker.notNull(today, "null today");
    ArgumentChecker.notNull(stepinDate, "null stepinDate");
    ArgumentChecker.notNull(valueDate, "null valueDate");
    ArgumentChecker.notNull(startDate, "null startDate");
    ArgumentChecker.notNull(endDate, "null endDate");
    ArgumentChecker.notNull(tenor, "null tenor");
    ArgumentChecker.notNull(stubType, "null stubType");
    ArgumentChecker.notNull(yieldCurve, "null yieldCurve");
    ArgumentChecker.notNull(hazardRateCurve, "null hazardRateCurve");
    ArgumentChecker.notNull(priceType, "null priceType");
    ArgumentChecker.isFalse(valueDate.isBefore(today), "Require valueDate >= today");
    ArgumentChecker.isFalse(stepinDate.isBefore(today), "Require stepin >= today");

    final ISDAPremiumLegSchedule paymentSchedule =
        new ISDAPremiumLegSchedule(
            startDate,
            endDate,
            tenor,
            stubType,
            _businessdayAdjustmentConvention,
            _calandar,
            protectStart);
    final int nPayments = paymentSchedule.getNumPayments();

    // these are potentially different from startDate and endDate
    final LocalDate globalAccStart = paymentSchedule.getAccStartDate(0);
    final LocalDate golobalAccEnd = paymentSchedule.getAccEndDate(nPayments - 1);

    // TODO this logic could be part of ISDAPremiumLegSchdule
    final LocalDate matDate = protectStart ? golobalAccEnd.minusDays(1) : golobalAccEnd;

    if (today.isAfter(matDate) || stepinDate.isAfter(matDate)) {
      return 0.0; // trade has expired
    }

    final LocalDate[] yieldCurveDates = yieldCurve.getCurveDates();
    final LocalDate[] creditCurveDates = hazardRateCurve.getCurveDates();
    // This is common to the protection leg
    final LocalDate[] integrationSchedule =
        payAccOnDefault
            ? getIntegrationNodesAsDates(
                globalAccStart, golobalAccEnd, yieldCurveDates, creditCurveDates)
            : null;
    final int obsOffset = protectStart ? -1 : 0; // protection start at the beginning or end day

    double rpv01 = 0.0;
    for (int i = 0; i < nPayments; i++) {

      final LocalDate accStart = paymentSchedule.getAccStartDate(i);
      final LocalDate accEnd = paymentSchedule.getAccEndDate(i);
      final LocalDate pay = paymentSchedule.getPaymentDate(i);

      if (!accEnd.isAfter(stepinDate)) {
        continue; // this cashflow has already been realised
      }

      final double[] temp =
          calculateSinglePeriodRPV01(
              today, accStart, accEnd, pay, obsOffset, yieldCurve, hazardRateCurve);
      rpv01 += temp[0];

      if (payAccOnDefault) {
        final LocalDate offsetStepinDate = stepinDate.plusDays(obsOffset);
        final LocalDate offsetAccStartDate = accStart.plusDays(obsOffset);
        final LocalDate offsetAccEndDate = accEnd.plusDays(obsOffset);
        rpv01 +=
            calculateSinglePeriodAccrualOnDefault(
                today,
                offsetStepinDate,
                offsetAccStartDate,
                offsetAccEndDate,
                temp[1],
                yieldCurve,
                hazardRateCurve,
                integrationSchedule);
      }
    }

    // Compute the discount factor discounting the upfront payment made on the cash settlement date
    // back to the valuation date
    final double t = _curveDayCount.getDayCountFraction(today, valueDate);
    final double df = yieldCurve.getDiscountFactor(t);
    rpv01 /= df;

    // Do we want to calculate the clean price (includes the previously accrued portion of the
    // premium)
    if (priceType == PriceType.CLEAN) {
      rpv01 -= calculateAccruedInterest(paymentSchedule, stepinDate);
    }

    return rpv01;
  }
  /**
   * Get the value of the protection leg for unit notional
   *
   * <p>This mimics the ISDA c function <b>JpmcdsCdsContingentLegPV</b>
   *
   * @param today The 'current' date
   * @param stepinDate Date when party assumes ownership. This is normally today + 1 (T+1). Aka
   *     assignment date or effective date.
   * @param valueDate The valuation date. The date that values are PVed to. Is is normally today + 3
   *     business days. Aka cash-settle date.
   * @param startDate The protection start date. If protectStart = true, then protections starts at
   *     the beginning of the day, otherwise it is at the end.
   * @param endDate The protection end date (the protection ends at end of day)
   * @param yieldCurve Curve from which payments are discounted
   * @param hazardRateCurve Curve giving survival probability
   * @param recoveryRate The recovery rate of the protected debt
   * @param protectStart Does protection start at the beginning of the day
   * @return unit notional PV of protection (or contingent) leg
   */
  public double calculateProtectionLeg(
      final LocalDate today,
      final LocalDate stepinDate,
      final LocalDate valueDate,
      final LocalDate startDate,
      final LocalDate endDate,
      final ISDACompliantDateYieldCurve yieldCurve,
      final ISDACompliantDateCreditCurve hazardRateCurve,
      final double recoveryRate,
      final boolean protectStart) {
    ArgumentChecker.notNull(today, "null today");
    ArgumentChecker.notNull(valueDate, "null valueDate");
    ArgumentChecker.notNull(startDate, "null startDate");
    ArgumentChecker.notNull(endDate, "null endDate");
    ArgumentChecker.notNull(yieldCurve, "null yieldCurve");
    ArgumentChecker.notNull(hazardRateCurve, "null hazardRateCurve");
    ArgumentChecker.isInRangeInclusive(0, 1.0, recoveryRate);
    ArgumentChecker.isFalse(valueDate.isBefore(today), "Require valueDate >= today");
    ArgumentChecker.isFalse(stepinDate.isBefore(today), "Require stepin >= today");

    if (recoveryRate == 1.0) {
      return 0.0;
    }

    final LocalDate temp = stepinDate.isAfter(startDate) ? stepinDate : startDate;
    final LocalDate effectiveStartDate = protectStart ? temp.minusDays(1) : temp;

    if (!endDate.isAfter(effectiveStartDate)) {
      return 0.0; // the protection has expired
    }

    final LocalDate[] yieldCurveDates = yieldCurve.getCurveDates();
    final LocalDate[] creditCurveDates = hazardRateCurve.getCurveDates();
    final double[] integrationSchedule =
        ISDACompliantScheduleGenerator.getIntegrationNodesAsTimes(
            today, effectiveStartDate, endDate, yieldCurveDates, creditCurveDates);

    // double s1 = hazardRateCurve.getSurvivalProbability(integrationSchedule[0]);
    // double df1 = yieldCurve.getDiscountFactor(integrationSchedule[0]);

    double ht1 = hazardRateCurve.getRT(integrationSchedule[0]);
    double rt1 = yieldCurve.getRT(integrationSchedule[0]);
    double s1 = Math.exp(-ht1);
    double p1 = Math.exp(-rt1);
    double pv = 0.0;
    final int n = integrationSchedule.length;
    for (int i = 1; i < n; ++i) {

      final double ht0 = ht1;
      final double rt0 = rt1;
      final double p0 = p1;
      final double s0 = s1;

      ht1 = hazardRateCurve.getRT(integrationSchedule[i]);
      rt1 = yieldCurve.getRT(integrationSchedule[i]);
      s1 = Math.exp(-ht1);
      p1 = Math.exp(-rt1);
      final double dht = ht1 - ht0;
      final double drt = rt1 - rt0;
      final double dhrt = dht + drt;

      // this is equivalent to the ISDA code without explicitly calculating the time step - it also
      // handles the limit
      double dPV;
      if (Math.abs(dhrt) < 1e-5) {
        dPV = dht * (1 - dhrt * (0.5 - dhrt / 6)) * p0 * s0;
      } else {
        dPV = dht / dhrt * (p0 * s0 - p1 * s1);
      }

      // *************
      // ISDA code
      // **************
      // final double dt = integrationSchedule[i] - integrationSchedule[i - 1];
      // final double s0 = s1;
      // final double df0 = df1;
      // s1 = hazardRateCurve.getSurvivalProbability(integrationSchedule[i]);
      // df1 = yieldCurve.getDiscountFactor(integrationSchedule[i]);
      // final double hazardRate = Math.log(s0 / s1) / dt;
      // final double interestRate = Math.log(df0 / df1) / dt;
      // pv += (hazardRate / (hazardRate + interestRate)) * (1.0 - Math.exp(-(hazardRate +
      // interestRate) * dt)) * s0 * df0;

      pv += dPV;
    }
    pv *= 1.0 - recoveryRate;
    // System.out.println(pv);

    // Compute the discount factor discounting the upfront payment made on the cash settlement date
    // back to the valuation date
    final double t = _curveDayCount.getDayCountFraction(today, valueDate);
    final double df = yieldCurve.getDiscountFactor(t);
    pv /= df;

    return pv;
  }