using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using Microsoft.Win32; using TechnologyAssembler.Core.IO; namespace HashCalculator.GUI { internal abstract class NotifyPropertyChanged : INotifyPropertyChanged { #region INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; protected void Notify(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } protected void SetField(ref X field, X value, [CallerMemberName] string? propertyName = null) { if (propertyName == null) { throw new InvalidOperationException(); } if (EqualityComparer.Default.Equals(field, value)) { return; } field = value; Notify(propertyName); } #endregion } [SuppressMessage("Microsoft.Performance", "CA1812")] internal class ViewModel : NotifyPropertyChanged { private static readonly Random _random = new Random(); public MainInputViewModel MainInput { get; } public BigInputViewModel BigEntryInput { get; } private ObservableCollection _entries = new ObservableCollection(); public ObservableCollection Entries { get => _entries; set => SetField(ref _entries, value); } private string _statusText = SuggestionString("不知道该显示些什么呢……"); public string StatusText { get => _statusText; set => SetField(ref _statusText, value); } private string _traceText = string.Empty; public string TraceText { get => _traceText; set => SetField(ref _traceText, value); } public ViewModel() { var controller = new Controller(this); MainInput = new MainInputViewModel(controller); BigEntryInput = new BigInputViewModel(controller); } public static string SuggestionString(string original) { var generated = $"{_random.NextDouble()}"; System.Diagnostics.Debug.WriteLine(generated); if (generated.Contains("38") || generated.Contains("16")) { return "你们都是喂鱼的马甲!("; } if (generated.IndexOf('2') == 2) { return "本来以为两小时就能写完这个小工具,没想到写了两个星期,开始怀疑自己的智商orz"; } return original; } } internal class InputBarViewModel : NotifyPropertyChanged { private IEnumerable? _items; public virtual IEnumerable? Items { get => _items; set => SetField(ref _items, value); } private InputEntry? _selectedItem; public virtual InputEntry? SelectedItem { get => _selectedItem; set => SetField(ref _selectedItem, value); } private int _selectedIndex; public virtual int SelectedIndex { get => _selectedIndex; set => SetField(ref _selectedIndex, value); } private string? _text; public virtual string? Text { get => _text; set => SetField(ref _text, value); } } internal class MainInputViewModel : InputBarViewModel { private readonly FileSystemSuggestions _suggestions = new FileSystemSuggestions(); public override int SelectedIndex { get => base.SelectedIndex; set { if (value == 0) { if (Items.Count() <= 1) { value = -1; } else { value = base.SelectedIndex < value ? 1 : -1; } } base.SelectedIndex = value; } } public override string? Text { get => base.Text; set { base.Text = value; if (value != SelectedItem?.ToString()) { UpdateList(value); } } } public Command BrowseCommand { get; } public Command SelectCommand { get; } public MainInputViewModel(Controller controller) { Items = Enumerable.Empty(); BrowseCommand = new Command(window => { var dialog = new OpenFileDialog { Multiselect = false, ValidateNames = true }; if (dialog.ShowDialog(window) == true) { Text = dialog.FileName; } return Task.CompletedTask; }); SelectCommand = new Command(async viewModel => { if (SelectedItem?.IsValid != true) { return; } try { BrowseCommand.CanExecuteValue = false; SelectCommand.CanExecuteValue = false; await controller.OnMainInputDecided(SelectedItem).ConfigureAwait(true); } finally { BrowseCommand.CanExecuteValue = true; SelectCommand.CanExecuteValue = true; SelectedIndex = -1; } }); } private void UpdateList(string? path) { bool candidateFound = false; var result = _suggestions.ProvideFileSystemSuggestions(path); InputEntry? first = result.FirstOrDefault(); if (first != null) { if (new FileInfo(path!).FullName == new FileInfo(first.Value).FullName) { candidateFound = true; } } if (!string.IsNullOrEmpty(path)) { result = result.Prepend(new InputEntry(InputEntryType.Text, path!, path!)); } Items = result; if (candidateFound) { SelectedIndex = 1; } } } internal class BigInputViewModel : InputBarViewModel { public override IEnumerable? Items { get => base.Items; set => base.Items = value; } public override InputEntry? SelectedItem { get => base.SelectedItem; set { base.SelectedItem = value; SelectCommand.CanExecuteValue = value != null; } } public override string? Text { get => base.Text; set { base.Text = value; if (value != SelectedItem?.ToString()) { UpdateList(value); } } } private InputEntry? _lastProcessedManifest; public InputEntry? LastProcessedManifest { get => _lastProcessedManifest; set => SetField(ref _lastProcessedManifest, value); } private IEnumerable? _allManifests; public IEnumerable? AllManifests { get => _allManifests; set { SetField(ref _allManifests, value); Items = value; LastProcessedManifest = null; Text = null; } } public Command SelectCommand { get; } [SuppressMessage("Design", "CA1031:不捕获常规异常类型", Justification = "<挂起>")] public BigInputViewModel(Controller controller) { SelectCommand = new Command(async viewModel => { var mainInput = viewModel.MainInput; try { SelectCommand.CanExecuteValue = false; mainInput.BrowseCommand.CanExecuteValue = false; mainInput.SelectCommand.CanExecuteValue = false; LastProcessedManifest = SelectedItem; await controller.ProcessManifest(SelectedItem!.Value).ConfigureAwait(true); } catch (Exception exception) { CopyableBox.ShowDialog(_ => { return Task.FromResult($"在加载 manifest 时发生错误:{exception}"); }); } finally { SelectCommand.CanExecuteValue = true; mainInput.BrowseCommand.CanExecuteValue = true; mainInput.SelectCommand.CanExecuteValue = true; } }) { CanExecuteValue = false }; } private void UpdateList(string? input) { input ??= string.Empty; input = input.Replace(VirtualFileSystem.AltDirectorySeparatorChar, VirtualFileSystem.DirectorySeparatorChar); var filtered = from manifest in AllManifests where manifest.Value.IndexOf(input, StringComparison.OrdinalIgnoreCase) != -1 select manifest; Items = filtered; if (Items.FirstOrDefault()?.Value.Equals(input, StringComparison.OrdinalIgnoreCase) == true) { SelectedIndex = 0; } } } }