using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.DeviceProviders;
using Artemis.Storage.Entities.Surface;
using RGB.NET.Core;
using SkiaSharp;
namespace Artemis.Core
{
///
/// Represents an RGB device usable by Artemis, provided by a
///
public class ArtemisDevice : CorePropertyChanged
{
private SKPath? _path;
private SKRect _rectangle;
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface)
{
DeviceEntity = new DeviceEntity();
RgbDevice = rgbDevice;
DeviceProvider = deviceProvider;
Surface = surface;
Rotation = 0;
Scale = 1;
ZIndex = 1;
RedScale = 1;
GreenScale = 1;
BlueScale = 1;
deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath);
LayoutPath = layoutPath;
InputIdentifiers = new List();
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
ApplyToEntity();
CalculateRenderProperties();
}
internal ArtemisDevice(IRGBDevice rgbDevice, DeviceProvider deviceProvider, ArtemisSurface surface, DeviceEntity deviceEntity)
{
DeviceEntity = deviceEntity;
RgbDevice = rgbDevice;
DeviceProvider = deviceProvider;
Surface = surface;
deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath);
LayoutPath = layoutPath;
InputIdentifiers = new List();
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
Leds = rgbDevice.Select(l => new ArtemisLed(l, this)).ToList().AsReadOnly();
LedIds = new ReadOnlyDictionary(Leds.ToDictionary(l => l.RgbLed.Id, l => l));
}
///
/// Gets the rectangle covering the device
///
public SKRect Rectangle
{
get => _rectangle;
private set => SetAndNotify(ref _rectangle, value);
}
///
/// Gets the path surrounding the device
///
public SKPath? Path
{
get => _path;
private set => SetAndNotify(ref _path, value);
}
///
/// Gets the RGB.NET device backing this Artemis device
///
public IRGBDevice RgbDevice { get; }
///
/// Gets the device provider that provided this device
///
public DeviceProvider DeviceProvider { get; }
///
/// Gets the surface containing this device
///
public ArtemisSurface Surface { get; }
///
/// Gets a read only collection containing the LEDs of this device
///
public ReadOnlyCollection Leds { get; }
///
/// Gets a dictionary containing all the LEDs of this device with their corresponding RGB.NET as
/// key
///
public ReadOnlyDictionary LedIds { get; }
///
/// Gets a list of input identifiers associated with this device
///
public List InputIdentifiers { get; }
///
/// Gets or sets the X-position of the device
///
public double X
{
get => DeviceEntity.X;
set
{
DeviceEntity.X = value;
OnPropertyChanged(nameof(X));
}
}
///
/// Gets or sets the Y-position of the device
///
public double Y
{
get => DeviceEntity.Y;
set
{
DeviceEntity.Y = value;
OnPropertyChanged(nameof(Y));
}
}
///
/// Gets or sets the rotation of the device
///
public double Rotation
{
get => DeviceEntity.Rotation;
set
{
DeviceEntity.Rotation = value;
OnPropertyChanged(nameof(Rotation));
}
}
///
/// Gets or sets the scale of the device
///
public double Scale
{
get => DeviceEntity.Scale;
set
{
DeviceEntity.Scale = value;
OnPropertyChanged(nameof(Scale));
}
}
///
/// Gets or sets the Z-index of the device
///
public int ZIndex
{
get => DeviceEntity.ZIndex;
set
{
DeviceEntity.ZIndex = value;
OnPropertyChanged(nameof(ZIndex));
}
}
///
/// Gets or sets the scale of the red color component used for calibration
///
public double RedScale
{
get => DeviceEntity.RedScale;
set
{
DeviceEntity.RedScale = value;
OnPropertyChanged(nameof(RedScale));
}
}
///
/// Gets or sets the scale of the green color component used for calibration
///
public double GreenScale
{
get => DeviceEntity.GreenScale;
set
{
DeviceEntity.GreenScale = value;
OnPropertyChanged(nameof(GreenScale));
}
}
///
/// Gets or sets the scale of the blue color component used for calibration
///
public double BlueScale
{
get => DeviceEntity.BlueScale;
set
{
DeviceEntity.BlueScale = value;
OnPropertyChanged(nameof(BlueScale));
}
}
///
/// Gets the path to where the layout of the device was (attempted to be) loaded from
///
public string? LayoutPath { get; internal set; }
internal DeviceEntity DeviceEntity { get; }
///
public override string ToString()
{
return $"[{RgbDevice.DeviceInfo.DeviceType}] {RgbDevice.DeviceInfo.DeviceName} - {X}.{Y}.{ZIndex}";
}
///
/// Attempts to retrieve the that corresponds the provided RGB.NET
///
/// The RGB.NET to find the corresponding for
/// If found, the corresponding ; otherwise .
public ArtemisLed? GetLed(Led led)
{
return GetLed(led.Id);
}
///
/// Attempts to retrieve the that corresponds the provided RGB.NET
///
/// The RGB.NET to find the corresponding for
/// If found, the corresponding ; otherwise .
public ArtemisLed? GetLed(LedId ledId)
{
LedIds.TryGetValue(ledId, out ArtemisLed? artemisLed);
return artemisLed;
}
internal void ApplyToEntity()
{
// Other properties are computed
DeviceEntity.DeviceIdentifier = RgbDevice.GetDeviceIdentifier();
DeviceEntity.InputIdentifiers.Clear();
foreach (ArtemisDeviceInputIdentifier identifier in InputIdentifiers)
DeviceEntity.InputIdentifiers.Add(new DeviceInputIdentifierEntity
{
InputProvider = identifier.InputProvider,
Identifier = identifier.Identifier
});
}
internal void ApplyToRgbDevice()
{
RgbDevice.Rotation = DeviceEntity.Rotation;
RgbDevice.Scale = DeviceEntity.Scale;
// Workaround for device rotation not applying
if (DeviceEntity.X == 0 && DeviceEntity.Y == 0)
RgbDevice.Location = new Point(1, 1);
RgbDevice.Location = new Point(DeviceEntity.X, DeviceEntity.Y);
InputIdentifiers.Clear();
foreach (DeviceInputIdentifierEntity identifierEntity in DeviceEntity.InputIdentifiers)
InputIdentifiers.Add(new ArtemisDeviceInputIdentifier(identifierEntity.InputProvider, identifierEntity.Identifier));
CalculateRenderProperties();
OnDeviceUpdated();
}
internal void CalculateRenderProperties()
{
Rectangle = RgbDevice.DeviceRectangle.ToSKRect();
if (!Leds.Any())
return;
foreach (ArtemisLed led in Leds)
led.CalculateRectangles();
SKPath path = new() {FillType = SKPathFillType.Winding};
foreach (ArtemisLed artemisLed in Leds)
path.AddRect(artemisLed.AbsoluteRectangle);
Path = path;
}
#region Events
///
/// Occurs when the underlying RGB.NET device was updated
///
public event EventHandler? DeviceUpdated;
///
/// Invokes the event
///
protected virtual void OnDeviceUpdated()
{
DeviceUpdated?.Invoke(this, EventArgs.Empty);
}
#endregion
}
}