// 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 MergedArtist : IArtist, IMergedMutable, IMergedMutable
{
private readonly List _sources;
private readonly MergedCollectionMap _trackCollectionMap;
private readonly MergedCollectionMap _imageCollectionMap;
private readonly MergedCollectionMap _albumCollectionItemMap;
private readonly MergedCollectionMap _genreCollectionMap;
private readonly MergedCollectionMap _urlCollectionMap;
private readonly ICoreArtist _preferredSource;
///
/// Creates a new instance of .
///
public MergedArtist(IEnumerable sources, MergedCollectionConfig config)
{
_sources = sources.ToList();
// TODO: Get the actual preferred source.
_preferredSource = _sources[0];
foreach (var source in _sources)
{
TotalAlbumItemsCount += source.TotalAlbumItemsCount;
TotalImageCount += source.TotalImageCount;
TotalTrackCount += source.TotalTrackCount;
TotalGenreCount += source.TotalGenreCount;
TotalUrlCount += source.TotalUrlCount;
}
Name = _preferredSource.Name;
Description = _preferredSource.Description;
PlaybackState = _preferredSource.PlaybackState;
Duration = _preferredSource.Duration;
LastPlayed = _preferredSource.LastPlayed;
AddedAt = _preferredSource.AddedAt;
_trackCollectionMap = new MergedCollectionMap(this, config);
_imageCollectionMap = new MergedCollectionMap(this, config);
_albumCollectionItemMap = new MergedCollectionMap(this, config);
_genreCollectionMap = new MergedCollectionMap(this, config);
_urlCollectionMap = new MergedCollectionMap(this, config);
var relatedItems = _sources.Select(x => x.RelatedItems).PruneNull().ToList();
if (relatedItems.Count > 0)
RelatedItems = new MergedPlayableCollectionGroup(relatedItems, config);
AttachEvents(_preferredSource);
}
private void AttachEvents(ICoreArtist source)
{
AttachPlayableEvents(source);
source.IsPlayTrackCollectionAsyncAvailableChanged += IsPlayTrackCollectionAsyncAvailableChanged;
source.IsPauseTrackCollectionAsyncAvailableChanged += IsPauseTrackCollectionAsyncAvailableChanged;
source.IsPlayAlbumCollectionAsyncAvailableChanged += IsPlayAlbumCollectionAsyncAvailableChanged;
source.IsPauseAlbumCollectionAsyncAvailableChanged += IsPauseAlbumCollectionAsyncAvailableChanged;
_trackCollectionMap.ItemsChanged += TrackCollectionMap_ItemsChanged;
_trackCollectionMap.ItemsCountChanged += TrackCollectionMap_ItemsCountChanged;
_imageCollectionMap.ItemsChanged += ImageCollectionMap_ItemsChanged;
_imageCollectionMap.ItemsCountChanged += ImageCollectionMap_ItemsCountChanged;
_albumCollectionItemMap.ItemsChanged += AlbumCollectionItemsChanged;
_albumCollectionItemMap.ItemsCountChanged += AlbumCollectionMap_ItemsCountChanged;
_genreCollectionMap.ItemsChanged += GenreCollectionMap_ItemsChanged;
_genreCollectionMap.ItemsCountChanged += GenreCollectionMap_ItemsCountChanged;
_urlCollectionMap.ItemsChanged += UrlCollectionMap_ItemsChanged;
_urlCollectionMap.ItemsCountChanged += UrlCollectionMap_ItemsCountChanged;
}
private void DetachEvents(ICoreArtist source)
{
DetachPlayableEvents(source);
source.IsPlayTrackCollectionAsyncAvailableChanged -= IsPlayTrackCollectionAsyncAvailableChanged;
source.IsPauseTrackCollectionAsyncAvailableChanged -= IsPauseTrackCollectionAsyncAvailableChanged;
source.IsPlayAlbumCollectionAsyncAvailableChanged += IsPlayAlbumCollectionAsyncAvailableChanged;
source.IsPauseAlbumCollectionAsyncAvailableChanged += IsPauseAlbumCollectionAsyncAvailableChanged;
_trackCollectionMap.ItemsChanged -= TrackCollectionMap_ItemsChanged;
_trackCollectionMap.ItemsCountChanged -= TrackCollectionMap_ItemsCountChanged;
_imageCollectionMap.ItemsChanged -= ImageCollectionMap_ItemsChanged;
_imageCollectionMap.ItemsCountChanged -= ImageCollectionMap_ItemsCountChanged;
_albumCollectionItemMap.ItemsChanged -= AlbumCollectionItemsChanged;
_albumCollectionItemMap.ItemsCountChanged -= AlbumCollectionMap_ItemsCountChanged;
_genreCollectionMap.ItemsChanged -= GenreCollectionMap_ItemsChanged;
_genreCollectionMap.ItemsCountChanged -= GenreCollectionMap_ItemsCountChanged;
_urlCollectionMap.ItemsChanged -= UrlCollectionMap_ItemsChanged;
_urlCollectionMap.ItemsCountChanged -= UrlCollectionMap_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;
}
private void AlbumCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalAlbumItemsCount = e;
AlbumItemsCountChanged?.Invoke(this, e);
}
private void TrackCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalTrackCount = e;
TracksCountChanged?.Invoke(this, e);
}
private void ImageCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalImageCount = e;
ImagesCountChanged?.Invoke(this, e);
}
private void GenreCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalGenreCount = e;
GenresCountChanged?.Invoke(this, e);
}
private void UrlCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalUrlCount = e;
UrlsCountChanged?.Invoke(this, e);
}
private void AlbumCollectionItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
AlbumItemsChanged?.Invoke(this, addedItems, removedItems);
}
private void TrackCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
TracksChanged?.Invoke(this, addedItems, removedItems);
}
private void ImageCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
ImagesChanged?.Invoke(this, addedItems, removedItems);
}
private void GenreCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
GenresChanged?.Invoke(this, addedItems, removedItems);
}
private void UrlCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
UrlsChanged?.Invoke(this, addedItems, removedItems);
}
///
public event EventHandler? AlbumItemsCountChanged;
///
public event EventHandler? TracksCountChanged;
///
public event EventHandler? ImagesCountChanged;
///
public event EventHandler? GenresCountChanged;
///
public event EventHandler? UrlsCountChanged;
///
public event CollectionChangedEventHandler? AlbumItemsChanged;
///
public event CollectionChangedEventHandler? TracksChanged;
///
public event CollectionChangedEventHandler? ImagesChanged;
///
public event CollectionChangedEventHandler? GenresChanged;
///
public event CollectionChangedEventHandler? UrlsChanged;
///
public event EventHandler? DurationChanged;
///
public event EventHandler? LastPlayedChanged;
///
public event EventHandler? IsPlayTrackCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPauseTrackCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPlayAlbumCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPauseAlbumCollectionAsyncAvailableChanged;
///
public event EventHandler? IsChangeNameAsyncAvailableChanged;
///
public event EventHandler? IsChangeDescriptionAsyncAvailableChanged;
///
public event EventHandler? IsChangeDurationAsyncAvailableChanged;
///
public event EventHandler? NameChanged;
///
public event EventHandler? DescriptionChanged;
///
public event EventHandler? DownloadInfoChanged;
///
public event EventHandler? SourcesChanged;
///
public event EventHandler? PlaybackStateChanged;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
IReadOnlyList IMerged.Sources => Sources;
///
/// The merged sources for this
///
public IReadOnlyList 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 int TotalTrackCount { get; internal set; }
///
public int TotalAlbumItemsCount { get; internal set; }
///
public int TotalImageCount { get; internal set; }
///
public int TotalGenreCount { get; internal set; }
///
public int TotalUrlCount { get; internal set; }
///
public IPlayableCollectionGroup? RelatedItems { get; }
///
public bool IsPlayTrackCollectionAsyncAvailable => _preferredSource.IsPlayTrackCollectionAsyncAvailable;
///
public bool IsPauseTrackCollectionAsyncAvailable => _preferredSource.IsPauseTrackCollectionAsyncAvailable;
///
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 Task IsAddTrackAvailableAsync(int index, CancellationToken cancellationToken = default) => _trackCollectionMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsAddAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _albumCollectionItemMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsAddImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _imageCollectionMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsAddGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _genreCollectionMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsAddUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _genreCollectionMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveTrackAvailableAsync(int index, CancellationToken cancellationToken = default) => _trackCollectionMap.IsRemoveItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveAlbumItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _albumCollectionItemMap.IsRemoveItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveImageAvailableAsync(int index, CancellationToken cancellationToken = default) => _imageCollectionMap.IsRemoveItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveGenreAvailableAsync(int index, CancellationToken cancellationToken = default) => _genreCollectionMap.IsRemoveItemAvailableAsync(index, cancellationToken);
///
public Task IsRemoveUrlAvailableAsync(int index, CancellationToken cancellationToken = default) => _urlCollectionMap.IsRemoveItemAvailableAsync(index, cancellationToken);
///
public IAsyncEnumerable GetAlbumItemsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _albumCollectionItemMap.GetItemsAsync(limit, offset, cancellationToken);
///
public Task AddAlbumItemAsync(IAlbumCollectionItem albumItem, int index, CancellationToken cancellationToken = default) => _albumCollectionItemMap.InsertItemAsync(albumItem, index, cancellationToken);
///
public IAsyncEnumerable GetTracksAsync(int limit, int offset, CancellationToken cancellationToken = default) => _trackCollectionMap.GetItemsAsync(limit, offset, cancellationToken);
///
public Task AddTrackAsync(ITrack track, int index, CancellationToken cancellationToken = default) => _trackCollectionMap.InsertItemAsync(track, index, cancellationToken);
///
public Task PlayTrackCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PlayTrackCollectionAsync(cancellationToken);
///
public Task PauseTrackCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PauseTrackCollectionAsync(cancellationToken);
///
public Task PlayAlbumCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PlayAlbumCollectionAsync(cancellationToken);
///
public Task PauseAlbumCollectionAsync(CancellationToken cancellationToken = default) => _preferredSource.PauseAlbumCollectionAsync(cancellationToken);
///
public Task PlayTrackCollectionAsync(ITrack track, CancellationToken cancellationToken = default)
{
var targetCore = _preferredSource.SourceCore;
var source = track.GetSources()
.FirstOrDefault(x => x.SourceCore.InstanceId == targetCore.InstanceId);
Guard.IsNotNull(source, nameof(source));
return _preferredSource.PlayTrackCollectionAsync(source, 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 ChangeNameAsync(string name, CancellationToken cancellationToken = default) => _preferredSource.ChangeNameAsync(name, cancellationToken);
///
public IAsyncEnumerable GetImagesAsync(int limit, int offset, CancellationToken cancellationToken = default) => _imageCollectionMap.GetItemsAsync(limit, offset, cancellationToken);
///
public IAsyncEnumerable GetGenresAsync(int limit, int offset, CancellationToken cancellationToken = default) => _genreCollectionMap.GetItemsAsync(limit, offset, cancellationToken);
///
public IAsyncEnumerable GetUrlsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _urlCollectionMap.GetItemsAsync(limit, offset, cancellationToken);
///
public Task AddImageAsync(IImage image, int index, CancellationToken cancellationToken = default) => _imageCollectionMap.InsertItemAsync(image, index, cancellationToken);
///
public Task AddGenreAsync(IGenre genre, int index, CancellationToken cancellationToken = default) => _genreCollectionMap.InsertItemAsync(genre, index, cancellationToken);
///
public Task AddUrlAsync(IUrl url, int index, CancellationToken cancellationToken = default) => _urlCollectionMap.InsertItemAsync(url, index, 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 RemoveTrackAsync(int index, CancellationToken cancellationToken = default) => _trackCollectionMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) => _trackCollectionMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveAlbumItemAsync(int index, CancellationToken cancellationToken = default) => _albumCollectionItemMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveGenreAsync(int index, CancellationToken cancellationToken = default) => _genreCollectionMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) => _urlCollectionMap.RemoveAtAsync(index, cancellationToken);
///
public Task StartDownloadOperationAsync(DownloadOperation operation, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
/// Adds a new source to this merged item.
///
/// The item to merge into this Artist
public void AddSource(ICoreArtist itemToMerge)
{
Guard.IsNotNull(itemToMerge, nameof(itemToMerge));
if (!Equals(itemToMerge))
ThrowHelper.ThrowArgumentException(nameof(itemToMerge), "Tried to merge an artist that doesn't match. Verify that the item matches before merging the source.");
_sources.Add(itemToMerge);
_albumCollectionItemMap.AddSource(itemToMerge);
_trackCollectionMap.AddSource(itemToMerge);
_imageCollectionMap.AddSource(itemToMerge);
_genreCollectionMap.AddSource(itemToMerge);
_urlCollectionMap.AddSource(itemToMerge);
SourcesChanged?.Invoke(this, EventArgs.Empty);
}
///
public void RemoveSource(ICoreArtist itemToRemove)
{
Guard.IsNotNull(itemToRemove, nameof(itemToRemove));
_sources.Remove(itemToRemove);
_albumCollectionItemMap.RemoveSource(itemToRemove);
_trackCollectionMap.RemoveSource(itemToRemove);
_imageCollectionMap.RemoveSource(itemToRemove);
_genreCollectionMap.RemoveSource(itemToRemove);
_urlCollectionMap.RemoveSource(itemToRemove);
SourcesChanged?.Invoke(this, EventArgs.Empty);
}
///
public void AddSource(ICoreArtistCollectionItem itemToMerge) => AddSource((ICoreArtist)itemToMerge);
///
public void RemoveSource(ICoreArtistCollectionItem itemToRemove) => RemoveSource((ICoreArtist)itemToRemove);
///
public bool Equals(ICoreArtist? other)
{
return other?.Name == Name;
}
///
public bool Equals(ICoreArtistCollectionItem other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreAlbumCollectionItem other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreImageCollection other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreAlbumCollection other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreTrackCollection other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreGenreCollection other) => Equals(other as ICoreArtist);
///
public bool Equals(ICoreUrlCollection other) => Equals(other as ICoreArtist);
///
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || (obj is ICoreArtist other && Equals(other));
}
///
public override int GetHashCode()
{
return _preferredSource.GetHashCode();
}
///
public async ValueTask DisposeAsync()
{
DetachEvents(_preferredSource);
await _albumCollectionItemMap.DisposeAsync();
await _trackCollectionMap.DisposeAsync();
await _imageCollectionMap.DisposeAsync();
await _genreCollectionMap.DisposeAsync();
await _urlCollectionMap.DisposeAsync();
await Sources.InParallel(x => x.DisposeAsync().AsTask());
}
}
}