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

#pragma once

#include <QGuiApplication>

#if defined( qApp )
    #undef qApp
#endif

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

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

    namespace Impl {
        class Application;
    }
}

class DFL::GuiApplication : public QGuiApplication {
    Q_OBJECT;

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

        /**
         * 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();

        [[deprecated("Use interceptSignal( int signum, std::function<void(int)> func = nullptr ) instead.")]]
        void interceptSignal( int signum, bool autoHandle );

        /**
         * Handle signals - SIGINT/SIGTERM/SIGQUIT/SIGABRT/SIGSEGV suitably.
         * In case of SIGINT, and SIGQUIT, a traceback is printed and
         * std::exit() is called to clean up the resources.
         * When SIGTERM is recieved, terminate() signal is emitted.
         * SIGABRT and SIGSEGV will both result in printing of a traceback
         * and calling of _Exit() -> No cleaning up of resources will be done.
         * If @func is not a nullptr, the function will be called before
         * std::exit() or _Exit().
         */
        void interceptSignal( int signum, std::function<void(int)> func = nullptr );

        /** 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();

    Q_SIGNALS:
        /** Message received 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();

        /** We received a SIGNAL */
        void unixSignalReceived( int signum );

    private:
        QScopedPointer<DFL::Impl::Application> impl;
};
