// Copyright (c) Arlo Godfrey. All Rights Reserved. // Licensed under the GNU Lesser General Public License, Version 3.0 with additional terms. // See the LICENSE, LICENSE.LESSER and LICENSE.ADDITIONAL files in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Diagnostics; using OwlCore.Events; using OwlCore.Extensions; using StrixMusic.Sdk.AppModels; using StrixMusic.Sdk.BaseModels; using StrixMusic.Sdk.CoreModels; using StrixMusic.Sdk.Extensions; using StrixMusic.Sdk.MediaPlayback; namespace StrixMusic.Sdk.AdapterModels { /// /// Merged multiple into a single /// public class MergedAlbumCollection : IAlbumCollection, IMergedMutable, IMergedMutable { private readonly List _sources; private readonly ICoreAlbumCollection _preferredSource; private readonly MergedCollectionMap _albumMap; private readonly MergedCollectionMap _imageMap; private readonly MergedCollectionMap _urlMap; /// /// Creates a new instance of . /// public MergedAlbumCollection(IEnumerable sources, MergedCollectionConfig config) { _sources = sources.ToList(); _preferredSource = _sources[0]; foreach (var source in _sources) { TotalAlbumItemsCount += source.TotalAlbumItemsCount; TotalImageCount += source.TotalImageCount; TotalUrlCount += source.TotalUrlCount; } Duration = _preferredSource.Duration; PlaybackState = _preferredSource.PlaybackState; Description = _preferredSource.Description; Name = _preferredSource.Name; LastPlayed = _preferredSource.LastPlayed; AddedAt = _preferredSource.AddedAt; _imageMap = new MergedCollectionMap(this, config); _urlMap = new MergedCollectionMap(this, config); _albumMap = new MergedCollectionMap(this, config); AttachEvents(_preferredSource); } private void AttachEvents(ICoreAlbumCollection source) { AttachPlayableEvents(source); source.IsPlayAlbumCollectionAsyncAvailableChanged += IsPlayAlbumCollectionAsyncAvailableChanged; _albumMap.ItemsChanged += AlbumMap_ItemsChanged; _albumMap.ItemsCountChanged += AlbumMap_ItemsCountChanged; _imageMap.ItemsChanged += ImageMap_ItemsChanged; _imageMap.ItemsCountChanged += ImageMap_ItemsCountChanged; _urlMap.ItemsChanged += UrlMap_ItemsChanged; _urlMap.ItemsCountChanged += UrlMap_ItemsCountChanged; } private void DetachEvents(ICoreAlbumCollection source) { DetachPlayableEvents(source); source.IsPlayAlbumCollectionAsyncAvailableChanged -= IsPlayAlbumCollectionAsyncAvailableChanged; _albumMap.ItemsChanged -= AlbumMap_ItemsChanged; _albumMap.ItemsCountChanged -= AlbumMap_ItemsCountChanged; _imageMap.ItemsChanged -= ImageMap_ItemsChanged; _imageMap.ItemsCountChanged -= ImageMap_ItemsCountChanged; _urlMap.ItemsChanged -= UrlMap_ItemsChanged; _urlMap.ItemsCountChanged -= UrlMap_ItemsCountChanged; } private void AttachPlayableEvents(IPlayableBase source) { source.PlaybackStateChanged += PlaybackStateChanged; source.NameChanged += NameChanged; source.DescriptionChanged += DescriptionChanged; source.DurationChanged += DurationChanged; source.LastPlayedChanged += LastPlayedChanged; source.IsChangeNameAsyncAvailableChanged += IsChangeNameAsyncAvailableChanged; source.IsChangeDurationAsyncAvailableChanged += IsChangeDurationAsyncAvailableChanged; source.IsChangeDescriptionAsyncAvailableChanged += IsChangeDescriptionAsyncAvailableChanged; } private void DetachPlayableEvents(IPlayableBase source) { source.PlaybackStateChanged -= PlaybackStateChanged; source.NameChanged -= NameChanged; source.DescriptionChanged -= DescriptionChanged; source.DurationChanged -= DurationChanged; source.LastPlayedChanged -= LastPlayedChanged; source.IsChangeNameAsyncAvailableChanged -= IsChangeNameAsyncAvailableChanged; source.IsChangeDurationAsyncAvailableChanged -= IsChangeDurationAsyncAvailableChanged; source.IsChangeDescriptionAsyncAvailableChanged -= IsChangeDescriptionAsyncAvailableChanged; } /// public event EventHandler? PlaybackStateChanged; /// public event EventHandler? NameChanged; /// public event EventHandler? DescriptionChanged; /// public event EventHandler? DurationChanged; /// public event EventHandler? LastPlayedChanged; /// public event EventHandler? IsPlayAlbumCollectionAsyncAvailableChanged; /// public event EventHandler? IsPauseAlbumCollectionAsyncAvailableChanged; /// public event EventHandler? IsChangeNameAsyncAvailableChanged; /// public event EventHandler? IsChangeDescriptionAsyncAvailableChanged; /// public event EventHandler? IsChangeDurationAsyncAvailableChanged; /// public event CollectionChangedEventHandler? AlbumItemsChanged; /// public event EventHandler? AlbumItemsCountChanged; /// public event CollectionChangedEventHandler? ImagesChanged; /// public event EventHandler? ImagesCountChanged; /// public event CollectionChangedEventHandler? UrlsChanged; /// public event EventHandler? UrlsCountChanged; /// public event EventHandler? DownloadInfoChanged; /// public event EventHandler? SourcesChanged; private void AlbumMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) { AlbumItemsChanged?.Invoke(this, addedItems, removedItems); } private void AlbumMap_ItemsCountChanged(object sender, int e) { TotalAlbumItemsCount = e; AlbumItemsCountChanged?.Invoke(this, e); } private void ImageMap_ItemsCountChanged(object sender, int e) { TotalImageCount = e; ImagesCountChanged?.Invoke(this, e); } private void ImageMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) { ImagesChanged?.Invoke(this, addedItems, removedItems); } private void UrlMap_ItemsCountChanged(object sender, int e) { TotalUrlCount = e; UrlsCountChanged?.Invoke(this, e); } private void UrlMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems) { UrlsChanged?.Invoke(this, addedItems, removedItems); } /// public IReadOnlyList Sources => _sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// IReadOnlyList IMerged.Sources => Sources; /// public string Id => _preferredSource.Id; /// public string Name { get; internal set; } /// public string? Description { get; internal set; } /// public PlaybackState PlaybackState { get; internal set; } /// public DownloadInfo DownloadInfo => default; /// public TimeSpan Duration { get; internal set; } /// public DateTime? LastPlayed { get; internal set; } /// public DateTime? AddedAt { get; internal set; } /// public bool IsPlayAlbumCollectionAsyncAvailable => _preferredSource.IsPlayAlbumCollectionAsyncAvailable; /// public bool IsPauseAlbumCollectionAsyncAvailable => _preferredSource.IsPauseAlbumCollectionAsyncAvailable; /// public bool IsChangeNameAsyncAvailable => _preferredSource.IsChangeNameAsyncAvailable; /// public bool IsChangeDescriptionAsyncAvailable => _preferredSource.IsChangeDescriptionAsyncAvailable; /// public bool IsChangeDurationAsyncAvailable => _preferredSource.IsChangeDurationAsyncAvailable; /// public int TotalAlbumItemsCount { get; internal set; } /// public int TotalImageCount { get; internal set; } /// public int TotalUrlCount { get; internal set; } /// public Task StartDownloadOperationAsync(DownloadOperation operation, CancellationToken cancellationToken = default) { throw new NotSupportedException(); } /// public Task IsAddAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _albumMap.IsAddItemAvailableAsync(index, cancellationToken); /// public Task IsRemoveAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _albumMap.IsRemoveItemAvailableAsync(index, cancellationToken); /// public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _imageMap.IsAddItemAvailableAsync(index, cancellationToken); /// public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _imageMap.IsRemoveItemAvailableAsync(index, cancellationToken); /// public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _urlMap.IsAddItemAvailableAsync(index, cancellationToken); /// public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _urlMap.IsRemoveItemAvailableAsync(index, cancellationToken); /// public Task PlayAlbumCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PlayAlbumCollectionAsync(cancellationToken); /// public Task PlayAlbumCollectionAsync(IAlbumCollectionItem albumItem, CancellationToken cancellationToken = default) { var targetCore = _preferredSource.SourceCore; ICoreAlbumCollectionItem? source = null; if (albumItem is IAlbum album) source = album.GetSources().FirstOrDefault(x => x.SourceCore.InstanceId == targetCore.InstanceId); if (albumItem is IAlbumCollection collection) source = collection.GetSources().FirstOrDefault(x => x.SourceCore.InstanceId == targetCore.InstanceId); Guard.IsNotNull(source, nameof(source)); return _preferredSource.PlayAlbumCollectionAsync(source, cancellationToken); } /// public Task PauseAlbumCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PauseAlbumCollectionAsync(cancellationToken); /// public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default) => _preferredSource.ChangeNameAsync(name, cancellationToken); /// public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default) => _preferredSource.ChangeDescriptionAsync(description, cancellationToken); /// public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default) => _preferredSource.ChangeDurationAsync(duration, cancellationToken); /// public Task AddAlbumItemAsync(IAlbumCollectionItem albumItem, int index, CancellationToken cancellationToken = default) => _albumMap.InsertItemAsync(albumItem, index, cancellationToken); /// public Task RemoveAlbumItemAsync(int index, CancellationToken cancellationToken = default) => _albumMap.RemoveAtAsync(index, cancellationToken); /// public Task AddImageAsync(IImage image, int index, CancellationToken cancellationToken = default) => _imageMap.InsertItemAsync(image, index, cancellationToken); /// public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) => _imageMap.RemoveAtAsync(index, cancellationToken); /// public Task AddUrlAsync(IUrl url, int index, CancellationToken cancellationToken = default) => _urlMap.InsertItemAsync(url, index, cancellationToken); /// public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) => _urlMap.RemoveAtAsync(index, cancellationToken); /// public IAsyncEnumerable GetAlbumItemsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _albumMap.GetItemsAsync(limit, offset, cancellationToken); /// public IAsyncEnumerable GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default) => _imageMap.GetItemsAsync(limit, offset, cancellationToken); /// public IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _urlMap.GetItemsAsync(limit, offset, cancellationToken); /// public void AddSource(ICoreAlbumCollection itemToMerge) { Guard.IsNotNull(itemToMerge, nameof(itemToMerge)); _sources.Add(itemToMerge); _albumMap.AddSource(itemToMerge); _imageMap.AddSource(itemToMerge); _imageMap.AddSource(itemToMerge); SourcesChanged?.Invoke(this, EventArgs.Empty); } /// public void RemoveSource(ICoreAlbumCollection itemToRemove) { Guard.IsNotNull(itemToRemove, nameof(itemToRemove)); _sources.Remove(itemToRemove); _imageMap.RemoveSource(itemToRemove); _albumMap.RemoveSource(itemToRemove); _albumMap.RemoveSource(itemToRemove); SourcesChanged?.Invoke(this, EventArgs.Empty); } /// public void AddSource(ICoreAlbumCollectionItem itemToMerge) => AddSource((ICoreAlbumCollection)itemToMerge); /// public void RemoveSource(ICoreAlbumCollectionItem itemToMerge) => RemoveSource((ICoreAlbumCollection)itemToMerge); /// public bool Equals(ICoreAlbumCollection? other) { return other?.Name.Equals(Name, StringComparison.InvariantCulture) ?? false; } /// public bool Equals(ICoreAlbumCollectionItem other) => Equals(other as ICoreAlbumCollection); /// public bool Equals(ICoreImageCollection other) => Equals(other as ICoreAlbumCollection); /// public bool Equals(ICoreUrlCollection other) => Equals(other as ICoreAlbumCollection); /// public async ValueTask DisposeAsync() { DetachEvents(_preferredSource); await _albumMap.DisposeAsync(); await _imageMap.DisposeAsync(); await _urlMap.DisposeAsync(); await Sources.InParallel(x => x.DisposeAsync().AsTask()); } } }