/*
 * SPDX-FileCopyrightText: 2004 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#ifndef TANGO_SERVER_ZMQEVENTSUPPLIER_H
#define TANGO_SERVER_ZMQEVENTSUPPLIER_H

#include <tango/common/zmq_event_marshalling.h>
#include <tango/server/EventSupplier.h>

#include <zmq.hpp>

#include <list>
#include <map>
#include <string>
#include <vector>

namespace Tango
{

//---------------------------------------------------------------------
//
//              ZmqEventSupplier class
//
//---------------------------------------------------------------------

#define LARGE_DATA_THRESHOLD 2048
#define LARGE_DATA_THRESHOLD_ENCODED LARGE_DATA_THRESHOLD * 4

class ZmqEventSupplier : public EventSupplier
{
  public:
    static ZmqEventSupplier *create(Util *);
    static void cleanup();
    ~ZmqEventSupplier() override;

    //------------------ Push event -------------------------------

    void push_heartbeat_event() override;
    void push_event(DeviceImpl *,
                    std::string,
                    const std::vector<std::string> &,
                    const std::vector<double> &,
                    const std::vector<std::string> &,
                    const std::vector<long> &,
                    const struct SuppliedEventData &,
                    const std::string &,
                    DevFailed *,
                    bool) override;

    std::string &get_heartbeat_endpoint()
    {
        return heartbeat_endpoint;
    }

    std::string &get_event_endpoint()
    {
        return event_endpoint;
    }

    std::vector<std::string> &get_alternate_heartbeat_endpoint()
    {
        return alternate_h_endpoint;
    }

    std::vector<std::string> &get_alternate_event_endpoint()
    {
        return alternate_e_endpoint;
    }

    void create_event_socket();
    void create_mcast_event_socket(const std::string &, const std::string &, int, bool);
    bool is_event_mcast(const std::string &);
    std::string &get_mcast_event_endpoint(const std::string &);
    void init_event_cptr(const std::string &event_name);

    size_t get_mcast_event_nb()
    {
        return event_mcast.size();
    }

    bool update_connected_client(client_addr *);

    void set_double_send()
    {
        double_send++;
        double_send_heartbeat = true;
    }

    int get_zmq_release()
    {
        return zmq_release;
    }

    int get_calling_th()
    {
        return calling_th;
    }

    std::string create_full_event_name(DeviceImpl *device_impl,
                                       const std::string &event_type,
                                       const std::string &obj_name_lower,
                                       bool intr_change);

    void query_event_system(std::ostream &os);
    static void enable_perf_mon(Tango::DevBoolean enabled);

  protected:
    ZmqEventSupplier(Util *);

  private:
    static ZmqEventSupplier *_instance;

    struct McastSocketPub
    {
        std::string endpoint;
        zmq::socket_t *pub_socket;
        bool local_client;
        bool double_send;
    };

    struct ConnectedClient
    {
        client_addr clnt;
        std::chrono::steady_clock::time_point date;
    };

    zmq::context_t zmq_context;                        // ZMQ context
    zmq::socket_t *heartbeat_pub_sock;                 // heartbeat publisher socket
    zmq::socket_t *event_pub_sock{nullptr};            // events publisher socket
    std::map<std::string, McastSocketPub> event_mcast; // multicast socket(s) map
                                                       // The key is the full event name
                                                       // ie: tango://kidiboo.esrf.fr:1000/dev/test/10/att.change

    std::string heartbeat_endpoint;     // heartbeat publisher endpoint
    std::string host_ip;                // Host IP address
    std::vector<std::string> alt_ip;    // Host alternate IP addresses
    std::string heartbeat_event_name;   // The event name used for the heartbeat
    ZmqCallInfo heartbeat_call;         // The heartbeat call info
    cdrMemoryStream heartbeat_call_cdr; //
    TangoCdrMemoryStream data_call_cdr;
    std::string event_name;
    std::vector<std::string> alternate_h_endpoint; // Alternate heartbeat endpoint (host with several NIC)

    zmq::message_t endian_mess;             // Zmq messages
    zmq::message_t endian_mess_2;           //
    zmq::message_t endian_mess_heartbeat;   //
    zmq::message_t endian_mess_heartbeat_2; //
    zmq::message_t heartbeat_call_mess;     //
    zmq::message_t heartbeat_call_mess_2;   //

    unsigned char host_endian; // the host endianess

    bool ip_specified;          // The user has specified an IP address
    bool name_specified{false}; // The user has specified a name as IP address
    std::string user_ip;        // The specified IP address

    std::string event_endpoint;                    // event publisher endpoint
    std::vector<std::string> alternate_e_endpoint; // Alternate event endpoint (host with several NIC)

    std::map<std::string, unsigned int> event_cptr; // event counter map

    std::list<ConnectedClient> con_client; // Connected clients
    int double_send{0};                    // Double send ctr
    bool double_send_heartbeat{false};

    int zmq_release; // ZMQ lib release

    int calling_th;

    void tango_bind(zmq::socket_t *, std::string &);
    unsigned char test_endian();
    void create_mcast_socket(const std::string &, int, McastSocketPub &);
    size_t get_blob_data_nb(DevVarPipeDataEltArray &);
    size_t get_data_elt_data_nb(DevPipeDataElt &);
    std::string ctr_event_name;

    /// Rebuild the heartbeat event name
    void rebuild_heartbeat_event_name();
};

} // namespace Tango

#endif
