using System;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NAudio.Wave;
using System.Collections.Concurrent;

namespace sharpFRN
{
    public class WebSocketAudioStreamer
    {
        private WaveOutEvent waveOut;
        private BufferedWaveProvider bufferedWaveProvider;
        private HttpListener httpListener;
        private ConcurrentDictionary<string, WebSocket> webSockets = new ConcurrentDictionary<string, WebSocket>();

        public WebSocketAudioStreamer()
        {
            InitializeGsmDecoder();
        }

        private void InitializeGsmDecoder()
        {
            WaveFormat pcmFormat = new WaveFormat(8000, 16, 1);

            waveOut = new WaveOutEvent();
            bufferedWaveProvider = new BufferedWaveProvider(pcmFormat)
            {
                BufferDuration = TimeSpan.FromSeconds(5) // Adjust if needed
            };
            waveOut.Init(bufferedWaveProvider);
            waveOut.Play();
        }

        public void PlayGSMChunk(byte[] voiceData)
        {
            if (voiceData.Length != 325)
            {
                Console.WriteLine($"Unexpected voice data length: {voiceData.Length}");
                return;
            }

            int frameSize = 65; // GSM frame size
            int numFrames = voiceData.Length / frameSize;

            for (int i = 0; i < numFrames; i++)
            {
                byte[] gsmFrame = new byte[frameSize];
                Array.Copy(voiceData, i * frameSize, gsmFrame, 0, frameSize);

                using (var gsmStream = new RawSourceWaveStream(new MemoryStream(gsmFrame), new Gsm610WaveFormat()))
                using (var pcmStream = WaveFormatConversionStream.CreatePcmStream(gsmStream))
                {
                    byte[] buffer = new byte[pcmStream.Length];
                    int bytesRead = pcmStream.Read(buffer, 0, buffer.Length);

                    // Add the PCM data to the BufferedWaveProvider
                    //bufferedWaveProvider.AddSamples(buffer, 0, bytesRead);

                    // Send the PCM data to all connected WebSocket clients
                    BroadcastToWebSockets(buffer, 0, bytesRead);
                }
            }
        }

        public async Task StartWebSocketServerAsync(string urlPrefix)
        {
            httpListener = new HttpListener();
            httpListener.Prefixes.Add(urlPrefix);
            httpListener.Start();
            Console.WriteLine($"WebSocket server started at {urlPrefix}");

            while (true)
            {
                var httpContext = await httpListener.GetContextAsync();

                if (httpContext.Request.IsWebSocketRequest)
                {
                    var webSocketContext = await httpContext.AcceptWebSocketAsync(null);
                    var webSocket = webSocketContext.WebSocket;
                    string clientId = Guid.NewGuid().ToString();
                    webSockets.TryAdd(clientId, webSocket);
                    Console.WriteLine($"WebSocket client connected: {clientId}");

                    _ = Task.Run(() => HandleWebSocketConnectionAsync(clientId, webSocket));
                }
                else
                {
                    httpContext.Response.StatusCode = 400;
                    httpContext.Response.Close();
                }
            }
        }

        private async Task HandleWebSocketConnectionAsync(string clientId, WebSocket webSocket)
        {
            var buffer = new byte[1024];
            try
            {
                while (webSocket.State == WebSocketState.Open)
                {
                    var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"WebSocket error: {ex.Message}");
            }
            finally
            {
                webSockets.TryRemove(clientId, out _);
                Console.WriteLine($"WebSocket client disconnected: {clientId}");
            }
        }

        private void BroadcastToWebSockets(byte[] buffer, int offset, int count)
        {
            foreach (var webSocket in webSockets.Values)
            {
                if (webSocket.State == WebSocketState.Open)
                {
                    webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, CancellationToken.None).Wait();
                }
            }
        }
    }
}
