using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using Avalonia; using Avalonia.Input.Platform; using DesertPaintCodex.Models; using DesertPaintCodex.Services; using DynamicData; using DynamicData.Binding; using ReactiveUI; namespace DesertPaintCodex.ViewModels { public class SimulatorViewModel : ViewModelBase { private PaintColor? _paintColor; public PaintColor? PaintColor { get => _paintColor; set => this.RaiseAndSetIfChanged(ref _paintColor, value); } private bool _hasMissingReactions; public bool HasMissingReactions { get => _hasMissingReactions; set => this.RaiseAndSetIfChanged(ref _hasMissingReactions, value); } private bool _isGoodRecipe; public bool IsGoodRecipe { get => _isGoodRecipe; set => this.RaiseAndSetIfChanged(ref _isGoodRecipe, value); } public ObservableCollection Reagents { get; } = new(); public ObservableCollection ActiveReagents { get; } = new(); public ObservableCollection RecipeItems { get; } = new(); private readonly PaintRecipe _currentRecipe = new(); public SimulatorViewModel() { List reagentNames = ReagentService.Names; for (int i = 0; i < reagentNames.Count; i++) { Reagents.Add(ReagentService.GetReagent(reagentNames[i])); } ActiveReagents.CollectionChanged += OnActiveReagentsChanged; RecipeItems .ToObservableChangeSet() .AutoRefresh(item => item.Quantity) .Subscribe(_ => Refresh()); } public async void CopyToClipboard() { IClipboard clipboard = Application.Current.Clipboard; await clipboard.SetTextAsync(_currentRecipe.ToString()); } public void MoveItemUp(RecipeItem item) { int pos = RecipeItems.IndexOf(item); if (pos <= 0) return; RecipeItems.RemoveAt(pos); RecipeItems.Insert(pos - 1, item); Refresh(); } public void MoveItemDown(RecipeItem item) { int pos = RecipeItems.IndexOf(item); if ((pos < 0) || (pos >= RecipeItems.Count - 1)) return; RecipeItems.RemoveAt(pos); RecipeItems.Insert(pos + 1, item); Refresh(); } private void UpdateRecipe() { _currentRecipe.Clear(); foreach (RecipeItem entry in RecipeItems) { if (!entry.Unused) { _currentRecipe.AddReagent(entry.Reagent.Name, entry.Quantity); } } PaintColor = null; // TODO: Find a better way to kick the paint swatch when reassigning color from the same ref. PaintColor = _currentRecipe.ReactedColor; HasMissingReactions = _currentRecipe.HasMissingReactions(); IsGoodRecipe = !HasMissingReactions && _currentRecipe.IsValidForConcentration(10); } private void OnActiveReagentsChanged(object? sender, NotifyCollectionChangedEventArgs e) { HashSet unmatchedReagents = new(ActiveReagents); for (int i = RecipeItems.Count - 1; i >= 0; i--) { bool found = false; foreach (Reagent reagent in ActiveReagents) { if (reagent == RecipeItems[i].Reagent) { unmatchedReagents.Remove(reagent); found = true; break; } } if (!found) RecipeItems.RemoveAt(i); } foreach (Reagent reagent in unmatchedReagents) { RecipeItems.Add(new RecipeItem(reagent, 1)); } Refresh(); } private void Refresh() { UpdateFlags(); UpdateRecipe(); } private void UpdateFlags() { int activeIngredients = 0; for (int i = 0; i < RecipeItems.Count; i++) { RecipeItem item = RecipeItems[i]; item.First = i == 0; item.Last = i == RecipeItems.Count - 1; if (item.Quantity > 0) { activeIngredients++; } item.Unused = ((activeIngredients > 5) && item.Reagent.IsCatalyst) || (item.Quantity == 0); } } } }