using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Inspectron.HawkEye
{
    public class UDPSocket:IDisposable
    {
        private Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        private const int bufSize = 8 * 1024;
        private State state = new State();
        private EndPoint epFrom = new IPEndPoint(IPAddress.Any, 0);
        private AsyncCallback recv = null;

        public class State
        {
            public byte[] buffer = new byte[bufSize];
        }

        public void Server(string address, int port)
        {
            _socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true);
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 10*1024*1024);
            
            _socket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
            
            ReceiveAsync();
        }

        public void Client(string address, int port)
        {
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 10 * 1024 * 1024);
            
            _socket.Connect(IPAddress.Parse(address), port);
            ReceiveAsync();
        }

        public void Send(byte[] data)
        {
            
            //_socket.BeginSend(data, 0, data.Length, SocketFlags.None, (ar) =>
            //{
            //    State so = (State)ar.AsyncState;
            //    int bytes = _socket.EndSend(ar);
            //}, state);

            for (int i = 0; i < Math.Ceiling(data.Length/1050.0); i++)
            {
                var size = Math.Min(1050, data.Length - i * 1050);
                _socket.Send(data,i* 1050, size,SocketFlags.None);
            }
            
            
        }

        public event Action<byte[]> Received = delegate { };
        ConcurrentQueue<byte[]> _receivePool = new ConcurrentQueue<byte[]>();
        private void ReceiveAsync()
        {
            for (int i = 0; i < 1000; i++)
            {
                _receivePool.Enqueue(new byte[1050]);
            }

            _receivePool.TryDequeue(out var buffer);
            _socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref epFrom, recv = (ar) =>
            {
                byte[] so = (byte[])ar.AsyncState;
                Task.Run(() =>
                {
                    Received(so);
                    _receivePool.Enqueue(so);
                }
                    );
                int bytes = _socket.EndReceiveFrom(ar, ref epFrom);

                _receivePool.TryDequeue(out var bufferLoc);
                _socket.BeginReceiveFrom(bufferLoc, 0, so.Length, SocketFlags.None, ref epFrom, recv, bufferLoc);


            }, buffer);
        }
        byte[] _receiveBuffer = new byte[1024*1024];
        public byte[] Receive()
        {
            int received = 0;
            while (received<1024*1024)
            {
                received+=_socket.Receive(_receiveBuffer);
            }
            
            return _receiveBuffer;
        }


        public void Dispose()
        {
            _socket?.Dispose();
        }
    }
}