using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace FastUDP
{
    public class QuickUdpClient : IDisposable
    {
        private readonly Socket _socket;
        private readonly IPEndPoint _remoteEndPoint;
        private bool _disposed = false;

        /// <summary>
        /// 构造函数：直接接收本地端口、远程IP、远程端口以及可选的超时时间
        /// </summary>
        /// <param name="localPort">本地绑定的监听端口</param>
        /// <param name="remoteIp">远程目标的IP地址（如 "127.0.0.1"）</param>
        /// <param name="remotePort">远程目标的接收端口</param>
        /// <param name="timeoutSeconds">接收超时时间（单位：秒），默认 2 秒</param>
        public QuickUdpClient(int localPort, string remoteIp, int remotePort, int timeoutSeconds = 2)
        {
            // 1. 内部自动构建本地端点
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, localPort);

            // 2. 内部自动解析远程目标端点
            if (!IPAddress.TryParse(remoteIp, out IPAddress parsedIp))
            {
                throw new ArgumentException("非法的远程 IP 地址格式", nameof(remoteIp));
            }
            _remoteEndPoint = new IPEndPoint(parsedIp, remotePort);

            // 3. 创建底层的 UDP Socket 并绑定本地端口
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _socket.Bind(localEndPoint);

            // 4. 核心：设定接收超时时间（Socket 接收超时单位为毫秒）
            _socket.ReceiveTimeout = timeoutSeconds * 1000;
        }

        /// <summary>
        /// 发送文本数据到构造函数中指定的远程目标
        /// </summary>
        public void Send(string message)
        {
            if (_disposed) throw new ObjectDisposedException(nameof(QuickUdpClient));
            if (string.IsNullOrEmpty(message)) return;

            byte[] data = Encoding.UTF8.GetBytes(message);
            _socket.SendTo(data, data.Length, SocketFlags.None, _remoteEndPoint);
        }

        public void Send(byte[] data)
        {
            _socket.SendTo(data, data.Length, SocketFlags.None, _remoteEndPoint);
        }

        /// <summary>
        /// 同步阻塞接收一条数据，若超过设定的秒数未收到则抛出超时异常
        /// </summary>
        /// <returns>收到的字符串消息</returns>
        public string ReceiveStr()
        {
            if (_disposed) throw new ObjectDisposedException(nameof(QuickUdpClient));

            // 准备一个 64KB 的缓冲区（UDP 最大报文限制）
            byte[] buffer = new byte[65535];

            // 这里的 remoteEP 在接收时会作为占位符，内部会被填充为实际发送方的 IP 和端口
            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

            try
            {
                // 如果在指定时间内没收到数据，这里会直接抛出 SocketException 异常
                int receivedBytes = _socket.ReceiveFrom(buffer, ref remoteEP);

                return Encoding.UTF8.GetString(buffer, 0, receivedBytes);
            }
            catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
            {
                // 将底层的超时错误包装为更友好的系统超时异常
                throw new TimeoutException("UDP 接收数据超时，对方在指定时间内未响应。", ex);
            }
        }

        public byte[]  ReceiveByte( )
        {
            if (_disposed) throw new ObjectDisposedException(nameof(QuickUdpClient));

            // 准备一个 64KB 的缓冲区（UDP 最大报文限制）
            byte[] buffer = new byte[65535];

            // 这里的 remoteEP 在接收时会作为占位符，内部会被填充为实际发送方的 IP 和端口
            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

            try
            {
                int receivedBytes = _socket.ReceiveFrom(buffer, ref remoteEP);

                // 只返回实际长度的副本
                byte[] result = new byte[receivedBytes];
                Array.Copy(buffer, 0, result, 0, receivedBytes);
                return result;
            }
            catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
            {
                // 将底层的超时错误包装为更友好的系统超时异常
                throw new TimeoutException("UDP 接收数据超时，对方在指定时间内未响应。", ex);
            }
        }

        /// <summary>
        /// 实现 IDisposable 接口，供 using 关键字自动释放资源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _socket?.Close();
                    _socket?.Dispose();
                }
                _disposed = true;
            }
        }

        static void Main(string[] args)
        {
            using (QuickUdpClient client = new QuickUdpClient(20012, "127.0.0.1", 50013))
            {
                client.Send("Hello, UDP Server!");
                string response = client.ReceiveStr();
                Console.WriteLine("Received from server: " + response);
            }

            using (QuickUdpClient client = new QuickUdpClient(20012, "127.0.0.1", 50013))
            {
                client.Send("Hello, UDP Server!");
                byte[] response = client.ReceiveByte();
                Console.WriteLine("Received from server: " + BitConverter.ToString(response));
            }

        }
    }
}


