/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwaylanddataoffer_p.h"
#include "qwaylanddatadevicemanager_p.h"
#include "qwaylanddisplay_p.h"

#include <QtCore/private/qcore_unix_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformclipboard.h>

#include <QtCore/QDebug>

QT_BEGIN_NAMESPACE

namespace QtWaylandClient {

static QString utf8Text()
{
    return QStringLiteral("text/plain;charset=utf-8");
}

static QString portalFileTransfer()
{
    return QStringLiteral("application/vnd.portal.filetransfer");
}

QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
    : QtWayland::wl_data_offer(offer)
    , m_display(display)
    , m_mimeData(new QWaylandMimeData(this))
{
}

QWaylandDataOffer::~QWaylandDataOffer()
{
    destroy();
}


QString QWaylandDataOffer::firstFormat() const
{
    if (m_mimeData->formats().isEmpty())
        return QString();

    return m_mimeData->formats().first();
}

QMimeData *QWaylandDataOffer::mimeData()
{
    return m_mimeData.data();
}

Qt::DropActions QWaylandDataOffer::supportedActions() const
{
    if (wl_data_offer_get_version(const_cast<::wl_data_offer*>(object())) < 3) {
        return Qt::MoveAction | Qt::CopyAction;
    }

    return m_supportedActions;
}

void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd)
{
    receive(mimeType, fd);
    wl_display_flush(m_display->wl_display());
}

void QWaylandDataOffer::data_offer_offer(const QString &mime_type)
{
    m_mimeData->appendFormat(mime_type);
}

void QWaylandDataOffer::data_offer_action(uint32_t dnd_action)
{
    Q_UNUSED(dnd_action);
    // This is the compositor telling the drag target what action it should perform
    // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action?
}

void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions)
{
    m_supportedActions = Qt::DropActions();
    if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
        m_supportedActions |= Qt::MoveAction;
    if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
        m_supportedActions |= Qt::CopyAction;
}

QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
    : m_dataOffer(dataOffer)
{
}

QWaylandMimeData::~QWaylandMimeData()
{
}

void QWaylandMimeData::appendFormat(const QString &mimeType)
{
    m_types << mimeType;
    m_data.remove(mimeType); // Clear previous contents
}

bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const
{
    if (m_types.contains(mimeType))
        return true;

    if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text()))
        return true;

    return false;
}

QStringList QWaylandMimeData::formats_sys() const
{
    return m_types;
}

QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const
{
    Q_UNUSED(type);

    if (m_data.contains(mimeType))
        return m_data.value(mimeType);

    QString mime = mimeType;

    if (!m_types.contains(mimeType)) {
        if (mimeType == QStringLiteral("text/plain") && m_types.contains(utf8Text()))
            mime = utf8Text();
        else
            return QVariant();
    }

    int pipefd[2];
    if (qt_safe_pipe(pipefd) == -1) {
        qWarning("QWaylandMimeData: pipe2() failed");
        return QVariant();
    }

    m_dataOffer->startReceiving(mime, pipefd[1]);

    close(pipefd[1]);

    QByteArray content;
    if (readData(pipefd[0], content) != 0) {
        qWarning("QWaylandDataOffer: error reading data for mimeType %s", qPrintable(mimeType));
        content = QByteArray();
    }

    close(pipefd[0]);
    if (mimeType != portalFileTransfer())
        m_data.insert(mimeType, content);

    return content;
}

int QWaylandMimeData::readData(int fd, QByteArray &data) const
{
    struct pollfd readset;
    readset.fd = fd;
    readset.events = POLLIN;
    struct timespec timeout;
    timeout.tv_sec = 1;
    timeout.tv_nsec = 0;


    Q_FOREVER {
        int ready = qt_safe_poll(&readset, 1, &timeout);
        if (ready < 0) {
            qWarning() << "QWaylandDataOffer: qt_safe_poll() failed";
            return -1;
        } else if (ready == 0) {
            qWarning("QWaylandDataOffer: timeout reading from pipe");
            return -1;
        } else {
            char buf[4096];
            int n = QT_READ(fd, buf, sizeof buf);

            if (n < 0) {
                qWarning("QWaylandDataOffer: read() failed");
                return -1;
            } else if (n == 0) {
                return 0;
            } else if (n > 0) {
                data.append(buf, n);
            }
        }
    }
}

}

QT_END_NAMESPACE
