/*
    SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
    SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "datasource.h"
#include "clientconnection.h"
#include "datadevicemanager.h"
#include "datasource_p.h"
#include "utils/resource.h"
// Qt
#include <QStringList>
// Wayland
#include <qwayland-server-wayland.h>
// system
#include <unistd.h>

namespace KWin
{
DataSourceInterfacePrivate::DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource)
    : QtWaylandServer::wl_data_source(resource)
    , q(_q)
{
}

void DataSourceInterfacePrivate::data_source_destroy_resource(Resource *resource)
{
    Q_EMIT q->aboutToBeDestroyed();
    delete q;
}

void DataSourceInterfacePrivate::data_source_offer(QtWaylandServer::wl_data_source::Resource *resource, const QString &mime_type)
{
    mimeTypes << mime_type;
    Q_EMIT q->mimeTypeOffered(mime_type);
}

void DataSourceInterfacePrivate::data_source_destroy(QtWaylandServer::wl_data_source::Resource *resource)
{
    wl_resource_destroy(resource->handle);
}

void DataSourceInterfacePrivate::offer(const QString &mimeType)
{
    mimeTypes << mimeType;
    Q_EMIT q->mimeTypeOffered(mimeType);
}

void DataSourceInterfacePrivate::data_source_set_actions(Resource *resource, uint32_t dnd_actions)
{
    // verify that the no other actions are sent
    if (dnd_actions
        & ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move
            | QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
        wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
        return;
    }
    DataDeviceManagerInterface::DnDActions supportedActions;
    if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
        supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
    }
    if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
        supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
    }
    if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
        supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
    }
    if (supportedDnDActions != supportedActions) {
        supportedDnDActions = supportedActions;
        Q_EMIT q->supportedDragAndDropActionsChanged();
    }
}

DataSourceInterfacePrivate *DataSourceInterfacePrivate::get(DataSourceInterface *dataSource)
{
    return dataSource->d.get();
}

DataSourceInterface::DataSourceInterface(wl_resource *resource)
    : d(new DataSourceInterfacePrivate(this, resource))
{
    if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
        d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy;
    }
}

DataSourceInterface::~DataSourceInterface() = default;

void DataSourceInterface::accept(const QString &mimeType)
{
    d->send_target(mimeType);
    d->isAccepted = !mimeType.isNull();
}

void DataSourceInterface::requestData(const QString &mimeType, qint32 fd)
{
    d->send_send(mimeType, int32_t(fd));
    close(fd);
}

void DataSourceInterface::cancel()
{
    d->send_cancelled();
}

QStringList DataSourceInterface::mimeTypes() const
{
    return d->mimeTypes;
}

DataSourceInterface *DataSourceInterface::get(wl_resource *native)
{
    if (auto sourcePrivate = resource_cast<DataSourceInterfacePrivate *>(native)) {
        return sourcePrivate->q;
    }
    return nullptr;
}

DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const
{
    return d->supportedDnDActions;
}

DataDeviceManagerInterface::DnDAction DataSourceInterface::selectedDndAction() const
{
    return d->selectedDndAction;
}

void DataSourceInterface::dropPerformed()
{
    d->dropPerformed = true;
    if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
        return;
    }
    d->send_dnd_drop_performed();
}

void DataSourceInterface::dndFinished()
{
    if (d->resource()->version() < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
        return;
    }
    d->send_dnd_finished();
}

bool DataSourceInterface::isDropPerformed() const
{
    return d->dropPerformed;
}

void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
{
    d->selectedDndAction = action;

    if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
        return;
    }
    uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
    if (action == DataDeviceManagerInterface::DnDAction::Copy) {
        wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
    } else if (action == DataDeviceManagerInterface::DnDAction::Move) {
        wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
    } else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
        wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
    }
    d->send_action(wlAction);
}

void DataSourceInterface::dndCancelled()
{
    d->isCanceled = true;
    // for v3 or less, cancel should not be called after a failed drag operation
    if (wl_resource_get_version(resource()) < 3) {
        return;
    }
    d->send_cancelled();
}

bool DataSourceInterface::isDndCancelled() const
{
    return d->isCanceled;
}

wl_resource *DataSourceInterface::resource() const
{
    return d->resource()->handle;
}

wl_client *DataSourceInterface::client() const
{
    return d->resource()->client();
}

bool DataSourceInterface::isAccepted() const
{
    return d->isAccepted;
}

void DataSourceInterface::setAccepted(bool accepted)
{
    d->isAccepted = accepted;
}

XdgToplevelDragV1Interface *DataSourceInterface::xdgToplevelDrag() const
{
    return d->xdgToplevelDrag;
}

}

#include "moc_datasource.cpp"
