using PortAudioSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using XiaoZhiSharp.Utils;

namespace XiaoZhiSharp.Services
{
    public class AudioPortService : IAudioService, IDisposable
    {
        // 音频输出相关组件
        private readonly PortAudioSharp.Stream? _waveOut;
        private readonly Queue<float[]> _waveOutStream = new Queue<float[]>();

        // 音频输入相关组件
        private readonly PortAudioSharp.Stream? _waveIn;

        public delegate Task PcmAudioEventHandler(byte[] pcm);
        public event IAudioService.PcmAudioEventHandler? OnPcmAudioEvent;

        // 音频参数
        private const int SampleRate = 24000;
        public int SampleRate_WaveIn { get; set; } = 16000;
        private const int Bitrate = 16;
        private const int Channels = 1;
        private const int FrameDuration = 60;
        private const int FrameSize = SampleRate * FrameDuration / 1000; // 帧大小

        public bool IsRecording { get; private set; }
        public bool IsPlaying { get; private set; }
        public int VadCounter { get; private set; } = 0; // 用于语音活动检测的计数器
        public AudioPortService()
        {
            // 初始化音频输出组件
            PortAudio.Initialize();

            int outputDeviceIndex = PortAudio.DefaultOutputDevice;
            if (outputDeviceIndex == PortAudio.NoDevice)
            {
                Console.WriteLine("No default output device found");
                LogConsole.InfoLine(PortAudio.VersionInfo.versionText);
                LogConsole.WriteLine($"Number of devices: {PortAudio.DeviceCount}");
                for (int i = 0; i != PortAudio.DeviceCount; ++i)
                {
                    LogConsole.WriteLine($" Device {i}");
                    DeviceInfo deviceInfo = PortAudio.GetDeviceInfo(i);
                    LogConsole.WriteLine($"   Name: {deviceInfo.name}");
                    LogConsole.WriteLine($"   Max input channels: {deviceInfo.maxInputChannels}");
                    LogConsole.WriteLine($"   Default sample rate: {deviceInfo.defaultSampleRate}");
                }
                //Environment.Exit(1);
            }
            var outputInfo = PortAudio.GetDeviceInfo(outputDeviceIndex);
            var outparam = new StreamParameters
            {
                device = outputDeviceIndex,
                channelCount = Channels,
                sampleFormat = SampleFormat.Float32,
                suggestedLatency = outputInfo.defaultLowOutputLatency,
                hostApiSpecificStreamInfo = IntPtr.Zero
            };
            LogConsole.InfoLine($"开始创建音频输出流...");
            _waveOut = new PortAudioSharp.Stream(
                inParams: null, outParams: outparam, sampleRate: SampleRate, framesPerBuffer: 1440,
                streamFlags: StreamFlags.ClipOff, callback: PlayCallback, userData: IntPtr.Zero
            );
            LogConsole.InfoLine($"成功创建音频输出流...");

            // 初始化音频输入组件
            int inputDeviceIndex = PortAudio.DefaultInputDevice;
            if (inputDeviceIndex == PortAudio.NoDevice)
            {
                Console.WriteLine("No default input device found");
                //Environment.Exit(1);
            }
            var inputInfo = PortAudio.GetDeviceInfo(inputDeviceIndex);
            var inparam = new StreamParameters
            {
                device = inputDeviceIndex,
                channelCount = Channels,
                sampleFormat = SampleFormat.Float32,
                suggestedLatency = inputInfo.defaultLowInputLatency,
                hostApiSpecificStreamInfo = IntPtr.Zero
            };

            LogConsole.InfoLine($"开始创建音频输入流...");
            _waveIn = new PortAudioSharp.Stream(
                inParams: inparam, outParams: null, sampleRate: SampleRate_WaveIn, framesPerBuffer: 1440,
                streamFlags: StreamFlags.ClipOff, callback: InCallback, userData: IntPtr.Zero
            );
            LogConsole.InfoLine($"成功创建音频输入流...");

            // 启动音频播放
            StartPlaying();

            LogConsole.InfoLine($"当前默认音频输入设备： {inputDeviceIndex} ({inputInfo.name})");
            LogConsole.InfoLine($"当前默认音频输出设备： {outputDeviceIndex} ({outputInfo.name})");
        }

        private StreamCallbackResult PlayCallback(
            IntPtr input, IntPtr output, uint frameCount, ref StreamCallbackTimeInfo timeInfo,
            StreamCallbackFlags statusFlags, IntPtr userData)
        {
            if (_waveOutStream.Count <= 0)
            {
                //return StreamCallbackResult.Complete;
            }
            try
            {
                while (_waveOutStream.Count > 0)
                {
                    float[]? buffer;
                    lock (_waveOutStream)
                    {
                        if (_waveOutStream.TryDequeue(out buffer))
                        {
                            if (buffer.Length < frameCount)
                            {
                                float[] paddedBuffer = new float[frameCount];
                                Array.Copy(buffer, paddedBuffer, buffer.Length);
                                Marshal.Copy(paddedBuffer, 0, output, (int)frameCount);
                                //Thread.Sleep(10);
                            }
                            else
                            {
                                Marshal.Copy(buffer, 0, output, (int)frameCount);
                            }
                        }
                        return StreamCallbackResult.Continue;
                    }
                }
                return StreamCallbackResult.Continue;
                //return StreamCallbackResult.Complete;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return StreamCallbackResult.Complete;
            }
        }

        private StreamCallbackResult InCallback(
            IntPtr input, IntPtr output, uint frameCount, ref StreamCallbackTimeInfo timeInfo,
            StreamCallbackFlags statusFlags, IntPtr userData)
        {
            try
            {
                if (!IsRecording)
                {
                    return StreamCallbackResult.Complete;
                }

                // 创建一个数组来存储输入的音频数据
                float[] samples = new float[frameCount];
                // 将输入的音频数据从非托管内存复制到托管数组
                Marshal.Copy(input, samples, 0, (int)frameCount);

                // 将音频数据转换为字节数组
                byte[] buffer = FloatArrayToByteArray(samples);

                // 处理音频数据
                if (OnPcmAudioEvent != null)
                    OnPcmAudioEvent(buffer);

                return StreamCallbackResult.Continue;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return StreamCallbackResult.Complete;
            }
        }

        public static byte[] FloatArrayToByteArray(float[] floatArray)
        {
            // 初始化一个与 float 数组长度两倍的 byte 数组，因为每个 short 占 2 个字节
            byte[] byteArray = new byte[floatArray.Length * 2];

            for (int i = 0; i < floatArray.Length; i++)
            {
                // 将 float 类型的值映射到 short 类型的范围
                short sample = (short)(floatArray[i] * short.MaxValue);

                // 将 short 类型的值拆分为两个字节
                byteArray[i * 2] = (byte)(sample & 0xFF);
                byteArray[i * 2 + 1] = (byte)(sample >> 8);
            }

            return byteArray;
        }

        public static float[] ByteArrayToFloatArray(byte[] byteArray)
        {
            int floatArrayLength = byteArray.Length / 2;
            float[] floatArray = new float[floatArrayLength];

            for (int i = 0; i < floatArrayLength; i++)
            {
                floatArray[i] = BitConverter.ToInt16(byteArray, i * 2) / 32768f;
            }

            return floatArray;
        }

        public void StartRecording()
        {
            if (!IsRecording)
            {
                _waveIn?.Start();
                IsRecording = true;
            }
        }

        public void StopRecording()
        {
            if (IsRecording)
            {
                _waveIn?.Stop();
                IsRecording = false;
            }
        }

        public void StartPlaying()
        {
            if (!IsPlaying)
            {
                _waveOut?.Start();
                IsPlaying = true;
            }
        }

        public void StopPlaying()
        {
            if (IsPlaying)
            {
                _waveOut?.Stop();
                IsPlaying = false;
            }
        }

        public void AddOutSamples(byte[] pcmData)
        {
            lock (_waveOutStream)
            {
                _waveOutStream.Enqueue(ByteArrayToFloatArray(pcmData));
            }
        }

        public void AddOutSamples(float[] pcmData)
        {
            lock (_waveOutStream)
            {
                _waveOutStream.Enqueue(pcmData);
            }
        }

        public void Dispose()
        {
            IsPlaying = false;
            IsRecording = false;
            _waveIn?.Dispose();
            _waveOut?.Dispose();
            PortAudio.Terminate();
        }
    }
}
