Files @ 6919080271d5
Branch filter:

Location: ATITD-Tools/Desert-Paint-Codex/ViewModels/ExperimentLogViewModel.cs

Malkyne
Implemented a system that will allow ingredients to be safely renamed, in the
future, without invalidating profiles. Standardized to PP's "FalconBait."
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<ReactionTest> RemainingTests { get; } = new();
        public ObservableCollection<ReactionTest> 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<ObservableCollection<ReactionTest>> _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<ReactionTest> list)
        {
            int oldPos = list.IndexOf(test);

            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<ReactionTest> list)
        {
            int i;
            for (i = 0; i < list.Count; i++)
            {
                if (test.CompareTo(list[i]) < 0) break;
            }
            list.Insert(i, test);
            return i;
        }
    }
}