using System;
using System.Collections.Generic;
using System.Linq;
namespace RGB.NET.Core
{
///
/// Represents a generic RGB-surface.
///
public class RGBSurface : AbstractLedGroup, IRGBSurface
{
#region Properties & Fields
private DateTime _lastUpdate;
///
public Dictionary Devices { get; } = new Dictionary();
private readonly LinkedList _ledGroups = new LinkedList();
///
public Rectangle SurfaceRectangle => new Rectangle(Devices.Select(x => x.Key.DeviceRectangle));
#endregion
#region Events
// ReSharper disable EventNeverSubscribedTo.Global
///
public event ExceptionEventHandler Exception;
///
public event UpdatingEventHandler Updating;
///
public event UpdatedEventHandler Updated;
// ReSharper restore EventNeverSubscribedTo.Global
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
public RGBSurface()
{
_lastUpdate = DateTime.Now;
}
#endregion
#region Methods
///
public void Update(bool flushLeds = false)
{
OnUpdating();
lock (_ledGroups)
{
// Update effects
foreach (ILedGroup ledGroup in _ledGroups)
ledGroup.UpdateEffects();
// Render brushes
Render(this);
foreach (ILedGroup ledGroup in _ledGroups.OrderBy(x => x.ZIndex))
Render(ledGroup);
}
foreach (IRGBDevice device in Devices.Keys)
device.Update(flushLeds);
OnUpdated();
}
///
/// Renders a ledgroup.
///
/// The led group to render.
private void Render(ILedGroup ledGroup)
{
IList leds = ledGroup.GetLeds().ToList();
IBrush brush = ledGroup.Brush;
try
{
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;
}
// ReSharper disable once CatchAllClause
catch (Exception ex)
{
OnException(ex);
}
}
private Rectangle GetDeviceLedLocation(Led led, Point extraOffset = null)
{
Point deviceLocation;
if (!Devices.TryGetValue(led.Device, out deviceLocation))
deviceLocation = new Point();
return extraOffset != null
? new Rectangle(led.LedRectangle.Location + deviceLocation + extraOffset, led.LedRectangle.Size)
: new Rectangle(led.LedRectangle.Location + deviceLocation, led.LedRectangle.Size);
}
///
public void PositionDevice(IRGBDevice device, Point location)
{
if (device == null) return;
lock (Devices)
Devices[device] = location ?? new Point();
}
///
/// Attaches the given .
///
/// The to attach.
/// true if the could be attached; otherwise, false.
public bool AttachLedGroup(ILedGroup ledGroup)
{
if (ledGroup is IRGBSurface) return false;
if (ledGroup == null) return false;
lock (_ledGroups)
{
if (_ledGroups.Contains(ledGroup)) return false;
_ledGroups.AddLast(ledGroup);
return true;
}
}
///
/// Detaches the given .
///
/// The to detached.
/// true if the could be detached; otherwise, false.
public bool DetachLedGroup(ILedGroup ledGroup)
{
if (ledGroup is IRGBSurface) return false;
if (ledGroup == null) return false;
lock (_ledGroups)
{
LinkedListNode node = _ledGroups.Find(ledGroup);
if (node == null) return false;
_ledGroups.Remove(node);
return true;
}
}
///
public override IEnumerable GetLeds()
{
return Devices.SelectMany(d => d.Key);
}
#region EventCaller
///
/// Handles the needed event-calls for an exception.
///
/// The exception previously thrown.
protected virtual void OnException(Exception ex)
{
try
{
Exception?.Invoke(this, new ExceptionEventArgs(ex));
}
catch
{
// Well ... that's not my fault
}
}
///
/// Handles the needed event-calls before updating.
///
protected virtual void OnUpdating()
{
try
{
long lastUpdateTicks = _lastUpdate.Ticks;
_lastUpdate = DateTime.Now;
Updating?.Invoke(this, new UpdatingEventArgs((DateTime.Now.Ticks - lastUpdateTicks) / 10000000.0));
}
catch
{
// Well ... that's not my fault
}
}
///
/// Handles the needed event-calls after an update.
///
protected virtual void OnUpdated()
{
try
{
Updated?.Invoke(this, new UpdatedEventArgs());
}
catch
{
// Well ... that's not my fault
}
}
#endregion
#endregion
}
}