using System.Collections.ObjectModel; using System.Reflection; using CommunityToolkit.Mvvm.ComponentModel; using Newtonsoft.Json; using OF_DL.Models.Config; namespace OF_DL.Gui.ViewModels; public partial class ConfigFieldViewModel : ViewModelBase { public ConfigFieldViewModel(PropertyInfo propertyInfo, object? initialValue) { PropertyInfo = propertyInfo; PropertyName = propertyInfo.Name; DisplayName = ToDisplayName(propertyInfo.Name); PropertyType = propertyInfo.PropertyType; IsBoolean = PropertyType == typeof(bool); IsEnum = PropertyType.IsEnum; IsDate = PropertyType == typeof(DateTime?); IsNumeric = PropertyType == typeof(int) || PropertyType == typeof(int?); IsMultiline = PropertyType == typeof(Dictionary); IsTextInput = !IsBoolean && !IsEnum && !IsDate && !IsNumeric; if (IsEnum) { foreach (string enumName in Enum.GetNames(PropertyType)) { EnumOptions.Add(enumName); } } LoadInitialValue(initialValue); } public PropertyInfo PropertyInfo { get; } public string PropertyName { get; } public string DisplayName { get; } public Type PropertyType { get; } public bool IsBoolean { get; } public bool IsEnum { get; } public bool IsDate { get; } public bool IsNumeric { get; } public bool IsTextInput { get; } public bool IsMultiline { get; } public double TextBoxMinHeight => IsMultiline ? 150 : 36; public ObservableCollection EnumOptions { get; } = []; [ObservableProperty] private bool _boolValue; [ObservableProperty] private string? _enumValue; [ObservableProperty] private DateTimeOffset? _dateValue; [ObservableProperty] private decimal? _numericValue; [ObservableProperty] private string _textValue = string.Empty; [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasError))] private string _errorMessage = string.Empty; public bool HasError => !string.IsNullOrWhiteSpace(ErrorMessage); public bool TryGetTypedValue(out object? value, out string? error) { value = null; error = null; if (IsBoolean) { value = BoolValue; return true; } if (IsEnum) { if (string.IsNullOrWhiteSpace(EnumValue)) { error = $"{DisplayName} is required."; return false; } if (!Enum.TryParse(PropertyType, EnumValue, true, out object? enumResult)) { error = $"{DisplayName} must be one of: {string.Join(", ", EnumOptions)}."; return false; } value = enumResult; return true; } if (PropertyType == typeof(string)) { value = TextValue.Trim(); return true; } if (PropertyType == typeof(int)) { if (!NumericValue.HasValue) { error = $"{DisplayName} must be a whole number."; return false; } if (decimal.Truncate(NumericValue.Value) != NumericValue.Value) { error = $"{DisplayName} must be a whole number."; return false; } value = (int)NumericValue.Value; return true; } if (PropertyType == typeof(int?)) { if (!NumericValue.HasValue) { value = null; return true; } if (decimal.Truncate(NumericValue.Value) != NumericValue.Value) { error = $"{DisplayName} must be a whole number."; return false; } value = (int)NumericValue.Value; return true; } if (PropertyType == typeof(DateTime?)) { if (!DateValue.HasValue) { value = null; return true; } value = DateValue.Value.DateTime; return true; } if (PropertyType == typeof(Dictionary)) { if (string.IsNullOrWhiteSpace(TextValue)) { value = new Dictionary(); return true; } try { Dictionary? parsed = JsonConvert.DeserializeObject>(TextValue); value = parsed ?? new Dictionary(); return true; } catch (JsonException) { error = $"{DisplayName} must be valid JSON."; return false; } } error = $"{DisplayName} has an unsupported field type."; return false; } public void ClearError() { ErrorMessage = string.Empty; } public void SetError(string message) { ErrorMessage = message; } private void LoadInitialValue(object? initialValue) { if (IsBoolean) { BoolValue = initialValue is bool boolValue && boolValue; return; } if (IsEnum) { EnumValue = initialValue?.ToString() ?? EnumOptions.FirstOrDefault(); return; } if (PropertyType == typeof(Dictionary)) { Dictionary creatorConfigs = initialValue as Dictionary ?? new Dictionary(); TextValue = JsonConvert.SerializeObject(creatorConfigs, Formatting.Indented); return; } if (PropertyType == typeof(DateTime?)) { DateTime? date = initialValue is DateTime dt ? dt : null; DateValue = date.HasValue ? new DateTimeOffset(date.Value) : null; return; } if (PropertyType == typeof(int)) { NumericValue = initialValue is int intValue ? intValue : 0; return; } if (PropertyType == typeof(int?)) { NumericValue = initialValue is int nullableIntValue ? nullableIntValue : null; return; } TextValue = initialValue?.ToString() ?? string.Empty; } private static string ToDisplayName(string propertyName) { if (string.IsNullOrWhiteSpace(propertyName)) { return propertyName; } return string.Concat(propertyName.Select((character, index) => index > 0 && char.IsUpper(character) && !char.IsUpper(propertyName[index - 1]) ? $" {character}" : character.ToString())); } }