using System.IO;
using System.Windows;
using System.Windows.Resources;
using Windows.Media.Core;
using Windows.Storage.Streams;
using WinMediaPlayer = Windows.Media.Playback.MediaPlayer;

namespace Noctalk_client.Client.Sound;

public sealed class Client_sound_engine
{
    private readonly Action<string>? log;
    private readonly object activeSessionsLock = new();
    private readonly List<Client_sound_session> activeSessions = [];
    private readonly Dictionary<enum__client_sound, byte[]> soundBytesBySound = [];

    public bool bSound_on { get; set; } = true;

    public Client_sound_engine(Action<string>? The_log = null)
    {
        log = The_log;
    }

    public void Sound__play(enum__client_sound The_sound)
    {
        _ = Sound__play_async(The_sound);
    }

    private async Task Sound__play_async(enum__client_sound The_sound)
    {
        try
        {
            if (!bSound_on)
            {
                return;
            }

            byte[] soundBytes = await Sound__bytes__get_async(The_sound);
            InMemoryRandomAccessStream randomAccessStream = new();
            DataWriter writer = new(randomAccessStream.GetOutputStreamAt(0));
            writer.WriteBytes(soundBytes);
            await writer.StoreAsync();
            await writer.FlushAsync();
            writer.DetachStream();
            writer.Dispose();
            randomAccessStream.Seek(0);

            MediaSource source = MediaSource.CreateFromStream(randomAccessStream, "audio/mpeg");
            WinMediaPlayer player = new() { Source = source };
            Client_sound_session session = new(player, source, randomAccessStream);
            player.MediaEnded += (_, _) => Sound__session__dispose(session);
            player.MediaFailed += (_, _) => Sound__session__dispose(session);

            lock (activeSessionsLock)
            {
                activeSessions.Add(session);
            }

            player.Play();
        }
        catch (Exception exception)
        {
            log?.Invoke($"Sound playback failed: {exception.Message}");
        }
    }

    private async Task<byte[]> Sound__bytes__get_async(enum__client_sound The_sound)
    {
        if (soundBytesBySound.TryGetValue(The_sound, out byte[]? soundBytes))
        {
            return soundBytes;
        }

        StreamResourceInfo? resourceInfo = Application.GetResourceStream(new Uri(Sound__resource_path__get(The_sound), UriKind.Relative));
        if (resourceInfo is null)
        {
            throw new InvalidOperationException($"Embedded sound resource was not found: {Sound__resource_path__get(The_sound)}");
        }

        using Stream resourceStream = resourceInfo.Stream;
        using MemoryStream memoryStream = new();
        await resourceStream.CopyToAsync(memoryStream);
        soundBytes = memoryStream.ToArray();
        soundBytesBySound[The_sound] = soundBytes;
        return soundBytes;
    }

    private static string Sound__resource_path__get(enum__client_sound The_sound)
    {
        return The_sound switch
        {
            enum__client_sound.Received_image => "Sfx/Received_image.mp3",
            enum__client_sound.User_online => "Sfx/User_online.mp3",
            _ => "Sfx/Received_message.mp3"
        };
    }

    private void Sound__session__dispose(Client_sound_session The_session)
    {
        lock (activeSessionsLock)
        {
            activeSessions.Remove(The_session);
        }

        The_session.Dispose();
    }

    private sealed class Client_sound_session : IDisposable
    {
        private readonly WinMediaPlayer player;
        private readonly MediaSource source;
        private readonly InMemoryRandomAccessStream stream;

        public Client_sound_session(WinMediaPlayer The_player, MediaSource The_source, InMemoryRandomAccessStream The_stream)
        {
            player = The_player;
            source = The_source;
            stream = The_stream;
        }

        public void Dispose()
        {
            player.Dispose();
            source.Dispose();
            stream.Dispose();
        }
    }
}
