using Avalonia.Media.Imaging;
using Avalonia.Threading;
using Harmonia.Core.Caching;
using Harmonia.Core.Imaging;
using Harmonia.Core.Models;
using Harmonia.Core.Player;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Harmonia.UI.ViewModels;

public class PlayingSongInfoViewModel : ViewModelBase, IDisposable
{
    private readonly IAudioPlayer _audioPlayer;
    private readonly IAudioImageCache _audioImageCache;

    private CancellationTokenSource? _audioImageCancellationTokenSource;

    private Song? _song;
    public Song? Song
    {
        get
        {
            return _song;
        }
        private set
        {
            _song = value;
            OnPropertyChanged();
        }
    }

    private Bitmap? _songImageSource;
    public Bitmap? SongImageSource
    {
        get
        {
            return _songImageSource;
        }
        private set
        {
            _songImageSource?.Dispose();
            _songImageSource = value;
            OnPropertyChanged();
        }
    }

    public PlayingSongInfoViewModel(IAudioPlayer audioPlayer, IAudioImageCache audioImageCache)
    {
        _audioPlayer = audioPlayer;
        _audioPlayer.PlayingSongChanged += OnAudioPlayerPlayingSongChanged;

        _audioImageCache = audioImageCache;
    }

    private void OnAudioPlayerPlayingSongChanged(object? sender, EventArgs e)
    {
        Song = _audioPlayer.PlayingSong?.Song;
        Task.Run(UpdateImage);
    }

    private async Task UpdateImage()
    {
        // TODO: Show default picture
        if (Song == null)
            return;

        if (_audioImageCancellationTokenSource != null)
            await _audioImageCancellationTokenSource.CancelAsync();

        _audioImageCancellationTokenSource = new();
        CancellationToken cancellationToken = _audioImageCancellationTokenSource.Token;

        SongPictureInfo? songPictureInfo = await _audioImageCache.GetAsync(Song, cancellationToken);

        if (songPictureInfo == null)
            return;

        await Dispatcher.UIThread.InvokeAsync(() => SetSongImageSource(songPictureInfo));
    }

    private void SetSongImageSource(SongPictureInfo songPictureInfo)
    {
        if (songPictureInfo.Data.Length == 0)
            return;

        using MemoryStream stream = new(songPictureInfo.Data);
        SongImageSource = new(stream);
    }

    public void Dispose()
    {
        SongImageSource?.Dispose();
        GC.SuppressFinalize(this);
    }
}