#pragma once

#include "protocol.h"
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <vector>

/**
 * Active UDP client for a MARTe2 UDPStreamer source.
 *
 * Connects to host:port, sends CONNECT, receives CONFIG + DATA packets.
 * Auto-reconnects after 5 seconds of silence.  Calls on_config and on_data
 * from the ingest thread.
 */
class UDPClient {
public:
    struct Stats {
        uint64_t packets_rx   = 0;
        uint64_t packets_lost = 0;
        uint64_t bytes_rx     = 0;
        double   rate_hz      = 0.0;
        bool     connected    = false;
    };

    /**
     * @param host            Server hostname / IP.
     * @param port            Control port (UDP unicast, or TCP in multicast mode).
     * @param multicast_group Multicast IP (e.g. "239.0.0.1"); "" = unicast.
     * @param data_port       UDP data port in multicast mode; 0 = port+1.
     */
    UDPClient(std::string host, uint16_t port,
              std::string multicast_group = "", uint16_t data_port = 0);
    ~UDPClient();

    void start();
    void stop();

    Stats stats() const;

    /** Called on ingest thread when a CONFIG packet arrives. */
    std::function<void(const std::vector<SignalDef>&)> on_config;

    /** Called on ingest thread for each complete DATA packet. */
    std::function<void(const DataPacket&, const std::vector<SignalDef>&)> on_data;

private:
    void run();
    void run_unicast();
    void run_multicast();
    bool run_multicast_session();

    std::string          host_;
    uint16_t             port_;
    std::string          multicast_group_;
    uint16_t             data_port_;
    std::thread          thread_;
    std::atomic<bool>    running_{false};

    std::atomic<uint64_t> rx_packets_{0};
    std::atomic<uint64_t> rx_bytes_{0};
    std::atomic<uint64_t> seq_gaps_{0};
    std::atomic<bool>     connected_{false};
};
