// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace RGB.NET.Core
{
///
/// Represents a RGB-surface containing multiple devices.
///
public partial class RGBSurface : AbstractBindable, IDisposable
{
#region Properties & Fields
///
/// Gets the singelot-instance of the class.
///
public static RGBSurface Instance { get; } = new RGBSurface();
private DateTime _lastUpdate;
private IList _deviceProvider = new List();
private IList _devices = new List();
// ReSharper disable InconsistentNaming
private readonly LinkedList _ledGroups = new LinkedList();
private readonly Rectangle _surfaceRectangle = new Rectangle();
// ReSharper restore InconsistentNaming
///
/// Gets a readonly list containing all loaded .
///
public IEnumerable Devices => new ReadOnlyCollection(_devices);
///
/// Gets a copy of the representing this .
///
public Rectangle SurfaceRectangle => new Rectangle(_surfaceRectangle);
///
/// Gets a list of all on this .
///
public IEnumerable Leds => _devices.SelectMany(x => x);
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
private RGBSurface()
{
_lastUpdate = DateTime.Now;
}
#endregion
#region Methods
///
/// Perform an update for all dirty , or all , if flushLeds is set to true.
///
/// Specifies whether all , (including clean ones) should be updated.
public void Update(bool flushLeds = false)
{
try
{
OnUpdating();
lock (_ledGroups)
{
// Update effects
foreach (ILedGroup ledGroup in _ledGroups)
try { ledGroup.UpdateEffects(); }
catch (Exception ex) { OnException(ex); }
// Render brushes
foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex))
try { Render(ledGroup); }
catch (Exception ex) { OnException(ex); }
}
foreach (IRGBDevice device in Devices)
try { device.Update(flushLeds); }
catch (Exception ex) { OnException(ex); }
OnUpdated();
}
catch (Exception ex)
{
OnException(ex);
}
}
///
public void Dispose()
{
if (_updateTokenSource?.IsCancellationRequested == false)
_updateTokenSource.Cancel();
foreach (IRGBDevice device in _devices)
try { device.Dispose(); }
catch { /* We do what we can */ }
_ledGroups.Clear();
_devices = null;
_deviceProvider = null;
}
///
/// Renders a ledgroup.
///
/// The led group to render.
private void Render(ILedGroup ledGroup)
{
IList leds = ledGroup.GetLeds().ToList();
IBrush brush = ledGroup.Brush;
if ((brush == null) || !brush.IsEnabled) return;
switch (brush.BrushCalculationMode)
{
case BrushCalculationMode.Relative:
Rectangle brushRectangle = new Rectangle(leds.Select(x => GetDeviceLedLocation(x)));
Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y);
brushRectangle.Location.X = 0;
brushRectangle.Location.Y = 0;
brush.PerformRender(brushRectangle,
leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x, offset))));
break;
case BrushCalculationMode.Absolute:
brush.PerformRender(SurfaceRectangle, leds.Select(x => new BrushRenderTarget(x, GetDeviceLedLocation(x))));
break;
default:
throw new ArgumentException();
}
brush.UpdateEffects();
brush.PerformFinalize();
foreach (KeyValuePair renders in brush.RenderedTargets)
renders.Key.Led.Color = renders.Value;
}
private Rectangle GetDeviceLedLocation(Led led, Point extraOffset = null)
{
return extraOffset != null
? new Rectangle(led.LedRectangle.Location + led.Device.Location + extraOffset, led.LedRectangle.Size)
: new Rectangle(led.LedRectangle.Location + led.Device.Location, led.LedRectangle.Size);
}
///
/// Attaches the given .
///
/// The to attach.
/// true if the could be attached; otherwise, false.
public bool AttachLedGroup(ILedGroup ledGroup)
{
if (ledGroup == null) return false;
lock (_ledGroups)
{
if (_ledGroups.Contains(ledGroup)) return false;
_ledGroups.AddLast(ledGroup);
ledGroup.OnAttach();
return true;
}
}
///
/// Detaches the given .
///
/// The to detached.
/// true if the could be detached; otherwise, false.
public bool DetachLedGroup(ILedGroup ledGroup)
{
if (ledGroup == null) return false;
lock (_ledGroups)
{
LinkedListNode node = _ledGroups.Find(ledGroup);
if (node == null) return false;
_ledGroups.Remove(node);
node.Value.OnDetach();
return true;
}
}
private void UpdateSurfaceRectangle()
{
Rectangle devicesRectangle = new Rectangle(_devices.Select(d => new Rectangle(d.Location, d.Size)));
_surfaceRectangle.Size.Width = devicesRectangle.Location.X + devicesRectangle.Size.Width;
_surfaceRectangle.Size.Height = devicesRectangle.Location.Y + devicesRectangle.Size.Height;
}
#endregion
}
}