/**
 * Copyright (c) 2022
 *    Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 *
 * This file was originally taken from LXQt project
 * <https://github.com/lxqt/lxqt-panel>
 * Several modifications have been made to suit the needs of DFL.
 * Any and all bug reports are to be filed with DFL and not LXQt.
 *
 * This library implements the StatusNotifierInterface specifications.
 * https://www.freedesktop.org/wiki/Specifications/StatusNotifierInterface/
 * DFL::StatusNotifierWatcher class provides the code for the watcher.
 *
 * Impl Class: Performs all DBus Actions Asynchronously.
 **/

#pragma once

#include <functional>
#include <QObject>
#include <QString>
#include <QtDBus>

class StatusNotifierInterface;

template<typename> struct remove_class_type { using type = void; };

template<typename C, typename R, typename ... ArgTypes> struct  remove_class_type<R (C::*) ( ArgTypes... )> { using type = R( ArgTypes... ); };

template<typename C, typename R, typename ... ArgTypes> struct remove_class_type<R (C::*) ( ArgTypes... ) const> { using type = R( ArgTypes... ); };

template<typename L> class call_sig_helper {
    template<typename L1>
    static decltype(&L1::operator()) test( int );

    template<typename L1>
    static void test( ... );   //bluff

    public:
        using type = decltype(test<L>( 0 ) );
};

template<typename L> struct call_signature : public remove_class_type<typename call_sig_helper<L>::type> {};

template<typename R, typename ... ArgTypes> struct call_signature<R (ArgTypes...)> { using type = R( ArgTypes... ); };

template<typename R, typename ... ArgTypes> struct call_signature<R (*) ( ArgTypes... )> { using type = R( ArgTypes... ); };

template<typename C, typename R, typename ... ArgTypes> struct call_signature<R (C::*) ( ArgTypes... )> { using type = R( ArgTypes... ); };

template<typename C, typename R, typename ... ArgTypes> struct call_signature<R (C::*) ( ArgTypes... ) const>  { using type = R( ArgTypes... ); };

template<typename> struct is_valid_signature : public std::false_type {};

template<typename Arg> struct is_valid_signature<void (Arg)> : public std::true_type {};

namespace DFL {
    class StatusNotifierItem;
}

class DFL::StatusNotifierItem : public QObject {
    Q_OBJECT;

    public:
        StatusNotifierItem( QString, QString, QDBusConnection, QObject *parent = nullptr );
        ~StatusNotifierItem();

        template<typename F>
        void propertyGetAsync( QString const& name, F finished ) {
            static_assert(
                is_valid_signature<typename call_signature<F>::type>::value, "need callable ( lambda, *function, callable obj ) ( Arg ) -> void"
            );

            connect(
                new QDBusPendingCallWatcher { asyncPropGet( name ), this },
                &QDBusPendingCallWatcher::finished,
                [ this, finished, name ]( QDBusPendingCallWatcher *call ) {
                    QDBusPendingReply<QVariant> reply = *call;

                    if ( reply.isError() ) {
                        qWarning() << "Error on DBus request:" << reply.error();
                    }

                    finished( qdbus_cast<typename std::function<typename call_signature<F>::type>::argument_type>( reply.value() ) );
                    call->deleteLater();
                }
            );
        }

        // exposed methods from StatusNotifierInterface
        QString service() const;

    public slots:
        // Forwarded slots from StatusNotifierInterface
        QDBusPendingReply<> Activate( int x, int y );
        QDBusPendingReply<> ContextMenu( int x, int y );
        QDBusPendingReply<> Scroll( int delta, const QString& orientation );
        QDBusPendingReply<> SecondaryActivate( int x, int y );

    signals:
        // Forwarded signals from StatusNotifierInterface
        void NewAttentionIcon();
        void NewIcon();
        void NewOverlayIcon();
        void NewStatus( const QString& status );
        void NewTitle();
        void NewToolTip();

    private:
        QDBusPendingReply<QDBusVariant> asyncPropGet( QString const& property );

    private:
        StatusNotifierInterface *mSni;
};
