Files @ a5faa82faf6a
Branch filter:

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

Malkyne
Import and Export should be working correctly. Can now import Practical Paint
reactions.txt files. Fixed a crash taht occurred when switching profiles.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input.Platform;
using DesertPaintCodex.Models;
using DesertPaintCodex.Services;
using ReactiveUI;

namespace DesertPaintCodex.ViewModels
{
    public class MainWindowViewModel : ViewModelBase
    {
        private string _statusText = string.Empty;

        public string StatusText
        {
            get => _statusText;
            private set => this.RaiseAndSetIfChanged(ref _statusText, value);
        }

        private static readonly List<string> ZipFileExtensions = new() {"zip"};
        private static readonly FileDialogFilter ZipFileFilter = new() {Extensions = ZipFileExtensions, Name = "Zip"};
        private static readonly List<FileDialogFilter> ZipFileFilters = new() {ZipFileFilter};

        private static readonly List<string> TxtFileExtensions = new() {"txt"};
        private static readonly FileDialogFilter TxtFileFilter = new() {Extensions = TxtFileExtensions, Name = "Text"};
        private static readonly List<FileDialogFilter> TxtFileFilters = new() {TxtFileFilter};
        
        private static readonly List<FileDialogFilter> NoFileFilters = new();

        public MainWindowViewModel()
        {
            if (!ProfileManager.HasProfileLoaded && ProfileManager.HasProfiles())
            {
                ProfileManager.LoadProfile(ProfileManager.GetProfileList()[0]);
            }
            Debug.Assert(ProfileManager.HasProfileLoaded);
            
            PaletteService.Initialize();
            ReactionTestService.Initialize();
            
            ShowAboutDialog = new Interaction<AboutViewModel, Unit>();
            ShowScreenSettingsDialog = new Interaction<ScreenSettingsViewModel, Unit>();
            
            StatusText = "USER PROFILE: " + ProfileManager.CurrentProfile?.Name;
            Exit = ReactiveCommand.Create(() => { });
        }

        public async void ManageProfiles()
        {
            if (Application.Current is not App app) return;
            
            if (await ValidateSafeExit())
            {
                ProfileManager.UnloadProfile();
                app.ReturnToWelcome();
            }
        }

        public async void ImportProfile()
        {
            string? fileName = await GetLoadFileName("Open Zipped Profile", ZipFileFilters);
            
            if (string.IsNullOrEmpty(fileName)) return;

            try
            {
                ProfileManager.CurrentProfile?.Import(fileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine("ImportProfile threw exception " + e);
                await ShowMessageBox("Import Failed",
                    "Your file could not be imported. It must be a zip file containing a Desert Paint Codex profile.", "OK");
            }

            ProfileManager.ReloadProfile();
            
            if (Application.Current is not App app) return;
            
            app.RefreshMainWindow();
        }

        public async void ExportProfile()
        {
            string? fileName = await GetSaveFileName("Save Zipped Profile", ZipFileFilters);
            
            if (string.IsNullOrEmpty(fileName)) return;
            
            try
            {
                ProfileManager.CurrentProfile?.Export(fileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine("ExportProfile threw exception " + e);
                await ShowMessageBox("Export Failed",
                    "Your profile could not be exported. Please ensure that you are providing a valid filename for the zip file that we are creating.", "OK");
            }
        }

        public async void ExportForPP(object f)
        {
            string? fileName = await GetSaveFileName("Save Practical Paint File", TxtFileFilters, "reactions.txt");
            
            if (string.IsNullOrEmpty(fileName)) return;
            
            try
            {
                ProfileManager.CurrentProfile?.SaveToPP(fileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine("ExportForPP threw exception " + e);
                await ShowMessageBox("Export Failed",
                    "Please ensure that you have provided a valid file path for your reactions file.", "OK");
            }
        }

        public async void ImportFromPP()
        {
            string? fileName = await GetLoadFileName("Import Reactions File", TxtFileFilters, "reactions.txt");
            
            if (string.IsNullOrEmpty(fileName)) return;

            try
            {
                ProfileManager.CurrentProfile?.ImportFromPP(fileName);
            }
            catch (Exception e)
            {
                Debug.WriteLine("ImportFromPP threw exception " + e);
                await ShowMessageBox("Import Failed",
                    "Your file could not be imported. It must be a valid Practical Paint reactions.txt file.", "OK");
            }

            ProfileManager.ReloadProfile();
            
            if (Application.Current is not App app) return;
            
            app.RefreshMainWindow();
        }

        public static async void ExportPaintRecipes()
        {
            string? fileName = await GetSaveFileName("Export Paint Recipes", NoFileFilters);
            if (!string.IsNullOrEmpty(fileName))
            {
                ProfileManager.CurrentProfile?.ExportWikiRecipes(fileName);
            }
        }
        
        public static async void ExportRibbonRecipes()
        {
            string? fileName = await GetSaveFileName("Export Ribbon Recipes", NoFileFilters);
            if (!string.IsNullOrEmpty(fileName))
            {
                ProfileManager.CurrentProfile?.ExportWikiRibbons(fileName);
            }
        }

        public static async void CopyPaintRecipes()
        {
            StringWriter writer = new();
            ProfileManager.CurrentProfile?.ExportWikiRecipes(writer);
            IClipboard clipboard = Application.Current.Clipboard;
            await writer.FlushAsync();
            await clipboard.SetTextAsync(writer.ToString());
            writer.Close();
        }
        
        public static async void CopyRibbonRecipes()
        {
            StringWriter writer = new();
            ProfileManager.CurrentProfile?.ExportWikiRibbons(writer);
            IClipboard clipboard = Application.Current.Clipboard;
            await writer.FlushAsync();
            await clipboard.SetTextAsync(writer.ToString());
            writer.Close();
        }

        public async Task ShowScreenSettings()
        {
            await ShowScreenSettingsDialog.Handle(new ScreenSettingsViewModel());
        }

        public async Task ShowAbout()
        {
            await ShowAboutDialog.Handle(new AboutViewModel());
        }

        public async Task<bool> ValidateSafeExit()
        {
            // TODO: Determine if there's unsaved stuff we need to deal with.
            // return await ShowYesNoBox("Leaving so Soon?", "[A potential reason not to quit goes here]");
            await Task.Delay(1); // Stub to prevent warnings.
            return true;
        }

        private static async Task<string?> GetLoadFileName(string title, List<FileDialogFilter> filters, string? fileName = null)
        {
            if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) return null;
            
            // TODO: Figure out why the file filters aren't working.
            
            OpenFileDialog dialog = new()
            {
                Title           = title,
                Filters         = filters,
                InitialFileName = fileName,
                AllowMultiple   = false
            };

            string[] files = await dialog.ShowAsync(desktop.MainWindow);
            return files.Length > 0 ? files[0] : null;
        }
        
        
        private static async Task<string?> GetSaveFileName(string title, List<FileDialogFilter> filters, string? fileName = null)
        {
            if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) return null;
            
            // TODO: Figure out why the file filters aren't working.
            
            SaveFileDialog dialog = new()
            {
                Title           = title,
                Filters         = filters,
                InitialFileName = fileName
            };

            return await dialog.ShowAsync(desktop.MainWindow);
        }
        
        public Interaction<AboutViewModel, Unit> ShowAboutDialog { get; }
        public Interaction<ScreenSettingsViewModel, Unit> ShowScreenSettingsDialog { get; }

        public ReactiveCommand<Unit, Unit> Exit { get; }
    }
}