class AnnuityDefinitionBuilder {

  public static AnnuityDefinition<CouponDefinition> couponIborWithNotional(final ZonedDateTime settlementDate, final ZonedDateTime maturityDate, final double notional,
      final IborIndex index, final DayCount dayCountAnnuity, final BusinessDayConvention bdcAnnuity, final boolean eomAnnuity,
      final Period paymentPeriod, final boolean isPayer, final Calendar calendar, final StubType stub, final int paymentLag,
      final boolean notionalStart, final boolean notionalEnd) {
    ArgumentChecker.notNull(settlementDate, "settlement date");
    ArgumentChecker.notNull(maturityDate, "maturity date");
    ArgumentChecker.notNull(index, "index");
    final AnnuityDefinition<CouponIborDefinition> annuityNoNotional = couponIbor(settlementDate, maturityDate, index.getTenor(), notional, index,
        isPayer, dayCountAnnuity, bdcAnnuity, eomAnnuity, calendar, stub, paymentLag);
    final double sign = (isPayer) ? -1.0 : 1.0;
    final int nbPay = annuityNoNotional.getNumberOfPayments();
    int nbNotional = 0;
    nbNotional = (notionalStart ? nbNotional + 1 : nbNotional);
    nbNotional = (notionalEnd ? nbNotional + 1 : nbNotional);
    final CouponDefinition[] annuityWithNotional = new CouponDefinition[nbPay + nbNotional];
    int loopnot = 0;
    if (notionalStart) {
      annuityWithNotional[0] = new CouponFixedDefinition(index.getCurrency(), settlementDate, settlementDate, settlementDate, 1.0, -notional * sign, 1);
      // Implementation note: Fixed amount written as a fixed coupon.
      loopnot++;
    }
    System.arraycopy(annuityNoNotional.getPayments(), 0, annuityWithNotional, loopnot, nbPay);
    if (notionalEnd) {
      final ZonedDateTime payDate = annuityNoNotional.getNthPayment(nbPay - 1).getPaymentDate();
      annuityWithNotional[nbPay + loopnot] = new CouponFixedDefinition(index.getCurrency(), payDate, payDate, payDate, 1.0, notional * sign, 1);
    }
    return new AnnuityDefinition<>(annuityWithNotional, calendar);
  }

}