Files @ d036e3a47323
Branch filter:

Location: ATITD-Tools/Desert-Paint-Codex/Models/ReactionTest.cs - annotation

Malkyne
Merge
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
f1d4569c0495
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
40eaee10ae56
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
f1d4569c0495
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
f419334a476f
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f419334a476f
f419334a476f
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
f419334a476f
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
d7b40cfbe0f3
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
f1d4569c0495
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
40eaee10ae56
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
40eaee10ae56
40eaee10ae56
f419334a476f
f419334a476f
f419334a476f
f419334a476f
f419334a476f
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f1d4569c0495
f419334a476f
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
40eaee10ae56
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using DesertPaintCodex.Services;
using JetBrains.Annotations;

namespace DesertPaintCodex.Models
{
    public class ReactionTest : INotifyPropertyChanged, IProgress<float>, IComparable<ReactionTest>
    {
        public enum TestState
        {
            Unset = -1,
            Untested,
            Scanning,
            LabNotFound,
            ClippedResult,
            GoodResult,
            Saved
        }

        public Reagent Reagent1 { get; }
        public Reagent Reagent2 { get; }

        private Reagent? _bufferReagentFirst;
        public Reagent? BufferReagentFirst
        {
            get => _bufferReagentFirst;
            set
            {
                if (_bufferReagentFirst == value) return;
                _bufferReagentFirst = value;
                
                UpdateRecipe();
                
                NotifyPropertyChanged(nameof(BufferReagentFirst));
                NotifyPropertyChanged(nameof(CanScan));
            }
        }
        
        private Reagent? _bufferReagentLast;
        public Reagent? BufferReagentLast
        {
            get => _bufferReagentLast;
            set
            {
                if (_bufferReagentLast == value) return;
                _bufferReagentLast = value;

                UpdateRecipe();

                NotifyPropertyChanged(nameof(BufferReagentLast));
                NotifyPropertyChanged(nameof(CanScan));
            }
        }

        private bool _useFirstBuffer;

        public bool UseFirstBuffer
        {
            get => _useFirstBuffer;
            set
            {
                _useFirstBuffer = value;
                UpdateRecipe();
                NotifyPropertyChanged(nameof(UseFirstBuffer));
                NotifyPropertyChanged(nameof(CanScan));
                NotifyPropertyChanged(nameof(ShowFirstBuffer));
                NotifyPropertyChanged(nameof(ShowLastBuffer));
            }
        }

        private ClipType _clipType;
        public ClipType Clipped {
            get => _clipType;
            set
            {
                _clipType = value;
                NotifyPropertyChanged(nameof(Clipped));
            }
        }

        public bool IsAllCatalysts { get; }

        private Reaction? _reaction;
        public Reaction? Reaction { get => _reaction; set { _reaction = value; NotifyPropertyChanged(nameof(Reaction)); } }

        private Reaction? _badReaction;
        public Reaction? BadReaction { get => _badReaction; set { _badReaction = value; NotifyPropertyChanged(nameof(BadReaction)); } }

        private int _scanProgress;
        public int ScanProgress { get => _scanProgress; set { _scanProgress = value; NotifyPropertyChanged(nameof(ScanProgress)); } }
        
        
        private TestState _state;

        public TestState State
        {
            get => _state;
            set
            {
                _state = value;
                NotifyPropertyChanged(nameof(State));
                NotifyPropertyChanged(nameof(Requires3Way));
                NotifyPropertyChanged(nameof(CanScan));
                NotifyPropertyChanged(nameof(IsScanning));
                NotifyPropertyChanged(nameof(HasResults));
                NotifyPropertyChanged(nameof(HasReaction));
                NotifyPropertyChanged(nameof(CanClear));
                NotifyPropertyChanged(nameof(CanSave));
                NotifyPropertyChanged(nameof(NoLab));
                NotifyPropertyChanged(nameof(CanPickBuffer));
                NotifyPropertyChanged(nameof(ShowFirstBuffer));
                NotifyPropertyChanged(nameof(ShowLastBuffer));
            }
        }

        public bool Requires3Way =>
            (State == TestState.ClippedResult) || IsAllCatalysts;

        public bool CanScan => (State is TestState.Untested or TestState.LabNotFound) || ((State == TestState.ClippedResult) && BufferIsSelected);
        
        public bool IsScanning => State == TestState.Scanning;

        public bool HasResults => (ObservedColor != null) && (State is TestState.ClippedResult or TestState.GoodResult or TestState.LabNotFound);

        public bool HasReaction => State is TestState.ClippedResult or TestState.GoodResult or TestState.Saved;
        
        public bool CanClear => State is TestState.ClippedResult or TestState.GoodResult or TestState.Saved;
        
        public bool CanSave => State == TestState.GoodResult;

        public bool NoLab => State == TestState.LabNotFound;

        public bool CanPickBuffer => (State == TestState.ClippedResult) || IsAllCatalysts;

        public bool ShowFirstBuffer => Requires3Way && UseFirstBuffer;

        public bool ShowLastBuffer => Requires3Way && !UseFirstBuffer;


        private PaintColor? _hypotheticalColor;
        public PaintColor? HypotheticalColor { get => _hypotheticalColor; set { _hypotheticalColor = value; NotifyPropertyChanged(nameof(HypotheticalColor)); } }

        private PaintColor? _observedColor;
        public PaintColor? ObservedColor { get => _observedColor; set { _observedColor = value; NotifyPropertyChanged(nameof(ObservedColor)); } }

        private readonly PaintRecipe _recipe = new();

        public bool IsStub { get; }


        public ReactionTest(Reagent reagent1, Reagent reagent2, Reaction? reaction, ClipType clipType, bool isStub = false)
        {
            Reagent1 = reagent1;
            Reagent2 = reagent2;
            UseFirstBuffer = true;
            IsAllCatalysts = reagent1.IsCatalyst && reagent2.IsCatalyst;
            Clipped = clipType;
            Reaction = reaction;
            State = (reaction != null) ? TestState.Saved :
                (clipType == ClipType.None) ? TestState.Untested : TestState.ClippedResult;
            _recipe.AddReagent(reagent1.Name);
            _recipe.AddReagent(reagent2.Name);
            IsStub = isStub;
            UpdateHypotheticalColor();
        }

        
        #region Actions
        public async Task StartScan()
        {
            Clipped = ClipType.None;
            ScanProgress = 0;
            Reaction = null;
            BadReaction = null;
            State = TestState.Scanning;
            bool foundLab = await ReactionScannerService.Instance.CaptureReactionAsync(this);
            if (foundLab)
            {
                ObservedColor = ReactionScannerService.Instance.RecordedColor;
                if (_observedColor != null)
                {
                    // Handle the normal case.
                    Clipped = _observedColor.Red switch
                        {
                            0   => ClipType.RedLow,
                            255 => ClipType.RedHigh,
                            _   => ClipType.None
                        }
                        | _observedColor.Green switch
                        {
                            0   => ClipType.GreenLow,
                            255 => ClipType.GreenHigh,
                            _   => ClipType.None
                        }
                        | _observedColor.Blue switch
                        {
                            0   => ClipType.BlueLow,
                            255 => ClipType.BlueHigh,
                            _   => ClipType.None
                        };

                    if (Clipped == ClipType.None)
                    {
                        State = TestState.GoodResult;
                        Reaction = CalculateReaction();
                    }
                    else
                    {
                        State = TestState.ClippedResult;
                        BadReaction = CalculateReaction();
                    }
                    
                    PlayerProfile? profile = ProfileManager.CurrentProfile;
                    profile?.SetPairClipStatus(Reagent1, Reagent2, Clipped);
                }
            }
            else
            {
                Debug.WriteLine("ERROR: Lab UI not found.");
                State = TestState.LabNotFound;
            }
        }

        public void CancelScan()
        {
            ReactionScannerService.Instance.CancelScan();
            State = TestState.Untested;
        }

        public void MarkInert()
        {
            Reaction = new Reaction(0, 0, 0);
            State = TestState.GoodResult;
        }

        public void ClearReaction()
        {
            PlayerProfile? profile = ProfileManager.CurrentProfile;
            if (profile == null) return;
            profile.Reactions.Remove(Reagent1, Reagent2);
            profile.SetPairClipStatus(Reagent1, Reagent2, ClipType.None);
            if (State == TestState.Saved)
            {
                profile.Save();
            }
            
            Reaction = null;
            BadReaction = null;
            Clipped = ClipType.None;
            State = TestState.Untested;
        }

        public void SaveReaction()
        {
            PlayerProfile? profile = ProfileManager.CurrentProfile;
            if (profile == null) return;
            profile.Reactions.Set(Reagent1, Reagent2, Reaction);
            profile.Save();
            State = TestState.Saved;
        }

        #endregion
        
        #region Internals

        private Reaction? CalculateReaction()
        {
            if (ProfileManager.CurrentProfile == null) return null;
            if (HypotheticalColor == null) return null;
            if (ObservedColor == null) return null;

            if (_useFirstBuffer)
            {
                if (BufferReagentFirst != null)
                {
                    return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor,
                        ObservedColor, BufferReagentFirst, Reagent1, Reagent2, true);
                }
            }
            else
            {
                if (BufferReagentLast != null)
                {
                    return ReactionScannerService.Calculate3WayReaction(ProfileManager.CurrentProfile, HypotheticalColor,
                        ObservedColor, Reagent1, Reagent2, BufferReagentLast, false);
                }
            }
            
            return ReactionScannerService.CalculateReaction(HypotheticalColor, ObservedColor);
        }


        private void UpdateHypotheticalColor()
        {
            HypotheticalColor = null;
            HypotheticalColor = (IsAllCatalysts && !BufferIsSelected) ? null : _recipe.BaseColor;
        }

        private bool BufferIsSelected => UseFirstBuffer ? (BufferReagentFirst != null) : (BufferReagentLast != null);


        private void UpdateRecipe()
        {
            _recipe.Clear();
            if (_useFirstBuffer)
            {
                if (_bufferReagentFirst == null)
                {
                    _recipe.AddReagent(Reagent1.Name);
                    _recipe.AddReagent(Reagent2.Name);
                }
                else
                {
                    _recipe.AddReagent(_bufferReagentFirst.Name);
                    _recipe.AddReagent(Reagent1.Name);
                    _recipe.AddReagent(Reagent2.Name);
                } 
            }
            else
            {
                if (_bufferReagentLast == null)
                {
                    _recipe.AddReagent(Reagent1.Name);
                    _recipe.AddReagent(Reagent2.Name);
                }
                else
                {
                    _recipe.AddReagent(Reagent1.Name);
                    _recipe.AddReagent(Reagent2.Name);
                    _recipe.AddReagent(_bufferReagentLast.Name);
                }                
            }
            
            UpdateHypotheticalColor();
        }
        
        #endregion
        
        

        #region Interface Implementations
        
        public event PropertyChangedEventHandler? PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void NotifyPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void Report(float value)
        {
            ScanProgress = (int)Math.Round(value * 100);
        }
        
        #endregion

        public int CompareTo(ReactionTest? other)
        {
            if (other == null) return 1;
            
            if (Clipped == ClipType.None)
            {
                if (other.Clipped != ClipType.None) return -1;
            }
            else if (other.Clipped == ClipType.None) return 1;

            if (IsAllCatalysts)
            {
                if (!other.IsAllCatalysts) return 1;
            }
            else if (other.IsAllCatalysts) return -1;
            
            return string.CompareOrdinal(Reagent1.Name, other.Reagent1.Name) switch
            {
                < 0 => -1,
                > 0 => 1,
                _   => string.CompareOrdinal(Reagent2.Name, other.Reagent2.Name)
            };
        }
    }
}