OF-DL/OF DL.Gui/ViewModels/ConfigFieldViewModel.cs

252 lines
6.8 KiB
C#

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<string, CreatorConfig>);
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<string> 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<string, CreatorConfig>))
{
if (string.IsNullOrWhiteSpace(TextValue))
{
value = new Dictionary<string, CreatorConfig>();
return true;
}
try
{
Dictionary<string, CreatorConfig>? parsed =
JsonConvert.DeserializeObject<Dictionary<string, CreatorConfig>>(TextValue);
value = parsed ?? new Dictionary<string, CreatorConfig>();
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<string, CreatorConfig>))
{
Dictionary<string, CreatorConfig> creatorConfigs =
initialValue as Dictionary<string, CreatorConfig> ?? new Dictionary<string, CreatorConfig>();
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()));
}
}