/**
 * Copyright (c) 2022
 *    Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 *    Abrar (https://gitlab.com/s96abrar)
 *    rahmanshaber (https://gitlab.com/rahmanshaber)
 *
 * DFL::Application is a thin wrapper around QApplication
 * to provide a single instance of an application.
 **/

#pragma once

#include <QApplication>

#if defined(qApp)
#undef qApp
#endif

#define qApp    (static_cast<DFL::Application *>(QCoreApplication::instance() ) )

class QWidget;

namespace DFL {
    class Application;
    namespace IPC {
        class Server;
        class Client;
    }
}

class ApplicationImpl;

class DFL::Application : public QApplication {
    Q_OBJECT;

    public:
        Application( int& argc, char **argv );
        ~Application();

        /**
         * Application Name
         * This value is transparently passed on to QCoreApplication.
         * Name is used to create a unique socket path:
         * $XDG_RUNTIME_DIR/ORG_NAME/APP_NAME.socket
         */
        void setApplicationName( QString );

        /**
         * Organization Name
         * This value is transparently passed on to QCoreApplication.
         * Name is used to create a unique socket path:
         * $XDG_RUNTIME_DIR/ORG_NAME/APP_NAME-APP_VER.socket
         */
        void setOrganizationName( QString );

        /**
         * Lock this instance of the application.
         * When this function is called, it will first search is another
         * instance of this app is running. If found, it will return false.
         * Othserwise, it will start the ipc server and return true.
         * NOTE: Application and Origanization name need to be set before
         * calling this function. Otherwise, locking will not be performed.
         */
        bool lockApplication();

        /**
         * Check if another instance of this application is running.
         */
        bool isRunning();

        /**
         * Handle signals - SIGINT/SIGTERM/SIGQUIT/SIGABRT/SIGSEGV
         * When a signal is auto handled, it simply prints a log message
         * and aborts: std::_Exit(). See autoHandleSignal(...)
         * Otherwise, it will emit a signal, and wait for the app to take
         * charge. See retransmitSignal(...)
         * If this function is not called for a signal, then that particular
         * signal will not be handled at all.
         */
        void interceptSignal( int signum, bool autoHandle );

        /** Send message to the server */
        bool messageServer( const QString& message );

        /** Send a message to the client */
        bool messageClient( const QString& message, int fd );

        /** Send a message to the all the connected clients */
        void broadcast( const QString& message );

        /** Disconnect from the server */
        void disconnect();

        /**
         * Automatically handle the signal.
         * SIGINT/TERM/QUIT are nice requests. Exit nicely with std::exit(...)
         * Print backtrace if we encounter a SIGSEGV. Then exit with std::_Exit(...)
         * SIGABRT: Try printing backtrace. Then exit with std::_Exit(...)
         */
        static void autoHandleSignal( int signum );

        /**
         * Automatically handle the signal.
         * SIGINT/TERM/QUIT are nice requests. We simply inform the app, and
         * wait for the app to take suitable action
         */
        static void retransmitSignal( int signum );

    Q_SIGNALS:
        /** Message recieved from the server */
        void messageFromServer( const QString& message );

        /** Message received from a client with id @clientID */
        void messageFromClient( const QString& message, int clientID );

        /** We received a SIGINT */
        void interrupted();

        /** We received a SIGTERM/SIGQUIT. */
        void terminate();

    private:
        ApplicationImpl *impl;
};
