/****************************************************************************
**
** Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwaylandclient.h"
#include <QtCore/private/qobject_p.h>

#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/private/qwaylandcompositor_p.h>


#include <wayland-server-core.h>
#include <wayland-util.h>

QT_BEGIN_NAMESPACE

class QWaylandClientPrivate : public QObjectPrivate
{
public:
    QWaylandClientPrivate(QWaylandCompositor *compositor, wl_client *_client)
        : compositor(compositor)
        , client(_client)
    {
        // Save client credentials
        wl_client_get_credentials(client, &pid, &uid, &gid);
    }

    ~QWaylandClientPrivate() override
    {
    }

    static void client_destroy_callback(wl_listener *listener, void *data)
    {
        Q_UNUSED(data);

        QWaylandClient *client = reinterpret_cast<Listener *>(listener)->parent;
        Q_ASSERT(client != nullptr);
        delete client;
    }

    QWaylandCompositor *compositor = nullptr;
    wl_client *client = nullptr;

    uid_t uid;
    gid_t gid;
    pid_t pid;

    struct Listener {
        wl_listener listener;
        QWaylandClient *parent = nullptr;
    };
    Listener listener;
};

/*!
 * \qmltype WaylandClient
 * \inqmlmodule QtWayland.Compositor
 * \since 5.8
 * \brief Represents a client connecting to the WaylandCompositor.
 *
 * This type represents a client connecting to the compositor using the Wayland protocol.
 * It corresponds to the Wayland interface wl_client.
 */

/*!
 * \class QWaylandClient
 * \inmodule QtWaylandCompositor
 * \since 5.8
 * \brief The QWaylandClient class represents a client connecting to the QWaylandCompositor.
 *
 * This class corresponds to a client connecting to the compositor using the Wayland protocol.
 * It corresponds to the Wayland interface wl_client.
 */

/*!
 * Constructs a QWaylandClient for the \a compositor and the Wayland \a client.
 */
QWaylandClient::QWaylandClient(QWaylandCompositor *compositor, wl_client *client)
    : QObject(*new QWaylandClientPrivate(compositor, client))
{
    Q_D(QWaylandClient);

    // Destroy wrapper when the client goes away
    d->listener.parent = this;
    d->listener.listener.notify = QWaylandClientPrivate::client_destroy_callback;
    wl_client_add_destroy_listener(client, &d->listener.listener);

    QWaylandCompositorPrivate::get(compositor)->addClient(this);
}

/*!
 * Destroys the QWaylandClient.
 */
QWaylandClient::~QWaylandClient()
{
    Q_D(QWaylandClient);

    // Remove listener from signal
    wl_list_remove(&d->listener.listener.link);

    QWaylandCompositorPrivate::get(d->compositor)->removeClient(this);
}

/*!
 * Returns the QWaylandClient corresponding to the Wayland client \a wlClient and \a compositor.
 * If a QWaylandClient has not already been created for a client, it is
 * created and returned.
 */
QWaylandClient *QWaylandClient::fromWlClient(QWaylandCompositor *compositor, wl_client *wlClient)
{
    if (!wlClient)
        return nullptr;

    QWaylandClient *client = nullptr;

    wl_listener *l = wl_client_get_destroy_listener(wlClient,
        QWaylandClientPrivate::client_destroy_callback);
    if (l)
        client = reinterpret_cast<QWaylandClientPrivate::Listener *>(
            wl_container_of(l, (QWaylandClientPrivate::Listener *)nullptr, listener))->parent;

    if (!client) {
        // The original idea was to create QWaylandClient instances when
        // a client bound wl_compositor, but it's legal for a client to
        // bind several times resulting in multiple QWaylandClient
        // instances for the same wl_client therefore we create it from
        // here on demand
        client = new QWaylandClient(compositor, wlClient);
    }

    return client;
}

/*!
 * \qmlproperty WaylandCompositor QtWaylandCompositor::WaylandClient::compositor
 *
 * This property holds the compositor of this WaylandClient.
 */

/*!
 * \property QWaylandClient::compositor
 *
 * This property holds the compositor of this QWaylandClient.
 */
QWaylandCompositor *QWaylandClient::compositor() const
{
    Q_D(const QWaylandClient);

    return d->compositor;
}

/*!
 * Returns the Wayland client of this QWaylandClient.
 */
wl_client *QWaylandClient::client() const
{
    Q_D(const QWaylandClient);

    return d->client;
}

/*!
 * \qmlproperty int QtWaylandCompositor::WaylandClient::userId
 *
 * This property holds the user id of this WaylandClient.
 */

/*!
 * \property QWaylandClient::userId
 * \readonly
 *
 * This property holds the user id of this QWaylandClient.
 */
qint64 QWaylandClient::userId() const
{
    Q_D(const QWaylandClient);

    return d->uid;
}

/*!
 * \qmlproperty int QtWaylandCompositor::WaylandClient::groupId
 * \readonly
 *
 * This property holds the group id of this WaylandClient.
 */

/*!
 * \property QWaylandClient::groupId
 *
 * This property holds the group id of this QWaylandClient.
 */
qint64 QWaylandClient::groupId() const
{
    Q_D(const QWaylandClient);

    return d->gid;
}

/*!
 * \qmlproperty int QtWaylandCompositor::WaylandClient::processId
 * \readonly
 *
 * This property holds the process id of this WaylandClient.
 */

/*!
 * \property QWaylandClient::processId
 *
 * This property holds the process id of this QWaylandClient.
 */
qint64 QWaylandClient::processId() const
{
    Q_D(const QWaylandClient);

    return d->pid;
}

/*!
 * \qmlmethod void QtWaylandCompositor::WaylandClient::kill(signal)
 *
 * Kills the client with the specified \a signal.
 */

/*!
 * Kills the client with the specified \a signal.
 */
void QWaylandClient::kill(int signal)
{
    Q_D(QWaylandClient);

    ::kill(d->pid, signal);
}

/*!
 * \qmlmethod void QtWaylandCompositor::WaylandClient::close()
 *
 * Closes the client
 */

/*!
 * Closes the client.
 */
void QWaylandClient::close()
{
    Q_D(QWaylandClient);
    d->compositor->destroyClient(this);
}

QT_END_NAMESPACE
