using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile.AdaptionHints; using RGB.NET.Core; namespace Artemis.Core; /// /// Represents an adapter that adapts a layer to a certain set of devices using s /// public class LayerAdapter : IStorageModel { private readonly List _adaptionHints; internal LayerAdapter(Layer layer) { _adaptionHints = new List(); Layer = layer; AdaptionHints = new ReadOnlyCollection(_adaptionHints); } /// /// Gets the layer this adapter can adapt /// public Layer Layer { get; } /// /// Gets or sets a list containing the adaption hints used by this adapter /// public ReadOnlyCollection AdaptionHints { get; set; } /// /// Modifies the layer, adapting it to the provided /// /// The devices to adapt the layer to public void Adapt(List devices) { // Use adaption hints if provided if (AdaptionHints.Any()) { foreach (IAdaptionHint adaptionHint in AdaptionHints) adaptionHint.Apply(Layer, devices); } // If there are no hints, try to find matching LEDs anyway else { List availableLeds = devices.SelectMany(d => d.Leds).ToList(); List usedLeds = new(); foreach (LedEntity ledEntity in Layer.LayerEntity.Leds) { // TODO: If this is a keyboard LED and the layouts don't match, convert it before looking for it on the devices LedId ledId = Enum.Parse(ledEntity.LedName); ArtemisLed? led = availableLeds.FirstOrDefault(l => l.RgbLed.Id == ledId); if (led != null) { availableLeds.Remove(led); usedLeds.Add(led); } } Layer.AddLeds(usedLeds); } } /// /// Automatically determine hints for this layer /// public List DetermineHints(IEnumerable devices) { List newHints = new(); if (devices.All(DoesLayerCoverDevice)) { DeviceAdaptionHint hint = new() {DeviceType = RGBDeviceType.All}; Add(hint); newHints.Add(hint); } else { // Any fully covered device will add a device adaption hint for that type foreach (IGrouping deviceLeds in Layer.Leds.GroupBy(l => l.Device)) { ArtemisDevice device = deviceLeds.Key; // If there is already an adaption hint for this type, don't add another if (AdaptionHints.Any(h => h is DeviceAdaptionHint d && d.DeviceType == device.DeviceType)) continue; if (DoesLayerCoverDevice(device)) { DeviceAdaptionHint hint = new() {DeviceType = device.DeviceType}; Add(hint); newHints.Add(hint); } } // Any fully covered category will add a category adaption hint for its category foreach (DeviceCategory deviceCategory in Enum.GetValues()) { if (AdaptionHints.Any(h => h is CategoryAdaptionHint c && c.Category == deviceCategory)) continue; List categoryDevices = devices.Where(d => d.Categories.Contains(deviceCategory)).ToList(); if (categoryDevices.Any() && categoryDevices.All(DoesLayerCoverDevice)) { CategoryAdaptionHint hint = new() {Category = deviceCategory}; Add(hint); newHints.Add(hint); } } // A single LED assignment is turned into a hint for one matching LED ID on the same device type if (Layer.Leds.Count == 1) { ArtemisLed led = Layer.Leds.Single(); SingleLedAdaptionHint hint = new() {LedId = led.RgbLed.Id, Amount = 1, LimitAmount = true}; Add(hint); newHints.Add(hint); } } return newHints; } /// /// Adds an adaption hint to the adapter. /// /// The adaption hint to add. public void Add(IAdaptionHint adaptionHint) { if (_adaptionHints.Contains(adaptionHint)) return; _adaptionHints.Add(adaptionHint); AdapterHintAdded?.Invoke(this, new LayerAdapterHintEventArgs(adaptionHint)); } /// /// Removes the first occurrence of a specific adaption hint from the adapter. /// /// The adaption hint to remove. public void Remove(IAdaptionHint adaptionHint) { if (_adaptionHints.Remove(adaptionHint)) AdapterHintRemoved?.Invoke(this, new LayerAdapterHintEventArgs(adaptionHint)); } /// /// Removes all adaption hints from the adapter. /// public void Clear() { while (_adaptionHints.Any()) Remove(_adaptionHints.First()); } /// /// Occurs whenever a new adapter hint is added to the adapter. /// public event EventHandler? AdapterHintAdded; /// /// Occurs whenever an adapter hint is removed from the adapter. /// public event EventHandler? AdapterHintRemoved; private bool DoesLayerCoverDevice(ArtemisDevice device) { return device.Leds.All(l => Layer.Leds.Contains(l)); } #region Implementation of IStorageModel /// public void Load() { _adaptionHints.Clear(); // Kind of meh. // This leaves the adapter responsible for finding the right hint for the right entity, but it's gotta be done somewhere.. foreach (IAdaptionHintEntity hintEntity in Layer.LayerEntity.AdaptionHints) { switch (hintEntity) { case DeviceAdaptionHintEntity entity: Add(new DeviceAdaptionHint(entity)); break; case CategoryAdaptionHintEntity entity: Add(new CategoryAdaptionHint(entity)); break; case KeyboardSectionAdaptionHintEntity entity: Add(new KeyboardSectionAdaptionHint(entity)); break; case SingleLedAdaptionHintEntity entity: Add(new SingleLedAdaptionHint(entity)); break; } } } /// public void Save() { Layer.LayerEntity.AdaptionHints.Clear(); foreach (IAdaptionHint adaptionHint in AdaptionHints) Layer.LayerEntity.AdaptionHints.Add(adaptionHint.GetEntry()); } #endregion }