// ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using RGB.NET.Core.Layout; namespace RGB.NET.Core { /// /// /// /// Represents a generic RGB-device. /// public abstract class AbstractRGBDevice : AbstractBindable, IRGBDevice where TDeviceInfo : class, IRGBDeviceInfo { #region Properties & Fields /// public abstract TDeviceInfo DeviceInfo { get; } /// IRGBDeviceInfo IRGBDevice.DeviceInfo => DeviceInfo; private Size _size = Size.Invalid; /// public Size Size { get => _size; set => SetProperty(ref _size, value); } private Point _location = new Point(0, 0); /// public Point Location { get => _location; set => SetProperty(ref _location, value); } /// /// Gets or sets if the device needs to be flushed on every update. /// protected bool RequiresFlush { get; set; } = false; /// public DeviceUpdateMode UpdateMode { get; set; } = DeviceUpdateMode.Sync; /// /// Gets a dictionary containing all of the . /// protected Dictionary LedMapping { get; } = new Dictionary(); /// /// Gets a dictionary containing all associated with this . /// protected Dictionary SpecialDeviceParts { get; } = new Dictionary(); #region Indexer /// Led IRGBDevice.this[LedId ledId] => LedMapping.TryGetValue(ledId, out Led led) ? led : null; /// Led IRGBDevice.this[Point location] => LedMapping.Values.FirstOrDefault(x => x.LedRectangle.Contains(location)); /// IEnumerable IRGBDevice.this[Rectangle referenceRect, double minOverlayPercentage] => LedMapping.Values.Where(x => referenceRect.CalculateIntersectPercentage(x.LedRectangle) >= minOverlayPercentage); #endregion #endregion #region Methods /// public virtual void Update(bool flushLeds = false) { // Device-specific updates DeviceUpdate(); // Send LEDs to SDK IEnumerable ledsToUpdate = ((RequiresFlush || flushLeds) ? LedMapping.Values : LedMapping.Values.Where(x => x.IsDirty)).ToList(); foreach (Led ledToUpdate in ledsToUpdate) ledToUpdate.Update(); if (UpdateMode.HasFlag(DeviceUpdateMode.Sync)) UpdateLeds(ledsToUpdate); } /// public virtual void SyncBack() { } /// public virtual void Dispose() { SpecialDeviceParts.Clear(); LedMapping.Clear(); } /// /// Performs device specific updates. /// protected virtual void DeviceUpdate() { } /// /// Sends all the updated to the device. /// protected abstract void UpdateLeds(IEnumerable ledsToUpdate); /// /// Initializes the with the specified id. /// /// The to initialize. /// The representing the position of the to initialize. /// protected virtual Led InitializeLed(LedId ledId, Rectangle ledRectangle) { if ((ledId == LedId.Invalid) || LedMapping.ContainsKey(ledId)) return null; Led led = new Led(this, ledId, ledRectangle, CreateLedCustomData(ledId)); LedMapping.Add(ledId, led); return led; } /// /// Applies the give to the ignoring internal workflows regarding locks and update-requests. /// This should be only used for syncbacks! /// /// The the should be aplied to. /// The to apply. protected virtual void SetLedColorWithoutRequest(Led led, Color color) { if (led == null) return; led.InternalColor = color; } /// /// Applies the given layout. /// /// The file containing the layout. /// The name of the layout used to get the images of the leds. /// If set to true a new led is initialized for every id in the layout if it doesn't already exist. protected virtual DeviceLayout ApplyLayoutFromFile(string layoutPath, string imageLayout, bool createMissingLeds = false) { DeviceLayout layout = DeviceLayout.Load(layoutPath); if (layout != null) { string imageBasePath = string.IsNullOrWhiteSpace(layout.ImageBasePath) ? null : PathHelper.GetAbsolutePath(layout.ImageBasePath); if ((imageBasePath != null) && !string.IsNullOrWhiteSpace(layout.DeviceImage) && (DeviceInfo != null)) DeviceInfo.Image = new Uri(Path.Combine(imageBasePath, layout.DeviceImage), UriKind.Absolute); LedImageLayout ledImageLayout = layout.LedImageLayouts.FirstOrDefault(x => string.Equals(x.Layout, imageLayout, StringComparison.OrdinalIgnoreCase)); Size = new Size(layout.Width, layout.Height); if (layout.Leds != null) foreach (LedLayout layoutLed in layout.Leds) { if (Enum.TryParse(layoutLed.Id, true, out LedId ledId)) { if (!LedMapping.TryGetValue(ledId, out Led led) && createMissingLeds) led = InitializeLed(ledId, new Rectangle()); if (led != null) { led.LedRectangle.Location = new Point(layoutLed.X, layoutLed.Y); led.LedRectangle.Size = new Size(layoutLed.Width, layoutLed.Height); led.Shape = layoutLed.Shape; led.ShapeData = layoutLed.ShapeData; LedImage image = ledImageLayout?.LedImages.FirstOrDefault(x => x.Id == layoutLed.Id); if ((imageBasePath != null) && !string.IsNullOrEmpty(image?.Image)) led.Image = new Uri(Path.Combine(imageBasePath, image.Image), UriKind.Absolute); } } } } return layout; } /// /// Creates provider-specific data associated with this . /// /// The . protected virtual object CreateLedCustomData(LedId ledId) => null; /// public void AddSpecialDevicePart(T specialDevicePart) where T : class, IRGBDeviceSpecialPart => SpecialDeviceParts[typeof(T)] = specialDevicePart; /// public T GetSpecialDevicePart() where T : class, IRGBDeviceSpecialPart => SpecialDeviceParts.TryGetValue(typeof(T), out IRGBDeviceSpecialPart devicePart) ? (T)devicePart : default(T); #region Enumerator /// /// /// Returns an enumerator that iterates over all of the . /// /// An enumerator for all of the . public IEnumerator GetEnumerator() => LedMapping.Values.GetEnumerator(); /// /// /// Returns an enumerator that iterates over all of the . /// /// An enumerator for all of the . IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #endregion } }