// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedMember.Global
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
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 Stopwatch _deltaTimeCounter;
private IList _deviceProvider = new List();
private IList _devices = new List();
private IList _updateTriggers = 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 readonly list containing all registered .
///
public IEnumerable UpdateTriggers => new ReadOnlyCollection(_updateTriggers);
///
/// 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()
{
_deltaTimeCounter = Stopwatch.StartNew();
}
#endregion
#region Methods
///
/// Perform a full update for all devices. Updates only dirty by default, or all , if flushLeds is set to true.
///
/// Specifies whether all , (including clean ones) should be updated.
public void Update(bool flushLeds = false) => Update(null, new CustomUpdateData(("flushLeds", flushLeds)));
private void Update(object updateTrigger, CustomUpdateData customData) => Update(updateTrigger as IUpdateTrigger, customData);
private void Update(IUpdateTrigger updateTrigger, CustomUpdateData customData)
{
if (customData == null)
customData = new CustomUpdateData();
try
{
bool flushLeds = customData["flushLeds"] as bool? ?? false;
bool syncBack = customData["syncBack"] as bool? ?? true;
bool render = customData["render"] as bool? ?? true;
bool updateDevices = customData["updateDevices"] as bool? ?? true;
lock (_updateTriggers)
{
OnUpdating(updateTrigger, customData);
if (syncBack)
foreach (IRGBDevice device in Devices)
if (device.UpdateMode.HasFlag(DeviceUpdateMode.SyncBack) && device.DeviceInfo.SupportsSyncBack)
try { device.SyncBack(); }
catch (Exception ex) { OnException(ex); }
if (render)
lock (_ledGroups)
{
// Render brushes
foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex))
try { Render(ledGroup); }
catch (Exception ex) { OnException(ex); }
}
if (updateDevices)
foreach (IRGBDevice device in Devices)
if (!device.UpdateMode.HasFlag(DeviceUpdateMode.NoUpdate))
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 */ }
foreach (IRGBDeviceProvider deviceProvider in _deviceProvider)
try { deviceProvider.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(led => led.AbsoluteLedRectangle));
Point offset = new Point(-brushRectangle.Location.X, -brushRectangle.Location.Y);
brushRectangle.Location = new Point(0, 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, x.AbsoluteLedRectangle)));
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)
{
Rectangle absoluteRectangle = led.AbsoluteLedRectangle;
return (absoluteRectangle.Location + extraOffset) + absoluteRectangle.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.Width = devicesRectangle.Location.X + devicesRectangle.Size.Width;
_surfaceRectangle.Height = devicesRectangle.Location.Y + devicesRectangle.Size.Height;
}
///
/// Gets all devices of a specific type.
///
/// The type of devices to get.
/// A list of devices with the specified type.
public IList GetDevices()
where T : class
=> new ReadOnlyCollection(_devices.Select(x => x as T).Where(x => x != null).ToList());
///
/// Gets all devices of the specified .
///
/// The of the devices to get.
/// a list of devices matching the specified .
public IList GetDevices(RGBDeviceType deviceType)
=> new ReadOnlyCollection(_devices.Where(x => x.DeviceInfo.DeviceType == deviceType).ToList());
///
/// Registers the provided .
///
/// The to register.
public void RegisterUpdateTrigger(IUpdateTrigger updateTrigger)
{
if (!_updateTriggers.Contains(updateTrigger))
{
_updateTriggers.Add(updateTrigger);
updateTrigger.Update += Update;
}
}
///
/// Unregisters the provided .
///
/// The to unregister.
public void UnregisterUpdateTrigger(IUpdateTrigger updateTrigger)
{
if (_updateTriggers.Remove(updateTrigger))
updateTrigger.Update -= Update;
}
#endregion
}
}