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 bool _isDisposed = false;
private readonly double _defaultUpdateRateHardLimit;
///
public bool IsInitialized { get; protected set; }
///
public bool ThrowsExceptions { get; protected set; }
///
/// The list of devices managed by this device-provider.
///
protected List InternalDevices { get; } = [];
///
public virtual IReadOnlyList Devices => new ReadOnlyCollection(InternalDevices);
///
/// Gets the dictionary containing the registered update triggers.
/// Normally should be used to access them.
///
protected Dictionary UpdateTriggerMapping { get; } = [];
///
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;
///
public event EventHandler? DevicesChanged;
#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;
}
~AbstractRGBDeviceProvider() => Dispose(false);
#endregion
#region Methods
///
public bool Initialize(RGBDeviceType loadFilter = RGBDeviceType.All, bool throwExceptions = false)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
ThrowsExceptions = throwExceptions;
try
{
Reset();
InitializeSDK();
foreach (IRGBDevice device in GetLoadedDevices(loadFilter))
AddDevice(device);
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)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
List devices = [];
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 (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
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()
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
foreach (IDeviceUpdateTrigger updateTrigger in UpdateTriggerMapping.Values)
updateTrigger.Dispose();
foreach (IRGBDevice device in Devices)
device.Dispose();
List devices = [..InternalDevices];
foreach (IRGBDevice device in devices)
RemoveDevice(device);
UpdateTriggerMapping.Clear();
IsInitialized = false;
}
///
/// Adds the provided device to the list of managed devices.
///
/// The device to add.
/// true if the device was added successfully; otherwise false.
protected virtual bool AddDevice(IRGBDevice device)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
if (InternalDevices.Contains(device)) return false;
InternalDevices.Add(device);
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesAddedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
return true;
}
///
/// Removes the provided device from the list of managed devices.
///
/// The device to remove.
/// true if the device was removed successfully; otherwise false.
protected virtual bool RemoveDevice(IRGBDevice device)
{
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
if (!InternalDevices.Remove(device)) return false;
try { OnDevicesChanged(DevicesChangedEventArgs.CreateDevicesRemovedArgs(device)); } catch { /* we don't want to throw due to bad event handlers */ }
return true;
}
///
/// 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.
public 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);
///
/// Throws the -event.
///
/// The parameters passed to the event.
protected virtual void OnDevicesChanged(DevicesChangedEventArgs args) => DevicesChanged?.Invoke(this, args);
///
public void Dispose()
{
if (_isDisposed) return;
try
{
Dispose(true);
}
catch { /* don't throw in dispose! */ }
GC.SuppressFinalize(this);
_isDisposed = true;
}
///
/// Disposes the object and frees all resources.
///
/// true if explicitely called through the Dispose-Method, false if called by the destructor.
protected virtual void Dispose(bool disposing) => Reset();
#endregion
}