using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Timers;
using System.Windows.Media;
using Artemis.Core;
using Artemis.Core.Modules;
using Artemis.Core.Services;
using Artemis.UI.Shared.Services;
using MaterialDesignColors.ColorManipulation;
using Stylet;
namespace Artemis.UI.Shared.Input
{
///
/// Represents a view model that allows selecting a data model property used by boolean operations on a certain data
/// model property
///
public class DataModelDynamicViewModel : PropertyChangedBase, IDisposable
{
private readonly List _modules;
private readonly IDataModelUIService _dataModelUIService;
private readonly Timer _updateTimer;
private SolidColorBrush _buttonBrush = new(Color.FromRgb(171, 71, 188));
private DataModelPath? _dataModelPath;
private DataModelPropertiesViewModel? _dataModelViewModel;
private bool _displaySwitchButton;
private Type[] _filterTypes = Array.Empty();
private bool _isDataModelViewModelOpen;
private bool _isEnabled = true;
private string _placeholder = "Select a property";
private readonly PluginSetting _showFullPath;
internal DataModelDynamicViewModel(List modules, ISettingsService settingsService, IDataModelUIService dataModelUIService)
{
_modules = modules;
_dataModelUIService = dataModelUIService;
_updateTimer = new Timer(500);
_showFullPath = settingsService.GetSetting("ProfileEditor.ShowFullPaths", true);
ExtraDataModelViewModels = new BindableCollection();
ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues");
SelectPropertyCommand = new DelegateCommand(ExecuteSelectPropertyCommand);
Initialize();
}
///
/// Gets or sets the brush to use for the input button
///
public SolidColorBrush ButtonBrush
{
get => _buttonBrush;
set
{
if (!SetAndNotify(ref _buttonBrush, value)) return;
NotifyOfPropertyChange(nameof(SwitchButtonBrush));
}
}
///
/// Gets the brush to use for the switch button
///
public SolidColorBrush SwitchButtonBrush => new(ButtonBrush.Color.Darken());
///
/// Gets or sets the placeholder text when no value is entered
///
public string Placeholder
{
get => _placeholder;
set => SetAndNotify(ref _placeholder, value);
}
///
/// Gets or sets the enabled state of the input
///
public bool IsEnabled
{
get => _isEnabled;
set => SetAndNotify(ref _isEnabled, value);
}
///
/// Gets or sets whether the switch button should be displayed
///
public bool DisplaySwitchButton
{
get => _displaySwitchButton;
set => SetAndNotify(ref _displaySwitchButton, value);
}
///
/// Gets or sets the types of properties this view model will allow to be selected
///
public Type[] FilterTypes
{
get => _filterTypes;
set
{
if (!SetAndNotify(ref _filterTypes, value)) return;
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes);
}
}
///
/// Gets a bindable collection of extra data model view models to show
///
public BindableCollection ExtraDataModelViewModels { get; }
///
/// Gets a boolean indicating whether there are any modules providing data models
///
public bool HasNoModules => (DataModelViewModel == null || !DataModelViewModel.Children.Any()) && !HasExtraDataModels;
///
/// Gets a boolean indicating whether there are any extra data models
///
public bool HasExtraDataModels => ExtraDataModelViewModels.Any();
///
/// Command used by view
///
public DelegateCommand SelectPropertyCommand { get; }
///
/// Setting used by view
///
public PluginSetting ShowDataModelValues { get; }
public PluginSetting ShowFullPath { get; }
///
/// Gets or sets root the data model view model
///
public DataModelPropertiesViewModel? DataModelViewModel
{
get => _dataModelViewModel;
private set => SetAndNotify(ref _dataModelViewModel, value);
}
///
/// Gets or sets a boolean indicating whether the data model is open
///
public bool IsDataModelViewModelOpen
{
get => _isDataModelViewModelOpen;
set
{
if (!SetAndNotify(ref _isDataModelViewModelOpen, value)) return;
if (value)
{
UpdateDataModelVisualization();
if (DataModelViewModel != null)
OpenSelectedValue(DataModelViewModel);
}
}
}
///
/// Gets or sets the data model path of the currently selected data model property
///
public DataModelPath? DataModelPath
{
get => _dataModelPath;
private set
{
if (!SetAndNotify(ref _dataModelPath, value)) return;
NotifyOfPropertyChange(nameof(IsValid));
NotifyOfPropertyChange(nameof(DisplayValue));
NotifyOfPropertyChange(nameof(DisplayPath));
}
}
///
/// Gets a boolean indicating whether the current selection is valid
///
public bool IsValid => DataModelPath?.IsValid ?? true;
///
/// Gets the display name of the currently selected property
///
public string? DisplayValue
{
get
{
if (_showFullPath.Value)
return DisplayPath;
return DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
}
}
///
/// Gets the human readable path of the currently selected property
///
public string DisplayPath
{
get
{
if (DataModelPath == null)
return "Click to select a property";
if (!DataModelPath.IsValid)
return "Invalid path";
return string.Join(" › ", DataModelPath.Segments.Where(s => s.GetPropertyDescription()!= null).Select(s => s.GetPropertyDescription()!.Name));
}
}
///
/// Gets or sets a boolean indicating whether event child VMs should be loaded, defaults to
///
public bool LoadEventChildren { get; set; } = true;
///
/// Changes the root data model VM stored in to the provided
///
///
/// The data model VM to set the new root data model to
public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
{
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested -= DataModelOnUpdateRequested;
DataModelViewModel = dataModel;
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
}
///
/// Changes the currently selected property by its path
///
/// The path of the property to set the selection to
public void ChangeDataModelPath(DataModelPath? dataModelPath)
{
DataModelPath?.Dispose();
DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null;
}
///
/// Requests switching the input type to static using a
///
public void SwitchToStatic()
{
ChangeDataModelPath(null);
OnPropertySelected(new DataModelInputDynamicEventArgs(null));
OnSwitchToStaticRequested();
}
private void Initialize()
{
// Get the data models
DataModelViewModel = _dataModelUIService.GetPluginDataModelVisualization(_modules, true);
if (DataModelViewModel != null)
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
ExtraDataModelViewModels.CollectionChanged += ExtraDataModelViewModelsOnCollectionChanged;
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
_showFullPath.SettingChanged += ShowFullPathOnSettingChanged;
}
private void ExecuteSelectPropertyCommand(object? context)
{
if (context is not DataModelVisualizationViewModel selected)
return;
ChangeDataModelPath(selected.DataModelPath);
OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath));
}
#region IDisposable
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
///
/// to release both managed and unmanaged resources;
/// to release only unmanaged resources.
///
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_updateTimer.Stop();
_updateTimer.Dispose();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
_showFullPath.SettingChanged -= ShowFullPathOnSettingChanged;
DataModelViewModel?.Dispose();
DataModelPath?.Dispose();
}
}
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Event handlers
private void DataModelOnUpdateRequested(object? sender, EventArgs e)
{
DataModelViewModel?.ApplyTypeFilter(true, FilterTypes);
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
extraDataModelViewModel.ApplyTypeFilter(true, FilterTypes);
}
private void ExtraDataModelViewModelsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
NotifyOfPropertyChange(nameof(HasExtraDataModels));
}
private void OnUpdateTimerOnElapsed(object? sender, ElapsedEventArgs e)
{
if (!IsDataModelViewModelOpen)
return;
UpdateDataModelVisualization();
}
private void UpdateDataModelVisualization()
{
DataModelViewModel?.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
foreach (DataModelPropertiesViewModel extraDataModelViewModel in ExtraDataModelViewModels)
extraDataModelViewModel.Update(_dataModelUIService, new DataModelUpdateConfiguration(LoadEventChildren));
}
private void OpenSelectedValue(DataModelVisualizationViewModel dataModelPropertiesViewModel)
{
if (DataModelPath == null)
return;
if (dataModelPropertiesViewModel.Children.Any(c => c.DataModelPath != null && DataModelPath.Path.StartsWith(c.DataModelPath.Path)))
{
dataModelPropertiesViewModel.IsVisualizationExpanded = true;
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in dataModelPropertiesViewModel.Children)
{
OpenSelectedValue(dataModelVisualizationViewModel);
}
}
}
private void ShowFullPathOnSettingChanged(object? sender, EventArgs e)
{
NotifyOfPropertyChange(nameof(DisplayValue));
}
#endregion
#region Events
///
/// Occurs when anew property has been selected
///
public event EventHandler? PropertySelected;
///
/// Occurs when a switch to static input has been requested
///
public event EventHandler? SwitchToStaticRequested;
///
/// Invokes the event
///
///
protected virtual void OnPropertySelected(DataModelInputDynamicEventArgs e)
{
PropertySelected?.Invoke(this, e);
}
///
/// Invokes the event
///
protected virtual void OnSwitchToStaticRequested()
{
SwitchToStaticRequested?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}