using DynamicData; using DynamicData.Binding; using Microsoft.WindowsAPICodePack.Dialogs; using ReactiveUI; using ReactiveUI.Fody.Helpers; using System; using System.IO; using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading.Tasks; using System.Windows.Media.Imaging; using Wabbajack.Common; using Wabbajack.Lib; namespace Wabbajack { public class CompilerVM : ViewModel { public MainWindowVM MWVM { get; } private readonly ObservableAsPropertyHelper _Image; public BitmapImage Image => _Image.Value; [Reactive] public ModManager SelectedCompilerType { get; set; } private readonly ObservableAsPropertyHelper _Compiler; public ISubCompilerVM Compiler => _Compiler.Value; private readonly ObservableAsPropertyHelper _CurrentModlistSettings; public ModlistSettingsEditorVM CurrentModlistSettings => _CurrentModlistSettings.Value; private readonly ObservableAsPropertyHelper _CurrentStatusTracker; public StatusUpdateTracker CurrentStatusTracker => _CurrentStatusTracker.Value; private readonly ObservableAsPropertyHelper _Compiling; public bool Compiling => _Compiling.Value; public CompilerVM(MainWindowVM mainWindowVM) { this.MWVM = mainWindowVM; // Load settings CompilerSettings settings = this.MWVM.Settings.Compiler; this.SelectedCompilerType = settings.LastCompiledModManager; this.MWVM.Settings.SaveSignal .Subscribe(_ => { settings.LastCompiledModManager = this.SelectedCompilerType; }) .DisposeWith(this.CompositeDisposable); // Swap to proper sub VM based on selected type this._Compiler = this.WhenAny(x => x.SelectedCompilerType) // Delay so the initial VM swap comes in immediately, subVM comes right after .DelayInitial(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .Select(type => { switch (type) { case ModManager.MO2: return new MO2CompilerVM(this); case ModManager.Vortex: return new VortexCompilerVM(this); default: return null; } }) // Unload old VM .Pairwise() .Do(pair => { pair.Previous?.Unload(); }) .Select(p => p.Current) .ToProperty(this, nameof(this.Compiler)); // Let sub VM determine what settings we're displaying and when this._CurrentModlistSettings = this.WhenAny(x => x.Compiler.ModlistSettings) .ToProperty(this, nameof(this.CurrentModlistSettings)); // Let sub VM determine what progress we're seeing this._CurrentStatusTracker = this.WhenAny(x => x.Compiler.StatusTracker) .ToProperty(this, nameof(this.CurrentStatusTracker)); this._Image = this.WhenAny(x => x.CurrentModlistSettings.ImagePath.TargetPath) // Throttle so that it only loads image after any sets of swaps have completed .Throttle(TimeSpan.FromMilliseconds(50), RxApp.MainThreadScheduler) .DistinctUntilChanged() .Select(path => { if (string.IsNullOrWhiteSpace(path)) return UIUtils.BitmapImageFromResource("Wabbajack.Resources.Wabba_Mouth.png"); if (UIUtils.TryGetBitmapImageFromFile(path, out var image)) { return image; } return null; }) .ToProperty(this, nameof(this.Image)); this._Compiling = this.WhenAny(x => x.Compiler.ActiveCompilation) .Select(compilation => compilation != null) .ObserveOnGuiThread() .ToProperty(this, nameof(this.Compiling)); // Compile progress updates and populate ObservableCollection var subscription = this.WhenAny(x => x.Compiler.ActiveCompilation) .SelectMany(c => c?.QueueStatus ?? Observable.Empty()) .ObserveOn(RxApp.TaskpoolScheduler) .ToObservableChangeSet(x => x.ID) .Batch(TimeSpan.FromMilliseconds(250), RxApp.TaskpoolScheduler) .EnsureUniqueChanges() .ObserveOn(RxApp.MainThreadScheduler) .Sort(SortExpressionComparer.Ascending(s => s.ID), SortOptimisations.ComparesImmutableValuesOnly) .Bind(this.MWVM.StatusList) .Subscribe() .DisposeWith(this.CompositeDisposable); } } }