using CommunityToolkit.Mvvm.Input;
using Harmonia.Core.Engine;
using Harmonia.Core.Models;
using Harmonia.Core.Player;
using Harmonia.WinUI.Caching;
using Harmonia.WinUI.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.System;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;

namespace Harmonia.WinUI.ViewModels;

public partial class PlayerViewModel : ViewModelBase
{
    private readonly IAudioPlayer _audioPlayer;
    private readonly IAudioBitmapImageCache _audioBitmapImageCache;
    private readonly DispatcherTimer _timer;
    private readonly DispatcherQueue _dispatcherQueue;

    private CancellationTokenSource? _songImageCancellationTokenSource;

    private Song? _song;
    public Song? Song
    {
        get
        {
            return _song;
        }
        private set
        {
            SetProperty(ref _song, value);

            CurrentPosition = 0; // What if player is being loaded and returning to save state?
            Position = 0;
            MaxPosition = value?.Length.TotalSeconds ?? 0;
        }
    }

    private BitmapImage? _songImageSource;
    public BitmapImage? SongImageSource
    {
        get
        {
            return _songImageSource;
        }
        private set
        {
            SetProperty(ref _songImageSource, value);
        }
    }

    private double _currentPosition;
    public double CurrentPosition
    {
        get
        {
            return _currentPosition;
        }
        set
        {
            SetProperty(ref _currentPosition, value);

            if (_isPositionChangeInProgress)
                _audioPlayer.Position = value;
        }
    }

    private double _position;
    public double Position
    {
        get
        {
            return _position;
        }
        private set
        {
            SetProperty(ref _position, value);
        }
    }

    private double _maxPosition;
    public double MaxPosition
    {
        get
        {
            return _maxPosition;
        }
        set
        {
            SetProperty(ref _maxPosition, value);
        }
    }

    private bool _isPositionChangeInProgress;
    public bool IsPositionChangeInProgress
    {
        get
        {
            return _isPositionChangeInProgress;
        }
        set
        {
            SetProperty(ref _isPositionChangeInProgress, value);
        }
    }

    public double Volume
    {
        get
        {
            return _audioPlayer.Volume;
        }
        set
        {
            if (IsMuted)
                IsMuted = false;

            _audioPlayer.Volume = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(VolumeState));
        }
    }

    public bool IsMuted
    {
        get
        {
            return _audioPlayer.IsMuted;
        }
        set
        {
            _audioPlayer.IsMuted = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(VolumeState));
        }
    }

    public VolumeState VolumeState
    {
        get
        {
            if (IsMuted)
                return VolumeState.Muted;

            if (Volume == 0)
                return VolumeState.Off;

            if (Volume < .33)
                return VolumeState.Low;

            if (Volume < .66)
                return VolumeState.Medium;

            return VolumeState.High;
        }
    }

    public bool IsRandom
    {
        get
        {
            return _audioPlayer.IsRandom;
        }
        private set
        {
            _audioPlayer.IsRandom = value;
            OnPropertyChanged();
        }
    }

    public RepeatState RepeatState
    {
        get
        {
            return _audioPlayer.RepeatState;
        }
        private set
        {
            _audioPlayer.RepeatState = value;
            OnPropertyChanged();
        }
    }

    public bool CanUpdatePosition
    {
        get
        {
            return _audioPlayer.State != AudioPlaybackState.Stopped;
        }
        private set
        {
            OnPropertyChanged();
        }
    }

    public ICommand PlaySongCommand => new RelayCommand(Play);
    public ICommand PauseSongCommand => new RelayCommand(Pause);
    public ICommand StopSongCommand => new RelayCommand(Stop);
    public ICommand PreviousSongCommand => new RelayCommand(Previous);
    public ICommand NextSongCommand => new RelayCommand(Next);
    public ICommand ToggleMuteCommand => new RelayCommand(ToggleMute);
    public ICommand ToggleRandomizerCommand => new RelayCommand(ToggleRandomizer);
    public ICommand ToggleRepeatCommand => new RelayCommand(ToggleRepeat);

    public PlayerViewModel(IAudioPlayer audioPlayer, IAudioBitmapImageCache audioBitmapCache)
    {
        _audioPlayer = audioPlayer;
        _audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
        _audioPlayer.PropertyChanged += OnAudioPlayerPropertyChanged;

        _audioBitmapImageCache = audioBitmapCache;

        _timer = new()
        {
            Interval = TimeSpan.FromMilliseconds(100)
        };

        _timer.Tick += TickTock;

        _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
    }

    #region Event Handlers

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

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

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

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

        //BitmapImage? bitmapImage = await _audioBitmapImageCache.GetAsync(Song, cancellationToken);

        _dispatcherQueue.TryEnqueue(async () =>
        {
            BitmapImage? bitmapImage = await _audioBitmapImageCache.GetAsync(Song, cancellationToken);
            SongImageSource = bitmapImage;
        });
    }

    private void OnAudioPlayerPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(_audioPlayer.State):
                UpdateTimer();
                OnPropertyChanged(nameof(CanUpdatePosition));
                break;
        }
    }

    private void UpdateTimer()
    {
        if (_audioPlayer.State == AudioPlaybackState.Playing)
        {
            _timer.Start();
        }
        else
        {
            _timer.Stop();
        }
    }

    private void TickTock(object? sender, object e)
    {
        Position = _audioPlayer.Position;

        if (IsPositionChangeInProgress)
            return;

        CurrentPosition = _audioPlayer.Position;
    }

    #endregion

    #region Commands

    public void Play()
    {
        _audioPlayer.Play();
    }

    public void Pause()
    {
        _audioPlayer.Pause();
    }

    public void Stop()
    {
        _audioPlayer.Stop();
        CurrentPosition = 0;
        Position = 0;
    }

    public void Previous()
    {
        _audioPlayer.PreviousAsync();
    }

    public void Next()
    {
        _audioPlayer.NextAsync();
    }

    public void ToggleMute()
    {
        IsMuted = !IsMuted;
    }

    public void ToggleRandomizer()
    {
        IsRandom = !IsRandom;
    }

    public void ToggleRepeat()
    {
        RepeatState = GetNextRepeatState();
    }

    private RepeatState GetNextRepeatState()
    {
        return _audioPlayer.RepeatState switch
        {
            RepeatState.Off => RepeatState.RepeatAll,
            RepeatState.RepeatAll => RepeatState.RepeatOne,
            RepeatState.RepeatOne => RepeatState.Off,
            _ => _audioPlayer.RepeatState,
        };
    }

    #endregion
}