// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#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");
}

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 (version() < 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, QMetaType type) const
{
    Q_UNUSED(type);

    auto it = m_data.constFind(mimeType);
    if (it != m_data.constEnd())
        return *it;

    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]);
    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
