internal static BondSpreadResult GetSpreads(
      OTBond bondType_, 
      NodaTime.LocalDate maturity_, 
      NodaTime.LocalDate issueDate_, 
      NodaTime.LocalDate firstCouponDate_,
      QuoteValueType priceType_, 
      double priceValue_, 
      double coupon_, 
      CurveMappings.Mapping pricingSetup_, 
      DateTime asOf_, 
      CarbonClient client_, 
      ThrowBehavior behavior_=ThrowBehavior.DontThrow )
    {
      var ret = new BondSpreadResult();
      ret.Spreads = new BondSpread();

      try
      {
        SLog.log.DebugFormat(
          "Calling PriceBondAsync with params: baseBondID={0} issueDate={1} maturity={2} coupon={3} asOf={4} pricingSetup={5} stubDate={6} priceValue={7}, priceType_={8}",
          ((long) bondType_).ToString(), issueDate_, maturity_, coupon_, asOf_, pricingSetup_.OT_BondPricingSetup,
          firstCouponDate_, priceValue_, priceType_);


        switch (priceType_)
        {
          case QuoteValueType.Price:
            {
              ret.Price = priceValue_;

              var result = client_.PriceBondAsync(
                baseBondId: (long)bondType_,
                issueDate: issueDate_,
                maturityDate: maturity_,
                coupon: coupon_,
                asof: asOf_,
                pricingSetup: pricingSetup_.OT_BondPricingSetup,
                quantity: 100000d,
                stubDate: firstCouponDate_,
                price: ret.Price.Value).Result;

              ret.Spreads.Spread = -result.Results[ServiceConstants.KEY_ASW_YY];
              ret.Spreads.TrueSpread = -result.Results[ServiceConstants.KEY_ZSpread];
              ret.Spreads.Yield = result.Results[ServiceConstants.KEY_Yield];
            }

            break;
          case QuoteValueType.Yield:
            {
              var result = client_.GetBondPriceFromYieldAsync(
                baseBondId: (long)bondType_,
                issueDate: issueDate_,
                maturityDate: maturity_,
                coupon: coupon_,
                asof: asOf_,
                pricingSetup: pricingSetup_.OT_BondPricingSetup,
                quantity: 100000d,
                yield: priceValue_,
                stubDate: firstCouponDate_).Result;

              ret.Price = result.Results[ServiceConstants.KEY_Price];

              ret.Spreads.Spread = -result.Results[ServiceConstants.KEY_ASW_YY];
              ret.Spreads.TrueSpread = -result.Results[ServiceConstants.KEY_ZSpread];
              ret.Spreads.Yield = priceValue_;
            }


            break;
          default:
            SLog.log.ErrorFormat("Cannot call Carbon to get spreads with priceType {0}", priceType_);
            break;
        }

        SLog.log.DebugFormat(
          "Calling PriceSwapAsync with params: curveName={0} startDate={1} endDate={2} asOf={3} pricingSetup={4}",
          pricingSetup_.OT_Swap, issueDate_, maturity_, asOf_, pricingSetup_.OT_BondPricingSetup);

        var mmsResult = client_.PriceSwapAsync(
          curveName: pricingSetup_.OT_Swap,
          startDate: asOf_.ToNodaLocalDate(),
          endDate: maturity_,
          asof: asOf_,
          pricingSetup: pricingSetup_.OT_BondPricingSetup).Result;

        ret.Spreads.MMS = mmsResult.Results[ServiceConstants.KEY_MMS];

        SLog.log.DebugFormat("Result: y={0} m={1} s={2} t={3}", ret.Spreads.Yield, ret.Spreads.MMS, ret.Spreads.Spread, ret.Spreads.TrueSpread);

        postProcess(bondType_, ret.Spreads, pricingSetup_);
      }
      catch (Exception ex_)
      {
        Exceptions.Rethrow("GetCurves", behavior_, ex_);
      }

      return ret;
    }
    private static void postProcess(OTBond bond_, BondSpread spread_, CurveMappings.Mapping pricingSetup_)
    {
      if(!string.IsNullOrEmpty(pricingSetup_.OT_BondPricingSetup))
        switch (pricingSetup_.OT_BondPricingSetup)
        {
          case "Sym_OIS":
            {
              // 26th November 2015:
              // we've realised that the ASW returned by OT is always that over LIBOR, regardless of what pricing setup is passed in.
              // As such, we're calculating the spread by taking the difference ourselves.  Obviously easy, but it maintains the need to have
              // 2 calls to the server (the second being to get the MMS).  Obviously, if the ASW were correct, then the MMS could be calculated using 
              // yield and ASW, and the second call could be removed.  Alas, not yet...

              spread_.Spread = spread_.MMS - spread_.Yield;

            }
            break;
        }
    }