/ Framework / OverwatchTranscript / MomentReferenceBuilder.cs
MomentReferenceBuilder.cs
  1  using Logging;
  2  using Newtonsoft.Json;
  3  
  4  namespace OverwatchTranscript
  5  {
  6      public class MomentReferenceBuilder
  7      {
  8          private const int MaxMomentsPerReference = 10000;
  9          private readonly ILog log;
 10          private readonly string workingDir;
 11  
 12          public MomentReferenceBuilder(ILog log, string workingDir)
 13          {
 14              this.log = log;
 15              this.workingDir = workingDir;
 16          }
 17  
 18          public OverwatchMomentReference[] Build(IFinalizedBucket[] finalizedBuckets)
 19          {
 20              var result = new List<OverwatchMomentReference>();
 21              var currentBuilder = new Builder(log, workingDir);
 22  
 23              var buckets = finalizedBuckets.ToList();
 24              log.Debug($"Building references for {buckets.Count} buckets.");
 25              while (buckets.Any())
 26              {
 27                  foreach (var b in buckets) b.Update();
 28  
 29                  buckets.RemoveAll(b => b.IsEmpty);
 30                  if (!buckets.Any()) break;
 31  
 32                  var earliestUtc = GetEarliestUtc(buckets);
 33                  if (earliestUtc == null) continue;
 34                  
 35                  var tops = CollectAllTopsForUtc(earliestUtc.Value, buckets);
 36                  var moment = ConvertTopsToMoment(tops);
 37                  currentBuilder.Add(moment);
 38                  if (currentBuilder.NumberOfMoments == MaxMomentsPerReference)
 39                  {
 40                      result.Add(currentBuilder.Build());
 41                      currentBuilder = new Builder(log, workingDir);
 42                  }
 43              }
 44  
 45              if (currentBuilder.NumberOfMoments > 0)
 46              {
 47                  result.Add(currentBuilder.Build());
 48              }
 49  
 50              return result.ToArray();
 51          }
 52  
 53          private OverwatchMoment ConvertTopsToMoment(List<BucketTop> tops)
 54          {
 55              var discintUtc = tops.Select(e => e.Utc).Distinct().ToArray();
 56              if (discintUtc.Length != 1) throw new Exception("UTC mixing in moment construction.");
 57  
 58              return new OverwatchMoment
 59              {
 60                  Utc = tops[0].Utc,
 61                  Events = tops.SelectMany(e => e.Events).ToArray()
 62              };
 63          }
 64  
 65          private List<BucketTop> CollectAllTopsForUtc(DateTime earliestUtc, List<IFinalizedBucket> buckets)
 66          {
 67              var result = new List<BucketTop>();
 68  
 69              foreach (var bucket in buckets)
 70              {
 71                  if (bucket.IsEmpty) continue;
 72  
 73                  var utc = bucket.SeeTopUtc();
 74                  if (utc == null) continue;
 75  
 76                  if (utc.Value == earliestUtc)
 77                  {
 78                      var top = bucket.TakeTop();
 79                      if (top == null) throw new Exception("top was null after top utc was not");
 80                      result.Add(top);
 81                  }
 82              }
 83  
 84              return result;
 85          }
 86  
 87          private DateTime? GetEarliestUtc(List<IFinalizedBucket> buckets)
 88          {
 89              var earliest = DateTime.MaxValue;
 90              foreach (var bucket in buckets)
 91              {
 92                  var utc = bucket.SeeTopUtc();
 93                  if (utc == null) return null;
 94  
 95                  if (utc.Value < earliest) earliest = utc.Value;
 96              }
 97              return earliest;
 98          }
 99  
100          public class Builder
101          {
102              private readonly ILog log;
103              private readonly string workingDir;
104              private OverwatchMomentReference reference;
105              private readonly ActionQueue queue = new ActionQueue();
106  
107              public Builder(ILog log, string workingDir)
108              {
109                  reference = new OverwatchMomentReference
110                  {
111                      MomentsFile = Guid.NewGuid().ToString(),
112                      EarliestUtc = DateTime.MaxValue,
113                      LatestUtc = DateTime.MinValue,
114                      NumberOfEvents = 0,
115                      NumberOfMoments = 0,
116                  };
117                  this.log = log;
118                  this.workingDir = workingDir;
119                  queue.Start();
120              }
121  
122              public int NumberOfMoments => reference.NumberOfMoments;
123  
124              public void Add(OverwatchMoment moment)
125              {
126                  if (moment.Utc < reference.EarliestUtc) reference.EarliestUtc = moment.Utc;
127                  if (moment.Utc > reference.LatestUtc) reference.LatestUtc = moment.Utc;
128                  reference.NumberOfMoments++;
129                  reference.NumberOfEvents += moment.Events.Length;
130  
131                  var filePath = Path.Combine(workingDir, reference.MomentsFile);
132  
133                  queue.Add(() =>
134                  {
135                      File.AppendAllLines(filePath, new[]
136                      {
137                          Json.Serialize(moment)
138                      });
139                  });
140              }
141  
142              public OverwatchMomentReference Build()
143              {
144                  queue.StopAndJoin();
145  
146                  log.Debug($"Created reference with {reference.NumberOfMoments} moments and {reference.NumberOfEvents} events...");
147                  var result = reference;
148                  reference = null!;
149                  return result;
150              }
151          }
152      }
153  }