public ClassSetupForm()
        {
            InitializeComponent();

            allChildControls = new Lazy<IEnumerable<Control>>(() => this.GetAllChildControlsRecursive().ToList());
            originalVersion = CacheManager.DefaultInstance.ActiveModel;
        }
        public int?GetBestNextCompetitorNumberOrNull(int?currentCompetitorNumber)
        {
            if (currentCompetitorNumber != null)
            {
                CompetitionClassModel snapshotSorted = GetSortedAscendingByCompetitorNumber();

                // @formatter:keep_existing_linebreaks true

                CompetitionRunResult?firstUncompletedCompetitorAfterCurrent = snapshotSorted.Results
                                                                              .SkipWhile(r => r.Competitor.Number != currentCompetitorNumber.Value)
                                                                              .Skip(1)
                                                                              .SkipWhile(r => r.HasCompleted)
                                                                              .FirstOrDefault();

                // @formatter:keep_existing_linebreaks restore

                if (firstUncompletedCompetitorAfterCurrent != null)
                {
                    return(firstUncompletedCompetitorAfterCurrent.Competitor.Number);
                }

                CompetitionRunResult?firstUncompletedCompetitor =
                    snapshotSorted.Results.FirstOrDefault(r => !r.HasCompleted && r.Competitor.Number != currentCompetitorNumber.Value);

                return(firstUncompletedCompetitor?.Competitor.Number);
            }

            return(null);
        }
        public int GetBestStartingCompetitorNumber()
        {
            AssertCompetitorsExist();

            CompetitionClassModel snapshotSorted = GetSortedAscendingByCompetitorNumber();

            CompetitionRunResult?lastCompleted = snapshotSorted.GetLastCompletedOrNull();

            if (lastCompleted != null)
            {
                // @formatter:keep_existing_linebreaks true

                CompetitionRunResult?nextUncompletedCompetitor = snapshotSorted.Results
                                                                 .SkipWhile(r => r.Competitor.Number != lastCompleted.Competitor.Number)
                                                                 .Skip(1)
                                                                 .SkipWhile(r => r.HasCompleted)
                                                                 .FirstOrDefault();

                // @formatter:keep_existing_linebreaks restore

                if (nextUncompletedCompetitor != null)
                {
                    return(nextUncompletedCompetitor.Competitor.Number);
                }
            }

            CompetitionRunResult?firstUncompletedCompetitor = snapshotSorted.Results.FirstOrDefault(r => !r.HasCompleted);

            return(firstUncompletedCompetitor?.Competitor.Number ?? snapshotSorted.Results.First().Competitor.Number);
        }
        public void RunExploded()
        {
            // Arrange
            var runCompletionScenarios = new List<OrderingScenario>
            {
                // Bits: X HasFinished, X IsEliminated, Y HasFinished, Y IsEliminated
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 0), OrderExpect.IsEven),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 1), OrderExpect.IsEven),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 0), OrderExpect.IsEven),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 1), OrderExpect.IsEven)
            };

            var penaltyOverrunScenarios = new List<OrderingScenario>
            {
                // Bits: PenaltyTime X > Y, OverrunTime X > Y, PenaltyTime Y > X, OverrunTime Y > X
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 0), OrderExpect.IsEven),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 1), OrderExpect.DoNotCare)
            };

            var finishNumberScenarios = new List<OrderingScenario>
            {
                // Bits: FinishTime X > Y, CompetitorNumber X > Y, FinishTime Y > X, CompetitorNumber Y > X
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 1), OrderExpect.DoNotCare)
            };

            IEnumerable<OrderingScenario> exploded = ExplodeCombinations(runCompletionScenarios, penaltyOverrunScenarios,
                finishNumberScenarios);

            CompetitionClassModel model = new CompetitionClassModel()
                .ChangeClassInfo(new CompetitionClassInfo()
                    .ChangeStandardCourseTime(StandardCourseTime));
            var comparer = new CompetitionRunResultRankingComparer(model, RankingComparisonMode.Regular);

            foreach (OrderingScenario scenario in exploded)
            {
                bool xHasFinished = scenario[0];
                bool xIsEliminated = scenario[1];
                bool yHasFinished = scenario[2];
                bool yIsEliminated = scenario[3];
                bool xPenaltyTimeIsGreater = scenario[4];
                bool xOverrunTimeIsGreater = scenario[5];
                bool yPenaltyTimeIsGreater = scenario[6];
                bool yOverrunTimeIsGreater = scenario[7];
                bool xFinishTimeIsGreater = scenario[8];
                bool xCompetitorNumberIsGreater = scenario[9];
                bool yFinishTimeIsGreater = scenario[10];
                bool yCompetitorNumberIsGreater = scenario[11];

                CompetitionRunResult xCompetitor = CreateCompetitorFor("X", xHasFinished, xIsEliminated, yHasFinished,
                    xPenaltyTimeIsGreater, xOverrunTimeIsGreater, yPenaltyTimeIsGreater, yOverrunTimeIsGreater,
                    xFinishTimeIsGreater, xCompetitorNumberIsGreater, yFinishTimeIsGreater, yCompetitorNumberIsGreater);
                CompetitionRunResult yCompetitor = CreateCompetitorFor("Y", yHasFinished, yIsEliminated, xHasFinished,
                    yPenaltyTimeIsGreater, yOverrunTimeIsGreater, xPenaltyTimeIsGreater, xOverrunTimeIsGreater,
                    yFinishTimeIsGreater, yCompetitorNumberIsGreater, xFinishTimeIsGreater, xCompetitorNumberIsGreater);

                AssertCompetitorsAreCompatibleWithProposedScenario(xCompetitor, yCompetitor, scenario, model);

                // Act
                int result = comparer.Compare(xCompetitor, yCompetitor);

                // Assert
                OrderExpect actual = TranslateComparerResult(result);
                actual.Should().Be(scenario.Result, scenario.ToString());
            }
        }
        private void PersistRunResultToCache()
        {
            try
            {
                if (currentCompetitorNumber != null)
                {
                    Competitor existingCompetitor =
                        modelSnapshot.GetRunResultFor(currentCompetitorNumber.Value).Competitor;
                    CompetitionRunResult newRunResult = runData.ToRunResultFor(existingCompetitor);
                    CompetitionClassModel newSnapshot =
                        modelSnapshot.ChangeRunResult(newRunResult)
                            .SafeChangeLastCompletedCompetitorNumber(existingCompetitor.Number)
                            .RecalculatePlacements();

                    modelSnapshot = CacheManager.DefaultInstance.ReplaceModel(newSnapshot, modelSnapshot);
                    AutoExportRunResults();
                }
            }
            catch (Exception ex)
            {
                int competitorNumber = AssertCurrentCompetitorNumberNotNull();

                string message = "Failed to save run results. Please write these down manually, " +
                    $"or press Ctrl+C to copy to clipboard.\n\n{runData.GetMessageFor(competitorNumber)}\n";
                Log.Error(message, ex);
                var exception = new Exception(message, ex);
                UnknownErrorDuringSave?.Invoke(this, new EventArgs<Exception>(exception));
            }
        }
        public void StartClass([NotNull] CompetitionClassRequirements requirements)
        {
            Guard.NotNull(requirements, nameof(requirements));

            Log.Debug("Entering StartClass.");

            ExecuteExclusiveIfStateIn(CompetitionClassState.Offline, () =>
            {
                modelSnapshot = CacheManager.DefaultInstance.ActiveModel;
                currentCompetitorNumber = modelSnapshot.GetBestStartingCompetitorNumber();

                nextCompetitorNumberTyped = null;
                nextCompetitorNumberGenerated = modelSnapshot.GetBestNextCompetitorNumberOrNull(currentCompetitorNumber);

                classRequirements = requirements;
                runData.Reset(false);

                SetState(CompetitionClassState.SetupCompleted);

                using (var collector = new VisualizationUpdateCollector(visualizer))
                {
                    collector.Include(VisualizationChangeFactory.ClearAll());
                    collector.Include(new ClassInfoUpdate(modelSnapshot.ClassInfo));

                    UpdateCurrentCompetitorVisualization(collector);
                    UpdateNextCompetitorVisualization(collector);

                    CompetitionRunResult previousCompetitorOrNull = modelSnapshot.GetLastCompletedOrNull();
                    collector.Include(new PreviousCompetitorRunUpdate(previousCompetitorOrNull));

                    IReadOnlyCollection<CompetitionRunResult> rankings =
                        modelSnapshot.FilterCompletedAndSortedAscendingByPlacement().Results;
                    collector.Include(new RankingsUpdate(rankings));
                }
            });
        }
        public string TryUpdateRunResult([NotNull] CompetitionRunResult originalRunVersion,
            [NotNull] CompetitionRunResult newRunVersion)
        {
            Guard.NotNull(originalRunVersion, nameof(originalRunVersion));
            Guard.NotNull(newRunVersion, nameof(newRunVersion));

            string result = null;
            ExecuteExclusiveIfStateIn(AllStates, () =>
            {
                // Assumptions:
                // 1] When class is started, underlying cache can never be changed from the outside.
                // 2] When no run active (state Offline), we are not in control of underlying cache 
                //    (so snapshot field should not be read because it is likely outdated).

                CompetitionClassModel activeModelVersion = classState == CompetitionClassState.Offline
                    ? CacheManager.DefaultInstance.ActiveModel
                    : modelSnapshot;
                CompetitionRunResult activeRunVersion =
                    activeModelVersion.GetRunResultOrNull(originalRunVersion.Competitor.Number);

                if (activeRunVersion == null)
                {
                    result = "Competitor not found";
                    return;
                }

                if (!CompetitionRunResult.AreEquivalent(activeRunVersion, originalRunVersion))
                {
                    result = "Competitor changed in-between";
                    return;
                }

                if (classState != CompetitionClassState.Offline &&
                    currentCompetitorNumber == newRunVersion.Competitor.Number)
                {
                    result = "Competitor is running";
                    return;
                }

                CompetitionClassModel newSnapshot =
                    activeModelVersion.ChangeRunResult(newRunVersion).RecalculatePlacements();

                modelSnapshot = CacheManager.DefaultInstance.ReplaceModel(newSnapshot, activeModelVersion);
                AutoExportRunResults();

                if (classState != CompetitionClassState.Offline)
                {
                    using (var collector = new VisualizationUpdateCollector(visualizer))
                    {
                        IReadOnlyCollection<CompetitionRunResult> rankings =
                            modelSnapshot.FilterCompletedAndSortedAscendingByPlacement().Results;
                        collector.Include(new RankingsUpdate(rankings));

                        if (newRunVersion.Competitor.Number == modelSnapshot.LastCompletedCompetitorNumber)
                        {
                            CompetitionRunResult previousCompetitorOrNull = modelSnapshot.GetLastCompletedOrNull();
                            collector.Include(new PreviousCompetitorRunUpdate(previousCompetitorOrNull));
                        }
                    }
                }
            });
            return result;
        }
        public CompetitionClassController([NotNull] Func<NetworkHealthReport> healthNeededCallback,
            [NotNull] ClockSynchronizationMonitor clockSynchronizationMonitor,
            [NotNull] ICompetitionRunVisualizer runVisualizer)
        {
            Guard.NotNull(healthNeededCallback, nameof(healthNeededCallback));
            Guard.NotNull(clockSynchronizationMonitor, nameof(clockSynchronizationMonitor));
            Guard.NotNull(runVisualizer, nameof(runVisualizer));

            networkHealthNeededCallback = healthNeededCallback;
            synchronizationMonitor = clockSynchronizationMonitor;
            visualizer = runVisualizer;

            clockSynchronizationMonitor.SyncRecommended += ClockSynchronizationMonitorOnSyncRecommended;
            clockSynchronizationMonitor.SyncRequired += ClockSynchronizationMonitorOnSyncRequired;
            clockSynchronizationMonitor.SyncCompleted += ClockSynchronizationMonitorOnSyncCompleted;

            runData.EliminationTracker.EliminationChanged += EliminationTrackerOnEliminationChanged;
            runData.EliminationTracker.RefusalCountChanged += EliminationTrackerOnRefusalCountChanged;

            modelSnapshot = CacheManager.DefaultInstance.ActiveModel;
        }
 private void ApplyModelChange([NotNull] CompetitionClassModel newVersion)
 {
     originalVersion = CacheManager.DefaultInstance.ReplaceModel(newVersion, originalVersion);
 }
 public CompetitorAssessmentCalculator([NotNull] CompetitionRunResult competitor,
     [NotNull] CompetitionClassModel modelSnapshot)
 {
     Guard.NotNull(competitor, nameof(competitor));
     Guard.NotNull(modelSnapshot, nameof(modelSnapshot));
     this.competitor = competitor;
     this.modelSnapshot = modelSnapshot;
 }
        public void PenaltyOverrun2()
        {
            // Arrange
            var scenarios = new List<OrderingScenario>
            {
                // Bits: PenaltyTime X > Y, OverrunTime X > Y, PenaltyTime Y > X, OverrunTime Y > X
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 0), OrderExpect.IsEven),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 0, 1, 1), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 0), OrderExpect.WinnerIsX),
                new OrderingScenario(4, OrderingScenario.FromBits(0, 1, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 0, 1), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 0, 1, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 0), OrderExpect.WinnerIsY),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 0, 1), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 0), OrderExpect.DoNotCare),
                new OrderingScenario(4, OrderingScenario.FromBits(1, 1, 1, 1), OrderExpect.DoNotCare)
            };

            CompetitionClassModel model = new CompetitionClassModel()
                .ChangeClassInfo(new CompetitionClassInfo()
                    .ChangeStandardCourseTime(StandardCourseTime));
            var comparer = new CompetitionRunResultRankingComparer(model, RankingComparisonMode.OnlyPhasePenaltyOverrun);

            foreach (OrderingScenario scenario in scenarios.Where(s => s.Result != OrderExpect.DoNotCare))
            {
                CompetitionRunResult xCompetitor = CreateCompetitorForPenaltyOverrun(scenario[0], scenario[1]);
                CompetitionRunResult yCompetitor = CreateCompetitorForPenaltyOverrun(scenario[2], scenario[3]);

                AssertCompetitorsAreCompatibleWithProposedScenario(xCompetitor, yCompetitor, scenario, model);

                // Act
                int result = comparer.Compare(xCompetitor, yCompetitor);

                // Assert
                OrderExpect actual = TranslateComparerResult(result);
                actual.Should().Be(scenario.Result, scenario.ToString());
            }
        }
        private void OkButton_Click([CanBeNull] object sender, [NotNull] EventArgs e)
        {
            CompetitionClassModel newVersion =
                originalVersion.ChangeRunResults(runResultsGrid.DataSource).RecalculatePlacements();

            originalVersion = CacheManager.DefaultInstance.ReplaceModel(newVersion, originalVersion);
            DialogResult = DialogResult.OK;
        }
        public ClassResultsForm()
        {
            InitializeComponent();

            originalVersion = CacheManager.DefaultInstance.ActiveModel;
        }