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

#ifndef TANGO_SERVER_ROOTATTREGISTRY_H
#define TANGO_SERVER_ROOTATTREGISTRY_H

#include <tango/client/CallBack.h>
#include <tango/common/tango_const.h>
#include <tango/server/ReadersWritersLock.h>

#include <map>
#include <string>

namespace Tango
{
class FwdAttr;
class DeviceProxy;

struct NameFwdAttr
{
    std::string local_name;     // Local device name
    std::string local_att_name; // Local attribute name
    std::string local_label;    // Local attribute label
    FwdAttr *fwd_attr;
    FwdAttr *fwd_attr_cl;
};

struct UserEvent
{
    EventType event_type; // Event type
    int event_id;         //
};

class RootAttRegistry
{
  public:
    RootAttRegistry() :
        cbp(this),
        cbu(this)
    {
    }

    ~RootAttRegistry();

    void add_root_att(
        const std::string &, const std::string &, const std::string &, const std::string &, FwdAttr *, DeviceImpl *);
    void remove_root_att(const std::string &, const std::string &);
    DeviceProxy *get_root_att_dp(const std::string &);

    DeviceImpl *get_local_dev(const std::string &_s)
    {
        return cbp.get_local_dev(_s);
    }

    std::string get_local_att_name(const std::string &_s)
    {
        return cbp.get_local_att_name(_s);
    }

    void update_label(const std::string &_d, const std::string &_a, const std::string &_l)
    {
        std::string s(_d + '/' + _a);
        cbp.update_label(s, _l);
    }

    void update_device_impl(const std::string &_n, DeviceImpl *_d)
    {
        cbp.update_device_impl(_n, _d);
    }

    void clear_attrdesc(const std::string &, const std::string &);
    bool check_root_dev_release(const std::string &);

    bool is_event_subscribed(const std::string &, EventType);
    void subscribe_user_event(const std::string &, const std::string &, EventType);
    void unsubscribe_user_event(const std::string &, const std::string &, EventType);
    void auto_unsub();

    bool is_root_attribute(const std::string &_s)
    {
        return cbp.is_root_att_in_map(_s);
    }

    bool empty()
    {
        return dps.empty();
    }

    bool is_root_dev_not_started_err()
    {
        return cbp.is_root_dev_not_started_err();
    }

  protected:
    bool check_loop(const std::string &, const std::string &, const std::string &, const std::string &);

  private:
    class RootAttConfCallBack : public Tango::CallBack
    {
      public:
        RootAttConfCallBack(RootAttRegistry *_r) :
            Tango::CallBack(),
            rar(_r)
        {
            ci.cpp_clnt(0);
        }

        ~RootAttConfCallBack() override;

        void push_event(Tango::AttrConfEventData *) override;

        void add_att(const std::string &, const std::string &, const std::string &, FwdAttr *, DeviceImpl *);
        void remove_att(const std::string &);
        void clear_attrdesc(const std::string &);
        bool is_root_att_in_map(const std::string &);
        int count_root_dev(const std::string &);
        std::string get_local_att_name(const std::string &);
        DeviceImpl *get_local_dev(const std::string &);
        void update_label(const std::string &, const std::string &);
        void update_device_impl(const std::string &, DeviceImpl *);
        void update_err_kind(const std::string &, FwdAttError);
        void device_restarting(const std::string &);
        bool is_root_dev_not_started_err();

      private:
        ClntIdent ci;
        omni_mutex the_lock;
        std::map<std::string, struct NameFwdAttr> map_attrdesc; // Key is root attribute device_name/att_name
        std::map<std::string, DeviceImpl *> local_dis;          // Key is local device name
        RootAttRegistry *rar;
    };

    class RootAttUserCallBack : public Tango::CallBack
    {
      public:
        RootAttUserCallBack(RootAttRegistry *_r) :
            Tango::CallBack(),
            rar(_r)
        {
        }

        void push_event(Tango::EventData *) override;
        void push_event(Tango::DataReadyEventData *) override;

      private:
        RootAttRegistry *rar;

        std::vector<std::string> dummy_vs;
        std::vector<double> dummy_vd;
        std::vector<long> dummy_vl;
    };

    std::map<std::string, DeviceProxy *> dps;                        // Key is root attribute device name
    std::map<std::string, int> map_event_id;                         // Key is root attribute device_name/att_name
    std::map<std::string, std::vector<UserEvent>> map_event_id_user; // Key is root attribute device name/att_name
    ReadersWritersLock id_user_lock;

    RootAttConfCallBack cbp;
    RootAttUserCallBack cbu;
};

} // namespace Tango

#endif /* TANGO_SERVER_ROOTATTREGISTRY_H */
