using Harmonia.Core.Caching;
using Harmonia.Core.Imaging;
using Harmonia.Core.Models;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Harmonia.WinUI.Caching;

public class AudioBitmapImageCache(IAudioImageExtractor audioImageExtractor) : MemoryCache<Song, BitmapImage>, IAudioBitmapImageCache
{
    protected override MemoryCacheOptions Options => new()
    {
        SizeLimit = 200_000_000,
        CompactionPercentage = 0.2,
    };

    protected override TimeSpan SlidingExpiration => TimeSpan.FromSeconds(600);
    protected override int MaxConcurrentRequests => 8;
    protected virtual int MaxImageWidthOrHeight => 1000;

    protected override object? GetKey(Song key)
    {
        if (string.IsNullOrWhiteSpace(key.ImageHash) == false)
        {
            return key.ImageHash;
        }
        else if (string.IsNullOrWhiteSpace(key.ImageName) == false)
        {
            return key.ImageName;
        }

        return "Default";
    }

    protected override async ValueTask<BitmapImage?> FetchAsync(Song key, CancellationToken cancellationToken)
    {
        SongPictureInfo? songPictureInfo = await audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken);

        if (songPictureInfo == null)
            return GetDefaultBitmapImage();

        using MemoryStream stream = new(songPictureInfo.Data);

        BitmapImage bitmapImage = new();

        await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());

        bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage);
        bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage);

        return bitmapImage;
    }

    private BitmapImage GetDefaultBitmapImage()
    {
        Uri uri = new("ms-appx:///Assets/Default.png", UriKind.Absolute);

        BitmapImage bitmapImage = new();
        bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage);
        bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage);
        bitmapImage.UriSource = uri;

        return bitmapImage;
    }

    private int GetDecodePixelWidth(BitmapImage bitmapImage)
    {
        int originalImageWidth = bitmapImage.PixelWidth;
        int orignalImageHeight = bitmapImage.PixelHeight;

        if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight)
            return 0;

        if (orignalImageHeight > originalImageWidth)
            return 0;

        return MaxImageWidthOrHeight;
    }

    private int GetDecodePixelHeight(BitmapImage bitmapImage)
    {
        int originalImageWidth = bitmapImage.PixelWidth;
        int orignalImageHeight = bitmapImage.PixelHeight;

        if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight)
            return 0;

        if (originalImageWidth > orignalImageHeight)
            return 0;

        return MaxImageWidthOrHeight;
    }

    protected override long GetEntrySize(BitmapImage entry)
    {
        return entry.DecodePixelWidth * entry.DecodePixelHeight;
    }
}


public class AudioBitmapImageCache2 : MemoryCache<Song, BitmapImage>, IAudioBitmapImageCache
{
    private readonly IAudioImageExtractor _audioImageExtractor;
    private readonly BitmapImage[] _bitmapPool;
    private int _nextIndex = 0;
    private ConcurrentDictionary<object, BitmapImage> _test = [];

    protected override MemoryCacheOptions Options => new()
    {
        SizeLimit = 200_000_000,
        CompactionPercentage = 0.2,
    };

    protected override TimeSpan SlidingExpiration => TimeSpan.FromSeconds(600);
    protected override int MaxConcurrentRequests => 8;
    protected virtual int MaxImageWidthOrHeight => 1000;
    protected virtual int BitmapPoolSize => 64;

    public AudioBitmapImageCache2(IAudioImageExtractor audioImageExtractor)
    {
        _audioImageExtractor = audioImageExtractor;
        _bitmapPool = new BitmapImage[BitmapPoolSize];

        InitializeBitmapPool();
    }

    private void InitializeBitmapPool()
    {
        for (int i = 0; i < _bitmapPool.Length; i++)
            _bitmapPool[i] = new BitmapImage();

        //DispatcherQueue dispatcherQueue = DispatcherQueue.GetForCurrentThread();

        //TaskCompletionSource taskCompletionSource = new();

        //dispatcherQueue.TryEnqueue(() =>
        //{
        //    for (int i = 0; i < _bitmapPool.Length; i++)
        //        _bitmapPool[i] = new BitmapImage();

        //    taskCompletionSource.SetResult();
        //});

        //taskCompletionSource.Task.GetAwaiter().GetResult();
    }

    protected override object? GetKey(Song key)
    {
        if (string.IsNullOrWhiteSpace(key.ImageHash) == false)
        {
            return key.ImageHash;
        }
        else if (string.IsNullOrWhiteSpace(key.ImageName) == false)
        {
            return key.ImageName;
        }

        return "Default";
    }

    protected override async ValueTask<BitmapImage?> FetchAsync(Song key, CancellationToken cancellationToken)
    {
        int index = Interlocked.Increment(ref _nextIndex);
        BitmapImage bitmapImage = _bitmapPool[index % _bitmapPool.Length];
        //_test.AddOrUpdate(index, bitmapImage);

        SongPictureInfo? songPictureInfo = await _audioImageExtractor.ExtractImageAsync(key.FileName, cancellationToken);

        if (songPictureInfo == null)
        {
            bitmapImage.UriSource = new("ms-appx:///Assets/Default.png", UriKind.Absolute);
        }
        else
        {
            using MemoryStream stream = new(songPictureInfo.Data);
            await bitmapImage.SetSourceAsync(stream.AsRandomAccessStream());
        }

        bitmapImage.DecodePixelWidth = GetDecodePixelWidth(bitmapImage);
        bitmapImage.DecodePixelHeight = GetDecodePixelHeight(bitmapImage);

        return bitmapImage;
    }

    private int GetDecodePixelWidth(BitmapImage bitmapImage)
    {
        int originalImageWidth = bitmapImage.PixelWidth;
        int orignalImageHeight = bitmapImage.PixelHeight;

        if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight)
            return 0;

        if (orignalImageHeight > originalImageWidth)
            return 0;

        return MaxImageWidthOrHeight;
    }

    private int GetDecodePixelHeight(BitmapImage bitmapImage)
    {
        int originalImageWidth = bitmapImage.PixelWidth;
        int orignalImageHeight = bitmapImage.PixelHeight;

        if (originalImageWidth <= MaxImageWidthOrHeight && orignalImageHeight <= MaxImageWidthOrHeight)
            return 0;

        if (originalImageWidth > orignalImageHeight)
            return 0;

        return MaxImageWidthOrHeight;
    }

    protected override long GetEntrySize(BitmapImage entry)
    {
        return entry.DecodePixelWidth * entry.DecodePixelHeight;
    }

    protected override void PostEvictionCallback(object? cacheKey, object? cacheValue, EvictionReason evictionReason, object? state)
    {
        
    }
}