using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using OwlCore.Events;
using OwlCore.Extensions;
using StrixMusic.Cores.Files.Services;
using StrixMusic.Sdk.CoreModels;
using StrixMusic.Sdk.Extensions;
using StrixMusic.Sdk.FileMetadata;
using StrixMusic.Sdk.FileMetadata.Models;
namespace StrixMusic.Cores.Files.Models
{
///
public sealed class FilesCoreLibrary : FilesCorePlayableCollectionGroupBase, ICoreLibrary
{
private readonly SemaphoreSlim _initSemaphore = new(1, 1);
private IFileMetadataManager? _fileMetadataManager;
///
/// Initializes a new instance of the class.
///
/// The core that created this instance.
public FilesCoreLibrary(ICore sourceCore)
: base(sourceCore)
{
AttachEvents();
}
///
public override async Task InitAsync(CancellationToken cancellationToken = default)
{
using var initReleaseRegistration = cancellationToken.Register(() => _initSemaphore.Release());
await _initSemaphore.WaitAsync(cancellationToken);
_fileMetadataManager = SourceCore.Cast().FileMetadataManager;
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
AttachEvents(_fileMetadataManager);
TotalAlbumItemsCount = await _fileMetadataManager.Albums.GetItemCount();
TotalArtistItemsCount = await _fileMetadataManager.Artists.GetItemCount();
TotalPlaylistItemsCount = await _fileMetadataManager.Playlists.GetItemCount();
TotalTrackCount = await _fileMetadataManager.Tracks.GetItemCount();
await base.InitAsync(cancellationToken);
IsInitialized = true;
_initSemaphore.Release();
}
private void AttachEvents()
{
base.TracksCountChanged += FilesCoreLibrary_TracksCountChanged;
base.ArtistItemsCountChanged += FilesCoreLibrary_ArtistItemsCountChanged;
base.AlbumItemsCountChanged += FilesCoreLibrary_AlbumItemsCountChanged;
base.PlaylistItemsCountChanged += FilesCoreLibrary_PlaylistItemsCountChanged;
base.ImagesCountChanged += FilesCoreLibrary_ImagesCountChanged;
}
private void DetachEvents()
{
base.TracksCountChanged -= FilesCoreLibrary_TracksCountChanged;
base.ArtistItemsCountChanged -= FilesCoreLibrary_ArtistItemsCountChanged;
base.AlbumItemsCountChanged -= FilesCoreLibrary_AlbumItemsCountChanged;
base.PlaylistItemsCountChanged -= FilesCoreLibrary_PlaylistItemsCountChanged;
base.ImagesCountChanged -= FilesCoreLibrary_ImagesCountChanged;
}
private void AttachEvents(IFileMetadataManager fileMetadataManager)
{
fileMetadataManager.Tracks.MetadataAdded += Tracks_MetadataAdded;
fileMetadataManager.Albums.MetadataAdded += Albums_MetadataAdded;
fileMetadataManager.Artists.MetadataAdded += Artists_MetadataAdded;
fileMetadataManager.Playlists.MetadataAdded += Playlists_MetadataAdded;
fileMetadataManager.Tracks.MetadataRemoved += Tracks_MetadataRemoved;
fileMetadataManager.Albums.MetadataRemoved += Albums_MetadataRemoved;
fileMetadataManager.Artists.MetadataRemoved += Artists_MetadataRemoved;
fileMetadataManager.Playlists.MetadataRemoved += Playlists_MetadataRemoved;
}
private void DetachEvents(IFileMetadataManager fileMetadataManager)
{
fileMetadataManager.Tracks.MetadataAdded -= Tracks_MetadataAdded;
fileMetadataManager.Albums.MetadataAdded -= Albums_MetadataAdded;
fileMetadataManager.Artists.MetadataAdded -= Artists_MetadataAdded;
fileMetadataManager.Playlists.MetadataAdded -= Playlists_MetadataAdded;
fileMetadataManager.Tracks.MetadataRemoved -= Tracks_MetadataRemoved;
fileMetadataManager.Albums.MetadataRemoved -= Albums_MetadataRemoved;
fileMetadataManager.Artists.MetadataRemoved -= Artists_MetadataRemoved;
fileMetadataManager.Playlists.MetadataRemoved -= Playlists_MetadataRemoved;
}
private void FilesCoreLibrary_PlaylistItemsCountChanged(object sender, int e)
{
PlaylistItemsCountChanged?.Invoke(sender, e);
}
private void FilesCoreLibrary_AlbumItemsCountChanged(object sender, int e)
{
AlbumItemsCountChanged?.Invoke(sender, e);
}
private void FilesCoreLibrary_ArtistItemsCountChanged(object sender, int e)
{
ArtistItemsCountChanged?.Invoke(sender, e);
}
private void FilesCoreLibrary_ImagesCountChanged(object sender, int e)
{
ImagesCountChanged?.Invoke(this, e);
}
private void FilesCoreLibrary_TracksCountChanged(object sender, int e)
{
TracksCountChanged?.Invoke(sender, e);
}
private void Playlists_MetadataAdded(object sender, IEnumerable e)
{
// ReSharper disable once CollectionNeverUpdated.Local
var removedItems = new List>();
var addedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
addedItems.Add(new CollectionChangedItem(InstanceCache.Playlists.GetOrCreate(item.Id, SourceCore, item), addedItems.Count));
}
TotalPlaylistItemsCount += addedItems.Count - removedItems.Count;
PlaylistItemsChanged?.Invoke(this, addedItems, removedItems);
PlaylistItemsCountChanged?.Invoke(this, TotalPlaylistItemsCount);
}
private void Tracks_MetadataAdded(object sender, IEnumerable e)
{
// ReSharper disable once CollectionNeverUpdated.Local
var removedItems = new List>();
var addedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
addedItems.Add(new CollectionChangedItem(InstanceCache.Tracks.GetOrCreate(item.Id, SourceCore, item), addedItems.Count));
}
TotalTrackCount += addedItems.Count - removedItems.Count;
TracksChanged?.Invoke(this, addedItems, removedItems);
TracksCountChanged?.Invoke(this, TotalTrackCount);
}
private void Artists_MetadataAdded(object sender, IEnumerable e)
{
// ReSharper disable once CollectionNeverUpdated.Local
var removedItems = new List>();
var addedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
addedItems.Add(new CollectionChangedItem(InstanceCache.Artists.GetOrCreate(item.Id, SourceCore, item), addedItems.Count));
}
TotalArtistItemsCount += addedItems.Count - removedItems.Count;
ArtistItemsChanged?.Invoke(this, addedItems, removedItems);
ArtistItemsCountChanged?.Invoke(this, TotalAlbumItemsCount);
}
private void Albums_MetadataAdded(object sender, IEnumerable e)
{
// ReSharper disable once CollectionNeverUpdated.Local
var removedItems = new List>();
var addedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
addedItems.Add(new CollectionChangedItem(InstanceCache.Albums.GetOrCreate(item.Id, SourceCore, item), addedItems.Count));
}
TotalAlbumItemsCount += addedItems.Count - removedItems.Count;
AlbumItemsChanged?.Invoke(this, addedItems, removedItems);
AlbumItemsCountChanged?.Invoke(this, TotalAlbumItemsCount);
}
private void Tracks_MetadataRemoved(object sender, IEnumerable e)
{
var addedItems = new List>();
var removedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
TotalTrackCount--;
TracksCountChanged?.Invoke(this, TotalTrackCount);
if (!InstanceCache.Tracks.HasId(item.Id))
continue;
removedItems.Add(new CollectionChangedItem(InstanceCache.Tracks.GetOrCreate(item.Id, SourceCore, item), removedItems.Count));
TracksChanged?.Invoke(this, addedItems, removedItems);
InstanceCache.Tracks.Remove(item.Id);
}
}
private void Artists_MetadataRemoved(object sender, IEnumerable e)
{
var addedItems = new List>();
var removedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
TotalArtistItemsCount--;
ArtistItemsCountChanged?.Invoke(this, TotalArtistItemsCount);
if (!InstanceCache.Artists.HasId(item.Id))
continue;
removedItems.Add(new CollectionChangedItem(InstanceCache.Artists.GetOrCreate(item.Id, SourceCore, item), removedItems.Count));
ArtistItemsChanged?.Invoke(this, addedItems, removedItems);
InstanceCache.Artists.Remove(item.Id);
}
}
private void Albums_MetadataRemoved(object sender, IEnumerable e)
{
var addedItems = new List>();
var removedItems = new List>();
foreach (var item in e)
{
Guard.IsNotNullOrWhiteSpace(item.Id, nameof(item.Id));
TotalAlbumItemsCount--;
AlbumItemsCountChanged?.Invoke(this, TotalAlbumItemsCount);
if (!InstanceCache.Albums.HasId(item.Id))
continue;
removedItems.Add(new CollectionChangedItem(InstanceCache.Albums.GetOrCreate(item.Id, SourceCore, item), removedItems.Count));
AlbumItemsChanged?.Invoke(this, addedItems, removedItems);
InstanceCache.Albums.Remove(item.Id);
}
}
private void Playlists_MetadataRemoved(object sender, IEnumerable e)
{
// TODO. Need to get the index of each item being removed.
// Remember to remove from instance cache and dispose the objects being removed after emitted.
}
///
/// Determines if collection base is initialized or not.
///
public override bool IsInitialized { get; protected set; }
///
public override string Id { get; protected set; } = "library";
///
public override string Name { get; protected set; } = "Library";
///
public override string? Description { get; protected set; } = null;
/// ?
public override event CollectionChangedEventHandler? PlaylistItemsChanged;
///
public override event CollectionChangedEventHandler? AlbumItemsChanged;
///
public override event CollectionChangedEventHandler? ArtistItemsChanged;
///
public override event CollectionChangedEventHandler? TracksChanged;
///
public override event EventHandler? AlbumItemsCountChanged;
///
public override event EventHandler? ArtistItemsCountChanged;
///
public override event EventHandler? TracksCountChanged;
///
public override event EventHandler? PlaylistItemsCountChanged;
///
public override event EventHandler? ChildrenCountChanged;
///
public override event EventHandler? ImagesCountChanged;
///
public override IAsyncEnumerable GetChildrenAsync(int limit, int offset, CancellationToken cancellationToken = default)
{
return AsyncEnumerable.Empty();
}
///
public override async IAsyncEnumerable GetPlaylistItemsAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var playlistsMetadata = await _fileMetadataManager.Playlists.GetItemsAsync(offset, limit);
foreach (var playList in playlistsMetadata)
{
Guard.IsNotNullOrWhiteSpace(playList.Id, nameof(playList.Id));
yield return InstanceCache.Playlists.GetOrCreate(playList.Id, SourceCore, playList);
}
}
///
public override async IAsyncEnumerable GetAlbumItemsAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var albumMetadata = await _fileMetadataManager.Albums.GetItemsAsync(offset, limit);
foreach (var album in albumMetadata)
{
Guard.IsNotNull(album.Id, nameof(album.Id));
yield return InstanceCache.Albums.GetOrCreate(album.Id, SourceCore, album);
}
}
///
public override async IAsyncEnumerable GetArtistItemsAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var artistMetadata = await _fileMetadataManager.Artists.GetItemsAsync(offset, limit);
foreach (var artist in artistMetadata)
{
Guard.IsNotNullOrWhiteSpace(artist.Id, nameof(artist.Id));
yield return InstanceCache.Artists.GetOrCreate(artist.Id, SourceCore, artist);
}
}
///
public override async IAsyncEnumerable GetTracksAsync(int limit, int offset, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Guard.IsNotNull(_fileMetadataManager, nameof(_fileMetadataManager));
var artistMetadata = await _fileMetadataManager.Tracks.GetItemsAsync(offset, limit);
foreach (var track in artistMetadata)
{
Guard.IsNotNullOrWhiteSpace(track.Id, nameof(track.Id));
yield return InstanceCache.Tracks.GetOrCreate(track.Id, SourceCore, track);
}
}
///
public override IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default)
{
return AsyncEnumerable.Empty();
}
///
public override async ValueTask DisposeAsync()
{
if (!IsInitialized)
return;
using (await OwlCore.Flow.EasySemaphore(_initSemaphore))
{
if (!IsInitialized)
return;
if (_fileMetadataManager is not null)
DetachEvents(_fileMetadataManager);
DetachEvents();
await base.DisposeAsync();
IsInitialized = false;
}
}
}
}