// 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
{
///
/// A concrete class that merged multiple s.
///
public class MergedPlaylistCollection : IPlaylistCollection, IMergedMutable
{
private readonly List _sources;
private readonly ICorePlaylistCollection _preferredSource;
private readonly MergedCollectionMap _playlistMap;
private readonly MergedCollectionMap _imageMap;
private readonly MergedCollectionMap _urlMap;
///
/// Creates a new instance of .
///
public MergedPlaylistCollection(IEnumerable collections, MergedCollectionConfig config)
{
_sources = collections.ToList();
_preferredSource = _sources[0];
_imageMap = new MergedCollectionMap(this, config);
_urlMap = new MergedCollectionMap(this, config);
_playlistMap = new MergedCollectionMap(this, config);
foreach (var item in _sources)
{
TotalPlaylistItemsCount += item.TotalPlaylistItemsCount;
TotalImageCount += item.TotalImageCount;
TotalUrlCount += item.TotalUrlCount;
}
Name = _preferredSource.Name;
Description = _preferredSource.Description;
PlaybackState = _preferredSource.PlaybackState;
Duration = _preferredSource.Duration;
LastPlayed = _preferredSource.LastPlayed;
AddedAt = _preferredSource.AddedAt;
AttachEvents(_preferredSource);
}
private void AttachEvents(ICorePlaylistCollection source)
{
AttachPlayableEvents(source);
source.IsPlayPlaylistCollectionAsyncAvailableChanged += IsPlayPlaylistCollectionAsyncAvailableChanged;
source.IsPausePlaylistCollectionAsyncAvailableChanged += IsPausePlaylistCollectionAsyncAvailableChanged;
_playlistMap.ItemsChanged += PlaylistMap_ItemsChanged;
_playlistMap.ItemsCountChanged += PlaylistMap_ItemsCountChanged;
_imageMap.ItemsChanged += ImageCollectionMap_ItemsChanged;
_imageMap.ItemsCountChanged += ImageCollectionMap_ItemsCountChanged;
_urlMap.ItemsChanged += UrlCollectionMap_ItemsChanged;
_urlMap.ItemsCountChanged += UrlCollectionMap_ItemsCountChanged;
}
private void DetachEvents(ICorePlaylistCollection source)
{
DetachPlayableEvents(source);
source.IsPlayPlaylistCollectionAsyncAvailableChanged -= IsPlayPlaylistCollectionAsyncAvailableChanged;
source.IsPausePlaylistCollectionAsyncAvailableChanged -= IsPausePlaylistCollectionAsyncAvailableChanged;
_playlistMap.ItemsChanged -= PlaylistMap_ItemsChanged;
_playlistMap.ItemsCountChanged -= PlaylistMap_ItemsCountChanged;
_imageMap.ItemsChanged -= ImageCollectionMap_ItemsChanged;
_imageMap.ItemsCountChanged -= ImageCollectionMap_ItemsCountChanged;
_urlMap.ItemsChanged -= UrlCollectionMap_ItemsChanged;
_urlMap.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 PlaylistMap_ItemsCountChanged(object sender, int e)
{
TotalPlaylistItemsCount = e;
PlaylistItemsCountChanged?.Invoke(this, e);
}
private void ImageCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalImageCount = e;
ImagesCountChanged?.Invoke(this, e);
}
private void UrlCollectionMap_ItemsCountChanged(object sender, int e)
{
TotalUrlCount = e;
UrlsCountChanged?.Invoke(this, e);
}
private void ImageCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
ImagesChanged?.Invoke(this, addedItems, removedItems);
}
private void UrlCollectionMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
UrlsChanged?.Invoke(this, addedItems, removedItems);
}
private void PlaylistMap_ItemsChanged(object sender, IReadOnlyList> addedItems, IReadOnlyList> removedItems)
{
PlaylistItemsChanged?.Invoke(this, addedItems, removedItems);
}
///
public event EventHandler? PlaybackStateChanged;
///
public event EventHandler? NameChanged;
///
public event EventHandler? DescriptionChanged;
///
public event EventHandler? DurationChanged;
///
public event EventHandler? LastPlayedChanged;
///
public event EventHandler? IsPlayPlaylistCollectionAsyncAvailableChanged;
///
public event EventHandler? IsPausePlaylistCollectionAsyncAvailableChanged;
///
public event EventHandler? IsChangeNameAsyncAvailableChanged;
///
public event EventHandler? IsChangeDescriptionAsyncAvailableChanged;
///
public event EventHandler? IsChangeDurationAsyncAvailableChanged;
///
public event EventHandler? ImagesCountChanged;
///
public event EventHandler? UrlsCountChanged;
///
public event EventHandler? PlaylistItemsCountChanged;
///
public event CollectionChangedEventHandler? ImagesChanged;
///
public event CollectionChangedEventHandler? UrlsChanged;
///
public event CollectionChangedEventHandler? PlaylistItemsChanged;
///
public event EventHandler? DownloadInfoChanged;
///
public event EventHandler? SourcesChanged;
///
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 TotalPlaylistItemsCount { get; internal set; }
///
public int TotalImageCount { get; internal set; }
///
public int TotalUrlCount { get; internal set; }
///
public bool IsPlayPlaylistCollectionAsyncAvailable => _preferredSource.IsPlayPlaylistCollectionAsyncAvailable;
///
public bool IsPausePlaylistCollectionAsyncAvailable => _preferredSource.IsPausePlaylistCollectionAsyncAvailable;
///
public bool IsChangeNameAsyncAvailable => _preferredSource.IsChangeNameAsyncAvailable;
///
public bool IsChangeDescriptionAsyncAvailable => _preferredSource.IsChangeDescriptionAsyncAvailable;
///
public bool IsChangeDurationAsyncAvailable => _preferredSource.IsChangeDurationAsyncAvailable;
///
public Task PlayPlaylistCollectionAsync(CancellationToken cancellationToken) => _preferredSource.PlayPlaylistCollectionAsync(cancellationToken);
///
public Task PausePlaylistCollectionAsync(CancellationToken cancellationToken) => _preferredSource.PausePlaylistCollectionAsync(cancellationToken);
///
public Task PlayPlaylistCollectionAsync(IPlaylistCollectionItem playlistItem, CancellationToken cancellationToken = default)
{
var targetCore = _preferredSource.SourceCore;
ICorePlaylistCollectionItem? source = null;
if (playlistItem is IPlaylist playlist)
source = playlist.GetSources().FirstOrDefault(x => x.SourceCore.InstanceId == targetCore.InstanceId);
if (playlistItem is IPlaylistCollection collection)
source = collection.GetSources().FirstOrDefault(x => x.SourceCore.InstanceId == targetCore.InstanceId);
Guard.IsNotNull(source, nameof(source));
return _preferredSource.PlayPlaylistCollectionAsync(source, cancellationToken);
}
///
public Task ChangeNameAsync(string name, CancellationToken cancellationToken = default)
{
return _preferredSource.ChangeNameAsync(name, cancellationToken);
}
///
public Task ChangeDescriptionAsync(string? description, CancellationToken cancellationToken = default)
{
return _preferredSource.ChangeDescriptionAsync(description, cancellationToken);
}
///
public Task ChangeDurationAsync(TimeSpan duration, CancellationToken cancellationToken = default)
{
return _preferredSource.ChangeDurationAsync(duration, cancellationToken);
}
///
public Task IsAddPlaylistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _playlistMap.IsAddItemAvailableAsync(index, cancellationToken);
///
public Task IsRemovePlaylistItemAvailableAsync(int index, CancellationToken cancellationToken = default) => _playlistMap.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 IReadOnlyList Sources => _sources;
///
IReadOnlyList IMerged.Sources => _sources;
///
IReadOnlyList IMerged.Sources => _sources;
///
IReadOnlyList IMerged.Sources => _sources;
///
IReadOnlyList IMerged.Sources => _sources;
///
public IAsyncEnumerable GetPlaylistItemsAsync(int limit, int offset, CancellationToken cancellationToken = default) => _playlistMap.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 Task AddPlaylistItemAsync(IPlaylistCollectionItem playlistItem, int index, CancellationToken cancellationToken = default) => _playlistMap.InsertItemAsync(playlistItem, index, cancellationToken);
///
public Task AddImageAsync(IImage image, int index, CancellationToken cancellationToken = default) => _imageMap.InsertItemAsync(image, index, cancellationToken);
///
public Task AddUrlAsync(IUrl url, int index, CancellationToken cancellationToken = default) => _urlMap.InsertItemAsync(url, index, cancellationToken);
///
public Task RemovePlaylistItemAsync(int index, CancellationToken cancellationToken = default) => _playlistMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveImageAsync(int index, CancellationToken cancellationToken = default) => _imageMap.RemoveAtAsync(index, cancellationToken);
///
public Task RemoveUrlAsync(int index, CancellationToken cancellationToken = default) => _urlMap.RemoveAtAsync(index, cancellationToken);
///
public Task StartDownloadOperationAsync(DownloadOperation operation, CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
///
public void AddSource(ICorePlaylistCollection itemToMerge)
{
Guard.IsNotNull(itemToMerge, nameof(itemToMerge));
_sources.Add(itemToMerge);
_playlistMap.AddSource(itemToMerge);
_imageMap.AddSource(itemToMerge);
_urlMap.AddSource(itemToMerge);
}
///
public void RemoveSource(ICorePlaylistCollection itemToRemove)
{
Guard.IsNotNull(itemToRemove, nameof(itemToRemove));
_sources.Remove(itemToRemove);
_playlistMap.RemoveSource(itemToRemove);
_imageMap.RemoveSource(itemToRemove);
_urlMap.RemoveSource(itemToRemove);
}
///
public bool Equals(ICorePlaylistCollection? other)
{
return other?.Name == Name;
}
///
public bool Equals(ICorePlaylistCollectionItem other) => Equals(other as ICorePlaylistCollection);
///
public bool Equals(ICoreImageCollection other) => Equals(other as ICorePlaylistCollection);
///
public bool Equals(ICoreUrlCollection other) => Equals(other as ICorePlaylistCollection);
///
public async ValueTask DisposeAsync()
{
DetachEvents(_preferredSource);
await _sources.InParallel(x => x.DisposeAsync().AsTask());
await _playlistMap.DisposeAsync();
await _imageMap.DisposeAsync();
await _urlMap.DisposeAsync();
}
}
}