using Quasar.Common.Video;
using System;
using System.Net;
using System.Net.Sockets;

namespace Quasar.Client.Networking
{
    /// <summary>
    /// Sends frame data over UDP to the server, fragmenting into MTU-safe chunks.
    /// Compresses the payload with Deflate before fragmenting.
    /// </summary>
    public class UdpFrameSender : IDisposable
    {
        private UdpClient _udp;
        private IPEndPoint _serverEndPoint;
        private uint _frameId = 0;
        private bool _disposed;

        public bool IsConnected { get; private set; }

        public UdpFrameSender(string serverHost, int serverPort)
        {
            try
            {
                _serverEndPoint = new IPEndPoint(Dns.GetHostAddresses(serverHost)[0], serverPort);
                _udp = new UdpClient();
                _udp.Client.SendBufferSize = 1024 * 512;
                // "Connect" so we can use Send() instead of SendTo()
                _udp.Connect(_serverEndPoint);
                IsConnected = true;
            }
            catch
            {
                IsConnected = false;
            }
        }

        /// <summary>
        /// Sends a frame payload. Compresses, fragments, and fires each chunk as a UDP datagram.
        /// </summary>
        /// <param name="frameData">Raw codec output bytes.</param>
        /// <param name="isKeyFrame">True if this is a full/key frame.</param>
        public void SendFrame(byte[] frameData, bool isKeyFrame)
        {
            if (!IsConnected || _disposed) return;

            try
            {
                // Compress
                byte[] compressed = UdpFrameProtocol.Compress(frameData);

                ushort flags = UdpFrameProtocol.FlagCompressed;
                if (isKeyFrame) flags |= UdpFrameProtocol.FlagKeyFrame;

                uint frameId = _frameId++;

                // Fragment
                int totalBytes = compressed.Length;
                int chunkCount = (totalBytes + UdpFrameProtocol.MaxPayloadSize - 1) / UdpFrameProtocol.MaxPayloadSize;
                if (chunkCount > ushort.MaxValue) return; // frame too large, skip

                ushort totalChunks = (ushort)chunkCount;

                for (ushort i = 0; i < totalChunks; i++)
                {
                    int offset = i * UdpFrameProtocol.MaxPayloadSize;
                    int length = Math.Min(UdpFrameProtocol.MaxPayloadSize, totalBytes - offset);

                    byte[] packet = UdpFrameProtocol.BuildPacket(frameId, i, totalChunks, flags, compressed, offset, length);
                    _udp.Send(packet, packet.Length);
                }
            }
            catch
            {
                IsConnected = false;
            }
        }

        public void Dispose()
        {
            _disposed = true;
            IsConnected = false;
            try { _udp?.Close(); } catch { }
            _udp = null;
        }
    }
}
