protected IEnumerable<Model.GenericMatchCoupon> PersistCoupons(IEnumerable<Model.GenericMatchCoupon> coupons, DateTime couponDate, string tournament)
    {
      var ret = new List<Model.GenericMatchCoupon>();
      var matches = this.fixtureRepository.GetMatchesForTournament(couponDate, tournament);

      var sources = coupons.Select(c => c.Source).Distinct()
        .ToDictionary(s => s, s => this.bookmakerRepository.GetExternalSource(s));

      var bestOddsBookmaker = coupons.Select(c => c.Source).Distinct()
        .ToDictionary(s => s + " Best Available", s => this.bookmakerRepository.FindByName(s + " Best Available"));

      foreach (var coupon in coupons)
      {
        var retCoupon = Mapper.Map<Model.GenericMatchCoupon, Model.GenericMatchCoupon>(coupon);

        var teamPlayerA = this.fixtureRepository.GetTeamOrPlayerFromNameAndMaybeFirstName(coupon.TeamOrPlayerA, coupon.FirstNameA);
        var teamPlayerB = this.fixtureRepository.GetTeamOrPlayerFromNameAndMaybeFirstName(coupon.TeamOrPlayerB, coupon.FirstNameB);
        var persistedMatch = this.fixtureRepository.GetMatchFromTeamSelections(teamPlayerA, teamPlayerB, couponDate);
        
        if (persistedMatch == null)
          continue; //won't get added to the return list but needs some reporting to the client

        retCoupon.MatchId = persistedMatch.Id;

        var matchCouponURLs = this.bookmakerRepository
                                  .GetMatchCouponURLs(persistedMatch.Id)
                                  .Where(m => m.ExternalSource.Source == coupon.Source)
                                  .ToList();

        if (matchCouponURLs.Count == 0)
        {
          var newMatchCouponURL = new MatchCouponURL()
          {
            MatchID = persistedMatch.Id,
            ExternalSource = sources[coupon.Source],
            MatchCouponURLString = coupon.MatchURL == null ? string.Empty : coupon.MatchURL.ToString()
          };
          this.bookmakerRepository.AddMatchCouponURL(newMatchCouponURL);
        }
        else
        {
          matchCouponURLs.First(u => u.ExternalSource.Source == coupon.Source).MatchCouponURLString = coupon.MatchURL == null ? string.Empty : coupon.MatchURL.ToString();
        }

        var outcomeProbs = this.predicitonRepository
                               .GetMatchOutcomeProbabilities(persistedMatch.Id)
                               .ToList();
        
        if (outcomeProbs.Count == 0)
          continue; //need a better way to deal with this, some message passing back to the caller

        foreach (var outcome in coupon.HeadlineOdds.Keys)
        {
          var probForOutcome = outcomeProbs.First(p => p.MatchOutcomeID == (int)outcome);

          var outcomeOdds = this.bookmakerRepository
                                 .GetMatchOutcomeOdds(probForOutcome.Id)
                                 .ToList();

          var bestAvailableBookmaker = string.Format("{0} Best Available", coupon.Source);

          Func<MatchOutcomeOdd, Model.Outcome, bool> predicate =
            (o, oc) => o.ExternalSource.Source == coupon.Source &&
                       o.Bookmaker.BookmakerName == bestAvailableBookmaker &&
                       o.TimeStamp == outcomeOdds.Where(o2 => o.ExternalSource == o2.ExternalSource && o.Bookmaker == o2.Bookmaker)
                                                 .Select(x=>x.TimeStamp)
                                                 .DefaultIfEmpty(DateTime.MinValue)
                                                 .Max() &&
                       o.Odd == (decimal)coupon.HeadlineOdds[oc];

          var persisistedOdd = outcomeOdds.FirstOrDefault(x => predicate(x, outcome));

          if (persisistedOdd == null)
          {
            var matchOutcomeOdd = new MatchOutcomeOdd()
            {
              MatchOutcomeProbabilitiesInMatchID = probForOutcome.Id,
              Bookmaker = bestOddsBookmaker[bestAvailableBookmaker],
              ExternalSource = sources[coupon.Source],
              Odd = (decimal)coupon.HeadlineOdds[outcome],
              TimeStamp = coupon.LastChecked
            };

            this.bookmakerRepository.AddMatchOutcomeOdd(matchOutcomeOdd);
            retCoupon.HeadlineOdds.Add(outcome, coupon.HeadlineOdds[outcome]);
          }
          else
          {
            if (!retCoupon.HeadlineOdds.ContainsKey(outcome))
              retCoupon.HeadlineOdds.Add(outcome, (double)persisistedOdd.Odd);//dubious
          }
        }

        foreach (var outcome in coupon.ActualOdds.Keys)
        {
          var probForOutcome = outcomeProbs.First(p => p.MatchOutcomeID == (int)outcome);

          var outcomeOdds = this.bookmakerRepository
                                 .GetMatchOutcomeOdds(probForOutcome.Id)
                                 .ToList();

          var newActualOdds = new List<Model.GenericOdd>();
          foreach (var odd in coupon.ActualOdds[outcome])
          {
            Func<MatchOutcomeOdd, Model.Outcome, bool> predicate =
              (o, oc) => o.ExternalSource.Source == coupon.Source &&
                        (o.Bookmaker == null || o.Bookmaker.BookmakerName == odd.BookmakerName) &&
                         o.TimeStamp == outcomeOdds.Where(o2 => o.ExternalSource == o2.ExternalSource && (o.Bookmaker == null || o.Bookmaker == o2.Bookmaker))
                                                   .Select(x => x.TimeStamp)
                                                   .DefaultIfEmpty(DateTime.MinValue)
                                                   .Max() &&
                         o.Odd == (decimal)odd.DecimalOdds;

            var persisistedOdd = outcomeOdds.FirstOrDefault(x => predicate(x, outcome));

            if (persisistedOdd == null)
            {
              var matchOutcomeOdd = new MatchOutcomeOdd()
              {
                MatchOutcomeProbabilitiesInMatchID = probForOutcome.Id,
                Bookmaker = this.bookmakerRepository.FindByName(odd.BookmakerName),
                ExternalSource = sources[coupon.Source],
                Odd = (decimal)odd.DecimalOdds,
                TimeStamp = odd.TimeStamp,
                ClickThroughURL = odd.ClickThroughURL == null ? null : odd.ClickThroughURL.ToString()
              };

              this.bookmakerRepository.AddMatchOutcomeOdd(matchOutcomeOdd);
              if (!retCoupon.ActualOdds.ContainsKey(outcome))
                retCoupon.ActualOdds.Add(outcome, new List<Model.GenericOdd>());
              newActualOdds.Add(odd);
            }
            else
            {
              if (!retCoupon.ActualOdds.ContainsKey(outcome))
                retCoupon.ActualOdds.Add(outcome, new List<Model.GenericOdd>());

              var equivalentOdd = Mapper.Map<Model.GenericOdd, Model.GenericOdd>(odd);
              
              equivalentOdd.TimeStamp = persisistedOdd.TimeStamp;
              equivalentOdd.OddsBeforeCommission = (double)persisistedOdd.Odd;

              newActualOdds.Add(equivalentOdd);
            }
          }
          retCoupon.ActualOdds[outcome] = newActualOdds;
        }
        ret.Add(retCoupon);
      }
      this.fixtureRepository.SaveChanges();

      return ret;
    }
 public void AddMatchOutcomeOdd(MatchOutcomeOdd odd)
 {
   Add<MatchOutcomeOdd>(odd);
 }