using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using ReactiveUI; using DesertPaintCodex.Services; using DesertPaintCodex.Models; using DesertPaintCodex.Util; using DynamicData.Binding; namespace DesertPaintCodex.ViewModels { public class ExperimentLogViewModel : ViewModelBase { public ObservableCollection RemainingTests { get; } = new(); public ObservableCollection CompletedTests { get; } = new(); private int _selectedList = 0; public int SelectedList { get => _selectedList; private set => this.RaiseAndSetIfChanged(ref _selectedList, value); } private int _selectedRemainingTest = 0; public int SelectedRemainingTest { get => _selectedRemainingTest; private set => this.RaiseAndSetIfChanged(ref _selectedRemainingTest, value); } private int _selectedCompletedTest = 0; public int SelectedCompletedTest { get => _selectedCompletedTest; private set => this.RaiseAndSetIfChanged(ref _selectedCompletedTest, value); } private ReactionTestViewModel _testView; public ReactionTestViewModel TestView { get => _testView; private set => this.RaiseAndSetIfChanged(ref _testView, value); } private readonly List> _testLists = new(); public ExperimentLogViewModel() { PlayerProfile? profile = ProfileManager.CurrentProfile; Debug.Assert(profile != null); _testLists.Add(RemainingTests); _testLists.Add(CompletedTests); ReactionTestService.PopulateRemainingTests(RemainingTests); ReactionTestService.PopulateCompletedTests(CompletedTests); // If we have no remaining tests, switch to the completed list. _testView = new ReactionTestViewModel(); if (RemainingTests.Count > 0) { SelectedList = 0; _testView.ReactionTest = RemainingTests[0]; } else { SelectedList = 1; _testView.ReactionTest = CompletedTests[0]; } this.WhenAnyPropertyChanged(nameof(SelectedList), nameof(SelectedRemainingTest), nameof(SelectedCompletedTest)) .Subscribe(_ => _testView.ReactionTest = GetSelectedReactionTest()); _testView.ReactionTest = GetSelectedReactionTest(); TestView.SaveReaction.Subscribe(_ => OnSaveReaction()); TestView.ClearReaction.Subscribe(_ => OnClearReaction()); TestView.FinalizeTestResults.Subscribe(_ => OnReactionResults()); } private ReactionTest GetSelectedReactionTest() { int itemIndex = (SelectedList == 0) ? SelectedRemainingTest : SelectedCompletedTest; if (itemIndex < 0) return Constants.StubReactionTest; var list = _testLists[SelectedList]; return (itemIndex >= list.Count) ? Constants.StubReactionTest : list[itemIndex]; } #region Command Handlers private void OnSaveReaction() { ReactionTest test = TestView.ReactionTest; int oldPos = RemainingTests.IndexOf(test); // Move test to Completed Tests. InsertTestIntoList(test, CompletedTests); RemainingTests.RemoveAt(oldPos); // Select next Remaining Test. if (RemainingTests.Count == 0) return; SelectedRemainingTest = (oldPos == RemainingTests.Count) ? RemainingTests.Count - 1 : oldPos; // If we have just inserted our first completed test, select it. if (CompletedTests.Count == 1) { SelectedCompletedTest = 0; } } private void OnClearReaction() { int oldPos = 0; ReactionTest test = TestView.ReactionTest; if (RemainingTests.Contains(test)) { SelectedRemainingTest = ResortTestInList(test, RemainingTests); return; } oldPos = CompletedTests.IndexOf(test); // Move test to Remaining Tests. InsertTestIntoList(test, RemainingTests); CompletedTests.RemoveAt(oldPos); // Select next Completed Test. if (CompletedTests.Count == 0) return; SelectedCompletedTest = (oldPos == CompletedTests.Count) ? CompletedTests.Count - 1 : oldPos; // If we have just inserted our first remaining test, select it. if (RemainingTests.Count == 1) { SelectedRemainingTest = 0; } } private void OnReactionResults() { int newPos = ResortTestInList(TestView.ReactionTest, RemainingTests); if (newPos != SelectedRemainingTest) { SelectedRemainingTest = newPos; } } #endregion private static int ResortTestInList(ReactionTest test, IList list) { int oldPos = list.IndexOf(test); // If the item is first in the list or the previous item is valued < the test // And either the item is last in the list or the next item in the list is valued > than the item // Don't move it if (((oldPos == 0) || (test.CompareTo(list[oldPos - 1]) > 0)) && ((oldPos == list.Count - 1) || (test.CompareTo(list[oldPos + 1]) < 0))) return oldPos; // No need to move. list.RemoveAt(oldPos); return InsertTestIntoList(test, list); } private static int InsertTestIntoList(ReactionTest test, IList list) { int i; for (i = 0; i < list.Count; i++) { if (test.CompareTo(list[i]) < 0) break; } list.Insert(i, test); return i; } } }