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

#ifndef TANGO_SERVER_DSERVERSIGNAL_H
#define TANGO_SERVER_DSERVERSIGNAL_H

#include <tango/common/defines.h>
#include <tango/common/omnithread_wrapper.h>
#include <tango/server/SynchronisedQueue.h>

#include <csignal>
#include <vector>

#ifdef _TG_WINDOWS_
struct sigaction
{
    void (*sa_handler)(int);
};
#endif

namespace Tango
{
class DeviceClass;
class DeviceImpl;
//=============================================================================
//
//            The DServerSignal class
//
// description :    Class to implement all data members and commands for
//            signal management in a TANGO device server.
//
//=============================================================================

#if (defined(_TG_WINDOWS_) || (defined __APPLE__) || (defined __freebsd__))
  #define _NSIG NSIG
#endif

typedef struct
{
    std::vector<DeviceClass *> registered_classes;
    std::vector<DeviceImpl *> registered_devices;
} DevSigAction;

class DServerSignal
{
  public:
    static DServerSignal *instance();
    static void cleanup_singleton();
    static void initialise_signal_names();

    ~DServerSignal();

    void initialise();

#ifndef _TG_WINDOWS_
    void register_class_signal(long, bool, DeviceClass *);
    void register_dev_signal(long, bool, DeviceImpl *);

    void register_handler(long, bool);
#else
    void register_class_signal(long, DeviceClass *);
    void register_dev_signal(long, DeviceImpl *);

    void register_handler(long);
#endif

    void unregister_class_signal(long, DeviceClass *);
    void unregister_class_signal(DeviceClass *);
    void unregister_dev_signal(long, DeviceImpl *);
    void unregister_dev_signal(DeviceImpl *);

    void unregister_handler(long);

    // NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
    class ThSig : public omni_thread
    {
        DServerSignal *ds;

      public:
        ThSig(DServerSignal *d) :
            ds(d)
        {
        }

        void *run_undetached(void *) override;

        void start()
        {
            start_undetached();
        }

      private:
        ~ThSig() override = default;
    };
    friend class ThSig;
    ThSig *sig_th;

  protected:
    DServerSignal() = default;
    static DevSigAction reg_sig[_NSIG];
    static void deliver_to_registered_handlers(int signo);

  private:
    static std::unique_ptr<DServerSignal> _instance;
    std::vector<DeviceImpl *>::iterator find_device(long, DeviceImpl *);
    std::vector<DeviceImpl *>::iterator find_delayed_device(long, DeviceImpl *);

    std::vector<DeviceClass *>::iterator find_class(long, DeviceClass *);
    std::vector<DeviceClass *>::iterator find_delayed_class(long, DeviceClass *);

    static bool auto_signal(long s)
    {
#ifdef _TG_WINDOWS_
        return (s == SIGINT) || (s == SIGTERM) || (s == SIGABRT) || (s == SIGBREAK);
#else
        return (s == SIGINT) || (s == SIGTERM) || (s == SIGQUIT) || (s == SIGHUP);
#endif
    }

    static std::string sig_name[_NSIG];
    struct sigaction enqueueing_sa;
    struct sigaction direct_sa;
    struct sigaction default_sa;

    void handle_on_signal_thread(int signo);
    void handle_directly(int signo);
    void handle_with_default(int signo);

    SynchronisedQueue<int> signal_queue;
    static constexpr int STOP_SIGNAL_THREAD = -1;
};

} // End of namespace Tango

#endif
