using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace RGB.NET.Core;
///
/// Represents the abstract base implementation for a .
///
public abstract class AbstractRGBDeviceProvider : IRGBDeviceProvider
{
#region Properties & Fields
private readonly double _defaultUpdateRateHardLimit;
///
public bool IsInitialized { get; protected set; }
///
public bool ThrowsExceptions { get; protected set; }
///
public virtual IEnumerable Devices { get; protected set; } = Enumerable.Empty();
///
/// Gets the dictionary containing the registered update triggers.
/// Normally should be used to access them.
///
protected Dictionary UpdateTriggerMapping { get; } = new();
///
public IReadOnlyList<(int id, IDeviceUpdateTrigger trigger)> UpdateTriggers => new ReadOnlyCollection<(int id, IDeviceUpdateTrigger trigger)>(UpdateTriggerMapping.Select(x => (x.Key, x.Value)).ToList());
#endregion
#region Events
///
public event EventHandler? Exception;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The update rate hard limit all update triggers for this device provider are initialized with.
protected AbstractRGBDeviceProvider(double defaultUpdateRateHardLimit = 0)
{
this._defaultUpdateRateHardLimit = defaultUpdateRateHardLimit;
}
#endregion
#region Methods
///
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
ThrowsExceptions = throwExceptions;
try
{
Reset();
InitializeSDK();
Devices = new ReadOnlyCollection(GetLoadedDevices(loadFilter).ToList());
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
updateTrigger.Start();
IsInitialized = true;
}
catch (DeviceProviderException)
{
Reset();
throw;
}
catch (Exception ex)
{
Reset();
Throw(ex, true);
return false;
}
return true;
}
///
/// Loads devices and returns a filtered list of them.
///
///
/// The underlying loading of the devices happens in .
///
/// -flags to filter the device with.
/// The filtered collection of loaded devices.
protected virtual IEnumerable GetLoadedDevices(RGBDeviceType loadFilter)
{
List devices = new();
foreach (IRGBDevice device in LoadDevices())
{
try
{
if (loadFilter.HasFlag(device.DeviceInfo.DeviceType))
devices.Add(device);
else
device.Dispose();
}
catch (Exception ex)
{
Throw(ex);
}
}
return devices;
}
///
/// Initializes the underlying SDK.
///
protected abstract void InitializeSDK();
///
/// Loads all devices this device provider is capable of loading.
///
///
/// Filtering happens in .
///
/// A collection of loaded devices.
protected abstract IEnumerable LoadDevices();
///
/// Gets the mapped to the specified id or a new one if the id wasn't requested before.
///
///
/// The creation of the update trigger happens in .
///
/// The id of the update trigger.
/// The update rate hard limit to be set in the update trigger.
/// The update trigger mapped to the specified id.
protected virtual IDeviceUpdateTrigger GetUpdateTrigger(int id = -1, double? updateRateHardLimit = null)
{
if (!UpdateTriggerMapping.TryGetValue(id, out IDeviceUpdateTrigger? updaeTrigger))
UpdateTriggerMapping[id] = (updaeTrigger = CreateUpdateTrigger(id, updateRateHardLimit ?? _defaultUpdateRateHardLimit));
return updaeTrigger;
}
///
/// Creates a update trigger with the specified id and the specified update rate hard limit.
///
/// The id of the update trigger.
/// The update rate hard limit tobe set in the update trigger.
/// The newly created update trigger.
protected virtual IDeviceUpdateTrigger CreateUpdateTrigger(int id, double updateRateHardLimit) => new DeviceUpdateTrigger(updateRateHardLimit);
///
/// Resets the device provider and disposes all devices and update triggers.
///
protected virtual void Reset()
{
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
updateTrigger.Dispose();
foreach (IRGBDevice device in Devices)
device.Dispose();
Devices = Enumerable.Empty();
UpdateTriggerMapping.Clear();
IsInitialized = false;
}
///
/// Triggers the -event and throws the specified exception if is true and it is not overriden in the event.
///
/// The exception to throw.
/// Indicates if the exception is critical for device provider to work correctly.
protected virtual void Throw(Exception ex, bool isCritical = false)
{
ExceptionEventArgs args = new(ex, isCritical, ThrowsExceptions);
try { OnException(args); } catch { /* we don't want to throw due to bad event handlers */ }
if (args.Throw)
throw new DeviceProviderException(ex, isCritical);
}
///
/// Throws the event.
///
/// The parameters passed to the event.
protected virtual void OnException(ExceptionEventArgs args) => Exception?.Invoke(this, args);
///
public virtual void Dispose()
{
Reset();
GC.SuppressFinalize(this);
}
#endregion
}