using System.Net;
using System.Net.Sockets;

namespace Pilz.Net;

public class UDPManager : ConnectionManagerBase
{
    private readonly UdpClient client;
    private Task? listenTask = null;
    private readonly CancellationTokenSource cancelTokenSource = new();
    private readonly CancellationToken cancelToken;
    public int MaxBufferSize { get; private set; } = 8192;

    public UDPManager(int port) : base(port)
    {
        cancelToken = cancelTokenSource.Token;
        client = new UdpClient(port);
    }

    ~UDPManager()
    {
        client.Client.Shutdown(SocketShutdown.Both);
        client.Close();
    }

    public override void Start()
    {
        if (!IsListening)
        {
            StartInternal();
        }
    }

    private void StartInternal()
    {
        IsListening = true;
        listenTask = Task.Run(() => { try { RetriveAnyData(cancelToken); } catch (Exception ex) { IsListening = false; } });
    }

    public override void Stop()
    {
        if (IsListening)
        {
            IsListening = false;
            cancelTokenSource.Cancel();
            listenTask?.Wait();
        }
    }

    protected override int GetBufferSize()
    {
        return MaxBufferSize;
    }

    private void RetriveAnyData(CancellationToken ct)
    {
        void doExit() => ct.ThrowIfCancellationRequested();
        var receiveTask = client.ReceiveAsync();

        // Wait for the data and cancel if requested
        receiveTask.Wait(ct);

        byte[] buf = receiveTask.Result.Buffer;
        string ip = receiveTask.Result.RemoteEndPoint.Address.ToString();

        doExit();

        ProcessRetrivedData(ip, buf);

        doExit();

        StartInternal();
    }

    protected override void SendData(IPEndPoint ep, byte[] buf)
    {
        var udp = new UdpClient();
        udp.Connect(ep);
        udp.Send(buf, buf.Length);
        udp.Client.Shutdown(SocketShutdown.Both);
        udp.Close();
    }
}