using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Core.Modules; using Artemis.Storage.Entities.Profile; namespace Artemis.Core; /// /// Represents the configuration of a profile, contained in a /// public class ProfileConfiguration : BreakableModel, IStorageModel, IDisposable, IPluginFeatureDependent { /// /// Represents an empty profile. /// public static readonly ProfileConfiguration Empty = new(ProfileCategory.Empty, "Empty", "Empty"); private bool _disposed; internal ProfileConfiguration(ProfileCategory category, string name, string icon) { Name = name; Category = category; Entity = new ProfileContainerEntity(); Icon = new ProfileConfigurationIcon(Entity); Icon.SetIconByName(icon); ActivationCondition = new NodeScript("Activate profile", "Whether or not the profile should be active", this); Entity.Profile.Id = Guid.NewGuid(); Entity.ProfileConfiguration.ProfileId = Entity.Profile.Id; } internal ProfileConfiguration(ProfileCategory category, ProfileContainerEntity entity) { // Will be loaded from the entity Name = null!; Category = category; Entity = entity; Icon = new ProfileConfigurationIcon(Entity); ActivationCondition = new NodeScript("Activate profile", "Whether or not the profile should be active", this); Load(); } /// /// Gets or sets the name of this profile configuration /// public string Name { get; set => SetAndNotify(ref field, value); } /// /// The order in which this profile appears in the update loop and sidebar /// public int Order { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets a boolean indicating whether this profile is suspended, disabling it regardless of the /// /// public bool IsSuspended { get; set => SetAndNotify(ref field, value); } /// /// Gets a boolean indicating whether this profile configuration is missing any modules /// public bool IsMissingModule { get; private set => SetAndNotify(ref field, value); } /// /// Gets or sets the category of this profile configuration /// public ProfileCategory Category { get; internal set => SetAndNotify(ref field, value); } /// /// Gets or sets the used to determine hotkey behaviour /// public ProfileConfigurationHotkeyMode HotkeyMode { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets the hotkey used to enable or toggle the profile /// public Hotkey? EnableHotkey { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets the hotkey used to disable the profile /// public Hotkey? DisableHotkey { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets the behaviour of when this profile is activated /// public ActivationBehaviour ActivationBehaviour { get; set => SetAndNotify(ref field, value); } /// /// Gets a boolean indicating whether the activation conditions where met during the last call /// public bool ActivationConditionMet { get; private set => SetAndNotify(ref field, value); } /// /// Gets the profile of this profile configuration /// public Profile? Profile { get; internal set => SetAndNotify(ref field, value); } /// /// Gets or sets a boolean indicating whether this profile should fade in and out when enabling or disabling /// public bool FadeInAndOut { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets a boolean indicating whether this profile is configurable via its . /// public bool IsConfigurable { get; set => SetAndNotify(ref field, value); } /// /// Gets or sets the module this profile uses /// public Module? Module { get; set { SetAndNotify(ref field, value); IsMissingModule = false; } } /// /// Gets the configuration sections of this profile configuration. /// public ObservableCollection ConfigurationSections { get; } = []; /// /// Gets the icon configuration /// public ProfileConfigurationIcon Icon { get; } /// /// Gets the data model condition that must evaluate to for this profile to be activated /// alongside any activation requirements of the , if set /// public NodeScript ActivationCondition { get; } /// /// Gets the entity used by this profile config /// public ProfileContainerEntity Entity { get; } /// /// Gets the ID of the profile of this profile configuration /// public Guid ProfileId => Entity.Profile.Id; #region Overrides of BreakableModel /// public override string BrokenDisplayName => "Profile Configuration"; #endregion /// /// Updates this configurations activation condition status /// public void Update() { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); if (!ActivationCondition.ExitNodeConnected) { ActivationConditionMet = true; } else { ActivationCondition.Run(); ActivationConditionMet = ActivationCondition.Result; } } /// /// Determines whether the profile of this configuration should be active /// /// Whether or not to take activation conditions into consideration public bool ShouldBeActive(bool includeActivationCondition) { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); if (Category.IsSuspended || IsSuspended || IsMissingModule) return false; if (includeActivationCondition) return ActivationConditionMet && (Module == null || Module.IsActivated); return Module == null || Module.IsActivated; } /// public override string ToString() { return $"[ProfileConfiguration] {nameof(Name)}: {Name}"; } internal void LoadModules(List enabledModules) { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); Module = enabledModules.FirstOrDefault(m => m.Id == Entity.ProfileConfiguration.ModuleId); IsMissingModule = Module == null && Entity.ProfileConfiguration.ModuleId != null; } /// public void Dispose() { _disposed = true; ActivationCondition.Dispose(); } /// public IEnumerable GetFeatureDependencies() { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); if (Profile == null) throw new InvalidOperationException("Cannot determine feature dependencies when the profile is not loaded."); return ActivationCondition.GetFeatureDependencies() .Concat(Profile.GetFeatureDependencies()) .Concat(Module != null ? [Module] : []); } #region Implementation of IStorageModel /// public void Load() { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); Name = Entity.ProfileConfiguration.Name; IsSuspended = Entity.ProfileConfiguration.IsSuspended; ActivationBehaviour = (ActivationBehaviour) Entity.ProfileConfiguration.ActivationBehaviour; HotkeyMode = (ProfileConfigurationHotkeyMode) Entity.ProfileConfiguration.HotkeyMode; FadeInAndOut = Entity.ProfileConfiguration.FadeInAndOut; Order = Entity.ProfileConfiguration.Order; Icon.Load(); if (Entity.ProfileConfiguration.ActivationCondition != null) ActivationCondition.LoadFromEntity(Entity.ProfileConfiguration.ActivationCondition); EnableHotkey = Entity.ProfileConfiguration.EnableHotkey != null ? new Hotkey(Entity.ProfileConfiguration.EnableHotkey) : null; DisableHotkey = Entity.ProfileConfiguration.DisableHotkey != null ? new Hotkey(Entity.ProfileConfiguration.DisableHotkey) : null; // Placeholder configuration sections ConfigurationSections.Clear(); ConfigurationSections.Add(new ConfigurationSection(this) { Name = "General" }); ConfigurationSections.Add(new ConfigurationSection(this) { Name = "Other" }); ConfigurationSections.Add(new ConfigurationSection(this) { Name = "Something else" }); ConfigurationSections[0].Items.Add(new ConfigurationTextItem {Text = "This is a placeholder text item in the General section."}); ConfigurationSections[0].Items.Add(new ConfigurationNumericItem {Name = "Numeric item", Description = "This one also has a description"}); ConfigurationSections[0].Items.Add(new ConfigurationBooleanItem {Name = "Do the thing?", TrueText = "Absolutely", FalseText = "Nope"}); ConfigurationSections[1].Items.Add(new ConfigurationTextItem {Text = "This is a placeholder text item in the Other section."}); ConfigurationSections[2].Items.Add(new ConfigurationTextItem {Text = "This is a placeholder text item in the Something else section."}); ConfigurationSections[2].Items.Add(new ConfigurationTextItem {Text = "This is another placeholder text item in the Something else section."}); ConfigurationSections[2].Items.Add(new ConfigurationNumericItem {Name = "Numeric item", Description = "This one uses a slider", Minimum = 0, Maximum = 10, Slider = true}); ConfigurationSections[2].Items.Add(new ConfigurationColorGradientItem {Name = "Color gradient"}); ConfigurationSections[2].Items.Add(new ConfigurationSKColorItem {Name = "A simple color", Description = "Again with a description"}); } /// public void Save() { if (_disposed) throw new ObjectDisposedException("ProfileConfiguration"); Entity.ProfileConfiguration.Name = Name; Entity.ProfileConfiguration.IsSuspended = IsSuspended; Entity.ProfileConfiguration.ActivationBehaviour = (int) ActivationBehaviour; Entity.ProfileConfiguration.HotkeyMode = (int) HotkeyMode; Entity.ProfileConfiguration.ProfileCategoryId = Category.Entity.Id; Entity.ProfileConfiguration.FadeInAndOut = FadeInAndOut; Entity.ProfileConfiguration.Order = Order; Icon.Save(); ActivationCondition.Save(); Entity.ProfileConfiguration.ActivationCondition = ActivationCondition.Entity; EnableHotkey?.Save(); Entity.ProfileConfiguration.EnableHotkey = EnableHotkey?.Entity; DisableHotkey?.Save(); Entity.ProfileConfiguration.DisableHotkey = DisableHotkey?.Entity; if (!IsMissingModule) Entity.ProfileConfiguration.ModuleId = Module?.Id; } #endregion } /// /// Represents a type of behaviour when this profile is activated /// public enum ActivationBehaviour { /// /// Do nothing to other profiles /// None, /// /// Disable all other profiles /// DisableOthers, /// /// Disable all other profiles below this one /// DisableOthersBelow, /// /// Disable all other profiles above this one /// DisableOthersAbove, /// /// Disable all other profiles in the same category /// DisableOthersInCategory, /// /// Disable all other profiles below this one in the same category /// DisableOthersBelowInCategory, /// /// Disable all other profiles above this one in the same category /// DisableOthersAboveInCategory } /// /// Represents a hotkey mode for a profile configuration /// public enum ProfileConfigurationHotkeyMode { /// /// Use no hotkeys /// None, /// /// Toggle the profile with one hotkey /// Toggle, /// /// Enable and disable the profile with two separate hotkeys /// EnableDisable }