/**
 * Compute the spread to be added to the rate of the instrument for which the present value of the
 * instrument is zero. The "rate" can be a "rate" or a "yield" and will depend of each instrument.
 *
 * @deprecated {@link YieldCurveBundle} is deprecated
 */
@Deprecated
public final class ParSpreadRateCalculator
    extends InstrumentDerivativeVisitorAdapter<YieldCurveBundle, Double> {

  /** The unique instance of the calculator. */
  private static final ParSpreadRateCalculator INSTANCE = new ParSpreadRateCalculator();

  /**
   * Gets the calculator instance.
   *
   * @return The calculator.
   */
  public static ParSpreadRateCalculator getInstance() {
    return INSTANCE;
  }

  /** Constructor. */
  private ParSpreadRateCalculator() {}

  /** The methods and calculators. */
  private static final PresentValueMCACalculator PVMCC = PresentValueMCACalculator.getInstance();

  private static final PresentValueBasisPointCalculator PVBPC =
      PresentValueBasisPointCalculator.getInstance();
  private static final CashDiscountingMethod METHOD_DEPOSIT = CashDiscountingMethod.getInstance();
  private static final DepositZeroDiscountingMethod METHOD_DEPOSIT_ZERO =
      DepositZeroDiscountingMethod.getInstance();
  private static final ForwardRateAgreementDiscountingMethod METHOD_FRA =
      ForwardRateAgreementDiscountingMethod.getInstance();
  //  private static final InterestRateFutureTransactionDiscountingMethod
  // METHOD_IR_FUTURES_TRANSACTION = InterestRateFutureTransactionDiscountingMethod.getInstance();
  private static final InterestRateFutureSecurityDiscountingMethod METHOD_IR_FUTURES_SECURITY =
      InterestRateFutureSecurityDiscountingMethod.getInstance();

  @Override
  public Double visitCash(final Cash deposit, final YieldCurveBundle curves) {
    return METHOD_DEPOSIT.parSpread(deposit, curves);
  }

  @Override
  public Double visitDepositZero(final DepositZero deposit, final YieldCurveBundle curves) {
    return METHOD_DEPOSIT_ZERO.parSpread(deposit, curves);
  }

  /**
   * For swaps the ParSpread is the spread to be added on each coupon of the first leg to obtain a
   * present value of zero. It is computed as the opposite of the present value of the swap divided
   * by the present value of a basis point of the first leg (as computed by the
   * PresentValueBasisPointCalculator).
   *
   * @param swap The swap.
   * @param curves The yield curve bundle.
   * @return The par spread.
   */
  @Override
  public Double visitSwap(final Swap<?, ?> swap, final YieldCurveBundle curves) {
    Validate.notNull(curves);
    Validate.notNull(swap);
    return -curves
            .getFxRates()
            .convert(swap.accept(PVMCC, curves), swap.getFirstLeg().getCurrency())
            .getAmount()
        / swap.getFirstLeg().accept(PVBPC, curves);
  }

  @Override
  public Double visitFixedCouponSwap(final SwapFixedCoupon<?> swap, final YieldCurveBundle curves) {
    return visitSwap(swap, curves);
  }

  /**
   * For ForwardRateAgreement the ParSpread is the spread to be added to the fixed rate to obtain a
   * present value of zero.
   *
   * @param fra The forward rate agreement.
   * @param curves The yield curve bundle.
   * @return The par spread.
   */
  @Override
  public Double visitForwardRateAgreement(
      final ForwardRateAgreement fra, final YieldCurveBundle curves) {
    Validate.notNull(curves);
    Validate.notNull(fra);
    return METHOD_FRA.parSpread(fra, curves);
  }

  /**
   * For InterestRateFutures the ParSpread is the spread to be added to the reference price to
   * obtain a present value of zero.
   *
   * @param future The futures.
   * @param curves The yield curve bundle.
   * @return The par spread.
   */
  @Override
  public Double visitInterestRateFutureTransaction(
      final InterestRateFutureTransaction future, final YieldCurveBundle curves) {
    return -(METHOD_IR_FUTURES_SECURITY.price(future.getUnderlying(), curves)
        - future.getReferencePrice());
  }

  //  /**
  //   * For InterestRateFutures the ParSpread is the spread to be added to the reference price to
  // obtain a present value of zero.
  //   * @param future The futures.
  //   * @param curves The yield curve bundle.
  //   * @return The par spread.
  //   */
  //  @Override
  //  public Double visitInterestRateFutureSecurity(final InterestRateFutureSecurity future, final
  // YieldCurveBundle curves) {
  //    return -(METHOD_IR_FUTURES_SECURITY.presentValue(future, curves).getAmount());
  //  }
}
public final class BondFutureOptionPremiumTransactionBlackSurfaceMethod {

  /** Creates the method unique instance. */
  private static final BondFutureOptionPremiumTransactionBlackSurfaceMethod INSTANCE =
      new BondFutureOptionPremiumTransactionBlackSurfaceMethod();

  /**
   * Return the method unique instance.
   *
   * @return The instance.
   */
  public static BondFutureOptionPremiumTransactionBlackSurfaceMethod getInstance() {
    return INSTANCE;
  }

  /** Constructor. */
  private BondFutureOptionPremiumTransactionBlackSurfaceMethod() {}

  /** The methods and calculators used. */
  private static final BondFutureOptionPremiumSecurityBlackSurfaceMethod METHOD_SECURITY =
      BondFutureOptionPremiumSecurityBlackSurfaceMethod.getInstance();

  private static final PresentValueMCACalculator PVC = PresentValueMCACalculator.getInstance();
  private static final PresentValueCurveSensitivityIRSCalculator PVCSC =
      PresentValueCurveSensitivityIRSCalculator.getInstance();

  /**
   * Compute the present value of a bond future option transaction from the quoted option price.
   *
   * @param option The future option, not null
   * @param curves The curves, not null
   * @param price The quoted price
   * @return The present value.
   */
  public CurrencyAmount presentValueFromPrice(
      final BondFutureOptionPremiumTransaction option,
      final YieldCurveBundle curves,
      final double price) {
    ArgumentChecker.notNull(option, "option");
    ArgumentChecker.notNull(curves, "curves");
    final Currency ccy = option.getUnderlyingOption().getCurrency();
    final CurrencyAmount premiumPV = option.getPremium().accept(PVC, curves).getCurrencyAmount(ccy);
    final double optionPV =
        price
            * option.getQuantity()
            * option.getUnderlyingOption().getUnderlyingFuture().getNotional();
    return premiumPV.plus(optionPV);
  }

  /**
   * Computes the present value of a bond future option. The option security price is computed
   * according to the data available in the bundle. If the underlying future price is available it
   * is used, if not it is computed from the curves.
   *
   * @param transaction The option transaction.
   * @param curves The curves bundle.
   * @return The present value.
   */
  public CurrencyAmount presentValue(
      final BondFutureOptionPremiumTransaction transaction, final YieldCurveBundle curves) {
    ArgumentChecker.notNull(transaction, "transaction");
    ArgumentChecker.notNull(curves, "curves");
    final double priceSecurity =
        METHOD_SECURITY.optionPrice(transaction.getUnderlyingOption(), curves);
    final CurrencyAmount pvTransaction = presentValueFromPrice(transaction, curves, priceSecurity);
    return pvTransaction;
  }

  /**
   * Computes the present value curve sensitivity of a transaction.
   *
   * @param transaction The future option transaction.
   * @param curves The yield curve bundle.
   * @return The present value curve sensitivity.
   */
  public InterestRateCurveSensitivity presentValueCurveSensitivity(
      final BondFutureOptionPremiumTransaction transaction, final YieldCurveBundle curves) {
    ArgumentChecker.notNull(transaction, "transaction");
    ArgumentChecker.notNull(curves, "curves");
    final InterestRateCurveSensitivity premiumSensitivity =
        PVCSC.visit(transaction.getPremium(), curves);
    final InterestRateCurveSensitivity securitySensitivity =
        METHOD_SECURITY.priceCurveSensitivity(transaction.getUnderlyingOption(), curves);
    return premiumSensitivity.plus(
        securitySensitivity.multipliedBy(
            transaction.getQuantity()
                * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional()));
  }

  /**
   * Computes the present value gamma of a transaction. This is with respect to futures rate
   *
   * @param transaction The future option transaction.
   * @param blackData The curve and Black volatility data.
   * @return The gamma.
   */
  public double presentValueGamma(
      final BondFutureOptionPremiumTransaction transaction,
      final YieldCurveWithBlackCubeBundle blackData) {
    ArgumentChecker.notNull(transaction, "transaction");
    ArgumentChecker.notNull(blackData, "Black data");
    final double securityGamma =
        METHOD_SECURITY.optionPriceGamma(transaction.getUnderlyingOption(), blackData);
    final double txnGamma =
        securityGamma
            * transaction.getQuantity()
            * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional();
    return txnGamma;
  }

  /**
   * Computes the present value delta of a transaction. This is with respect to futures rate
   *
   * @param transaction The future option transaction.
   * @param blackData The curve and Black volatility data.
   * @return The delta.
   */
  public double presentValueDelta(
      final BondFutureOptionPremiumTransaction transaction,
      final YieldCurveWithBlackCubeBundle blackData) {
    ArgumentChecker.notNull(transaction, "transaction");
    ArgumentChecker.notNull(blackData, "Black data");
    final double securityDelta =
        METHOD_SECURITY.optionPriceDelta(transaction.getUnderlyingOption(), blackData);
    final double txnDelta =
        securityDelta
            * transaction.getQuantity()
            * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional();
    return txnDelta;
  }

  /**
   * Computes the present value vega of a transaction.
   *
   * @param transaction The future option transaction.
   * @param blackData The curve and Black volatility data.
   * @return The delta.
   */
  public double presentValueVega(
      final BondFutureOptionPremiumTransaction transaction,
      final YieldCurveWithBlackCubeBundle blackData) {
    ArgumentChecker.notNull(transaction, "transaction");
    ArgumentChecker.notNull(blackData, "Black data");
    final double securityVega =
        METHOD_SECURITY.optionPriceVega(transaction.getUnderlyingOption(), blackData);
    final double txnVega =
        securityVega
            * transaction.getQuantity()
            * transaction.getUnderlyingOption().getUnderlyingFuture().getNotional();
    return txnVega;
  }
}