using Insight.Domain.Network.Remote.Messages;
using Insight.Remote.Shared.Abstractions;
using Microsoft.Extensions.Logging;
using NAudio.Wave;
using System.IO;
using Vaitr.Bus;

namespace Insight.Remote.Windows.Services;

internal class WinAudioProvider : IAudioProvider
{
    private WasapiLoopbackCapture? _capturer;
    private WaveFormat? _targetFormat;

    private readonly Bus _bus;
    private readonly ILogger<WinAudioProvider> _logger;

    public WinAudioProvider(Bus bus, ILogger<WinAudioProvider> logger)
    {
        _bus = bus;
        _logger = logger;
    }

    public async Task InitAsync(CancellationToken cancellationToken)
    {
        _capturer = new WasapiLoopbackCapture();

        try
        {
            _targetFormat ??= new WaveFormat(16000, 8, 1);

            _capturer.DataAvailable += OnDataAvailable;
            _capturer.StartRecording();

            while (cancellationToken.IsCancellationRequested is false)
            {
                await Task.Delay(500, cancellationToken);
            }
        }
        catch (TaskCanceledException) { }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error while creating audio capturer.  Make sure a sound device is installed and working.");
        }
        finally
        {
            _capturer.DataAvailable -= OnDataAvailable;
            _capturer.StopRecording();
            _capturer.Dispose();
        }
    }

    private async void OnDataAvailable(object? sender, WaveInEventArgs args)
    {
        if (_capturer is null || args.Buffer.All(x => x == 0) || args.BytesRecorded <= 0) return;

        // gets auto-disposed after writer
        using var inputStream = new MemoryStream();

        // writer needs to be disposed here
        using (var writer = new WaveFileWriter(inputStream, _capturer.WaveFormat))
            writer.Write(args.Buffer);

        // gets auto-disposed after reader
        using var workStream = new MemoryStream(inputStream.ToArray());
        using var reader = new WaveFileReader(workStream);

        using var outputStream = new MemoryStream();

        // resample to 16-bit
        using (var resampler = new MediaFoundationResampler(reader, _targetFormat))
        {
            WaveFileWriter.WriteWavFileToStream(outputStream, resampler);
        }

        // invoke event
        await _bus.PublishAsync(new CastAudio
        {
            Buffer = outputStream.ToArray()
        }, cancellationToken: default);
    }
}