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

#ifndef TANGO_SERVER_EVENTSUPPLIER_H
#define TANGO_SERVER_EVENTSUPPLIER_H

#include <tango/common/omnithread_wrapper.h>
#include <tango/server/BlackBox.h>
#include <tango/server/DeviceImpl.h>
#include <tango/server/Except.h>
#include <tango/server/tango_clock.h>
#include <tango/server/Util.h>

#include <zmq.hpp>

#include <string>
#include <vector>

namespace Tango
{

//---------------------------------------------------------------------
//
//              EventSupplier base class
//
//---------------------------------------------------------------------

class EventSupplier
{
  public:
    EventSupplier(Util *);

    virtual ~EventSupplier() { }

    void push_att_data_ready_event(DeviceImpl *, const std::string &, long, DevLong);
    void push_dev_intr_change_event(DeviceImpl *, bool, DevCmdInfoList_2 *, AttributeConfigList_5 *);
    bool any_dev_intr_client(const DeviceImpl *) const;

    struct SuppliedEventData
    {
        const AttributeValue *attr_val;
        const AttributeValue_3 *attr_val_3;
        const AttributeValue_4 *attr_val_4;
        const AttributeValue_5 *attr_val_5;
        const AttributeConfig_2 *attr_conf_2;
        const AttributeConfig_3 *attr_conf_3;
        const AttributeConfig_5 *attr_conf_5;
        const AttDataReady *attr_dat_ready;
        const DevIntrChange *dev_intr_change;
        DevPipeData *pipe_val;
        zmq::message_t *zmq_mess;
    };

    SendEventType detect_and_push_events(
        DeviceImpl *, struct SuppliedEventData &, DevFailed *, std::string &, PollClock::time_point);

    //------------------ Change event ---------------------------

    bool detect_change(
        Attribute &, struct SuppliedEventData &, bool, double &, double &, DevFailed *, bool &, DeviceImpl *);

    //------------------ Detect, push change event --------------

    bool detect_and_push_change_event(
        DeviceImpl *, struct SuppliedEventData &, Attribute &, std::string &, DevFailed *, bool user_push = false);

    //------------------ Detect, push alarm event --------------

    bool detect_and_push_alarm_event(
        DeviceImpl *, struct SuppliedEventData &, Attribute &, std::string &, DevFailed *, bool user_push = false);

    //------------------ Detect, push archive event --------------

    bool detect_and_push_archive_event(DeviceImpl *,
                                       struct SuppliedEventData &,
                                       Attribute &,
                                       std::string &,
                                       DevFailed *,
                                       PollClock::time_point,
                                       bool user_push = false);

    //------------------ Detect, push periodic event -------------

    bool detect_and_push_periodic_event(
        DeviceImpl *, struct SuppliedEventData &, Attribute &, std::string &, DevFailed *, PollClock::time_point);

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

    virtual 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) = 0;
    virtual void push_heartbeat_event() = 0;

    //------------------- Attribute conf change event ---------------------

    void push_att_conf_events(DeviceImpl *device_impl, const SuppliedEventData &, DevFailed *, const std::string &);

    void push_att_val_events(DeviceImpl *,
                             EventType,
                             const std::vector<std::string> &,
                             const std::vector<double> &,
                             const std::vector<std::string> &,
                             const std::vector<long> &,
                             const struct SuppliedEventData &,
                             Attribute &,
                             DevFailed *);

    omni_mutex &get_push_mutex()
    {
        return push_mutex;
    }

    omni_condition &get_push_cond()
    {
        return push_cond;
    }

    static omni_mutex &get_event_mutex()
    {
        return event_mutex;
    }

    std::string &get_fqdn_prefix()
    {
        return fqdn_prefix;
    }

    void convert_att_event_to_5(const struct SuppliedEventData &, struct SuppliedEventData &, bool &, Attribute &);
    void convert_att_event_to_4(const struct SuppliedEventData &, struct SuppliedEventData &, bool &, Attribute &);
    void convert_att_event_to_3(const struct SuppliedEventData &, struct SuppliedEventData &, bool &, Attribute &);

    bool get_one_subscription_cmd()
    {
        return one_subscription_cmd;
    }

    void set_one_subscription_cmd(bool val)
    {
        one_subscription_cmd = val;
    }

  protected:
    static std::string fqdn_prefix;

    // Added a mutex to synchronize the access to
    //    detect_and_push_change_event    and
    // detect_and_push_archive_event which are used
    // from different threads
    static omni_mutex event_mutex;

    // Added a semaphore to synchronize the access to
    //    push_event which is used
    // from different threads. Do not use a simple mutex because in some cases
    // it is taken by Tango threads and released by ZMQ threads
    static omni_mutex push_mutex;
    static omni_condition push_cond;

  private:
    bool one_subscription_cmd{false};
};

} // namespace Tango

#endif
