using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Media; using Artemis.Core; using Artemis.Core.Modules; using Artemis.Core.Services; using Artemis.UI.Shared; using Artemis.UI.Shared.Input; using Artemis.UI.Shared.Services; using Stylet; namespace Artemis.UI.Screens.ProfileEditor.Conditions.Abstract { public abstract class DataModelConditionPredicateViewModel : DataModelConditionViewModel, IDisposable { private readonly IConditionOperatorService _conditionOperatorService; private readonly IDataModelUIService _dataModelUIService; private readonly List _modules; private readonly IProfileEditorService _profileEditorService; private DataModelStaticViewModel _rightSideInputViewModel; private DataModelDynamicViewModel _rightSideSelectionViewModel; private BaseConditionOperator _selectedOperator; private List _supportedInputTypes; protected DataModelConditionPredicateViewModel( DataModelConditionPredicate dataModelConditionPredicate, List modules, IProfileEditorService profileEditorService, IDataModelUIService dataModelUIService, IConditionOperatorService conditionOperatorService, ISettingsService settingsService) : base(dataModelConditionPredicate) { _modules = modules; _profileEditorService = profileEditorService; _dataModelUIService = dataModelUIService; _conditionOperatorService = conditionOperatorService; _supportedInputTypes = new List(); SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand); Operators = new BindableCollection(); ShowDataModelValues = settingsService.GetSetting("ProfileEditor.ShowDataModelValues"); } public DataModelConditionPredicate DataModelConditionPredicate => (DataModelConditionPredicate) Model; public PluginSetting ShowDataModelValues { get; } public bool CanSelectOperator => DataModelConditionPredicate.LeftPath is {IsValid: true}; public BaseConditionOperator SelectedOperator { get => _selectedOperator; set => SetAndNotify(ref _selectedOperator, value); } public DataModelDynamicViewModel RightSideSelectionViewModel { get => _rightSideSelectionViewModel; set => SetAndNotify(ref _rightSideSelectionViewModel, value); } public DataModelStaticViewModel RightSideInputViewModel { get => _rightSideInputViewModel; set => SetAndNotify(ref _rightSideInputViewModel, value); } public DelegateCommand SelectOperatorCommand { get; } public BindableCollection Operators { get; } protected SolidColorBrush LeftSideColor { get; set; } public override void Evaluate() { IsConditionMet = DataModelConditionPredicate.Evaluate(); } public override void Delete() { base.Delete(); _profileEditorService.SaveSelectedProfileElement(); } public virtual void Initialize() { LeftSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules); LeftSideSelectionViewModel.PropertySelected += LeftSideOnPropertySelected; if (LeftSideColor != null) LeftSideSelectionViewModel.ButtonBrush = LeftSideColor; // Determine which types are currently supported _supportedInputTypes = GetSupportedInputTypes(); Update(); } public override void Update() { LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray(); LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.LeftPath); Type leftSideType = GetLeftSideType(); // Get the supported operators Operators.Clear(); Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object), ConditionParameterSide.Left)); if (DataModelConditionPredicate.Operator == null) DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault()); // The core doesn't care about best matches so if there is a new preferred operator, use that instead else if (!Operators.Contains(DataModelConditionPredicate.Operator)) DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.Description == DataModelConditionPredicate.Operator.Description) ?? Operators.FirstOrDefault()); NotifyOfPropertyChange(nameof(CanSelectOperator)); SelectedOperator = DataModelConditionPredicate.Operator; // Without a selected operator or one that supports a right side, leave the right side input empty if (SelectedOperator?.RightSideType == null) { DisposeRightSideStaticViewModel(); DisposeRightSideDynamicViewModel(); return; } // Ensure the right side has the proper VM if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic) { DisposeRightSideStaticViewModel(); if (RightSideSelectionViewModel == null) CreateRightSideSelectionViewModel(); RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath); RightSideSelectionViewModel.FilterTypes = new[] {SelectedOperator.RightSideType}; } else { DisposeRightSideDynamicViewModel(); if (RightSideInputViewModel == null) CreateRightSideInputViewModel(); Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType(); if (preferredType != null && RightSideInputViewModel.TargetType != preferredType) RightSideInputViewModel.UpdateTargetType(preferredType); RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue; } } public void ApplyLeftSide() { Type newType = LeftSideSelectionViewModel.DataModelPath.GetPropertyType(); bool converted = ConvertIfRequired(newType); if (converted) return; DataModelConditionPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath); _profileEditorService.SaveSelectedProfileElement(); SelectedOperator = DataModelConditionPredicate.Operator; Update(); } public void ApplyRightSideDynamic() { DataModelConditionPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath); _profileEditorService.SaveSelectedProfileElement(); Update(); } public void ApplyRightSideStatic(object value) { DataModelConditionPredicate.UpdateRightSideStatic(value); _profileEditorService.SaveSelectedProfileElement(); Update(); } public void ApplyOperator() { DataModelConditionPredicate.UpdateOperator(SelectedOperator); _profileEditorService.SaveSelectedProfileElement(); Update(); } protected abstract List GetSupportedInputTypes(); protected abstract Type GetLeftSideType(); protected virtual List GetExtraRightSideDataModelViewModels() { return null; } private void ExecuteSelectOperatorCommand(object context) { if (!(context is BaseConditionOperator dataModelConditionOperator)) return; SelectedOperator = dataModelConditionOperator; ApplyOperator(); } public override void UpdateModules() { if (LeftSideSelectionViewModel != null) { LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected; LeftSideSelectionViewModel.Dispose(); LeftSideSelectionViewModel = null; } DisposeRightSideStaticViewModel(); DisposeRightSideDynamicViewModel(); // If the modules changed the paths may no longer be valid if they targeted a module no longer available, in that case clear the path if (DataModelConditionPredicate.LeftPath?.Target != null && !DataModelConditionPredicate.LeftPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.LeftPath.Target.Module)) DataModelConditionPredicate.UpdateLeftSide(null); if (DataModelConditionPredicate.RightPath?.Target != null && !DataModelConditionPredicate.RightPath.Target.IsExpansion && !_modules.Contains(DataModelConditionPredicate.RightPath.Target.Module)) DataModelConditionPredicate.UpdateRightSideDynamic(null); Initialize(); } #region IDisposable protected virtual void Dispose(bool disposing) { if (disposing) { if (LeftSideSelectionViewModel != null) { LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected; LeftSideSelectionViewModel.Dispose(); LeftSideSelectionViewModel = null; } DisposeRightSideStaticViewModel(); DisposeRightSideDynamicViewModel(); } } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion #region View model creation private void CreateRightSideSelectionViewModel() { RightSideSelectionViewModel = _dataModelUIService.GetDynamicSelectionViewModel(_modules); RightSideSelectionViewModel.ButtonBrush = (SolidColorBrush) Application.Current.FindResource("PrimaryHueMidBrush"); RightSideSelectionViewModel.DisplaySwitchButton = true; RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected; RightSideSelectionViewModel.SwitchToStaticRequested += RightSideSelectionViewModelOnSwitchToStaticRequested; List extra = GetExtraRightSideDataModelViewModels(); if (extra != null) RightSideSelectionViewModel.ExtraDataModelViewModels.AddRange(extra); } private void CreateRightSideInputViewModel() { Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType(); RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(preferredType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription()); RightSideInputViewModel.ButtonBrush = (SolidColorBrush) Application.Current.FindResource("PrimaryHueMidBrush"); RightSideInputViewModel.DisplaySwitchButton = true; RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered; RightSideInputViewModel.SwitchToDynamicRequested += RightSideInputViewModelOnSwitchToDynamicRequested; } private void DisposeRightSideStaticViewModel() { if (RightSideInputViewModel == null) return; RightSideInputViewModel.ValueUpdated -= RightSideOnValueEntered; RightSideInputViewModel.SwitchToDynamicRequested -= RightSideInputViewModelOnSwitchToDynamicRequested; RightSideInputViewModel.Dispose(); RightSideInputViewModel = null; } private void DisposeRightSideDynamicViewModel() { if (RightSideSelectionViewModel == null) return; RightSideSelectionViewModel.PropertySelected -= RightSideOnPropertySelected; RightSideSelectionViewModel.SwitchToStaticRequested -= RightSideSelectionViewModelOnSwitchToStaticRequested; RightSideSelectionViewModel.Dispose(); RightSideSelectionViewModel = null; } #endregion #region Event handlers private void LeftSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) { ApplyLeftSide(); } private void RightSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e) { ApplyRightSideDynamic(); } private void RightSideOnValueEntered(object sender, DataModelInputStaticEventArgs e) { ApplyRightSideStatic(e.Value); } private void RightSideSelectionViewModelOnSwitchToStaticRequested(object sender, EventArgs e) { DataModelConditionPredicate.PredicateType = ProfileRightSideType.Static; // Ensure the right static value is never null when the preferred type is a value type Type preferredType = DataModelConditionPredicate.GetPreferredRightSideType(); if (DataModelConditionPredicate.RightStaticValue == null && preferredType != null && preferredType.IsValueType) DataModelConditionPredicate.UpdateRightSideStatic(preferredType.GetDefault()); Update(); } private void RightSideInputViewModelOnSwitchToDynamicRequested(object sender, EventArgs e) { DataModelConditionPredicate.PredicateType = ProfileRightSideType.Dynamic; Update(); } #endregion } }