// SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: GPL-1.0-or-later

#include "qwaylanddisplay_p.h"
#include "qwaylandscreen_p.h"
#include "qwaylandscreencopy_p.h"
#include "qwaylandshm_p.h"

#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

#include <QEventLoop>

QT_BEGIN_NAMESPACE

namespace QtWaylandClient
{

QWaylandScreenCopyManagerV1::QWaylandScreenCopyManagerV1(QWaylandDisplay *display, uint id, uint version)
    : QtWayland::zwlr_screencopy_manager_v1(display->wl_registry(), id, version)
    , mDisplay(display)
{
}

QPixmap QWaylandScreenCopyManagerV1::captureScreen(wl_output *output, bool hasCursor)
{
    QWaylandScreenCopyFrameV1 frame(mDisplay, capture_output(hasCursor ? 1 : 0, output));
    QEventLoop loop;
    QPixmap pixmap;
    connect(&frame, &QWaylandScreenCopyFrameV1::ready, &loop, [&frame, &pixmap, &loop]() {
        pixmap = frame.pixmap();
        loop.quit();
    });
    connect(&frame, &QWaylandScreenCopyFrameV1::failed, &loop, &QEventLoop::quit);

    loop.exec();

    return pixmap;
}

QPixmap QWaylandScreenCopyManagerV1::captureScreenRegion(wl_output *output, int32_t x, int32_t y, int32_t width, int32_t height, bool hasCursor)
{
    QWaylandScreenCopyFrameV1 frame(mDisplay, capture_output_region(hasCursor ? 1 : 0, output, x, y, width, height));
    QEventLoop loop;
    QPixmap pixmap;
    connect(&frame, &QWaylandScreenCopyFrameV1::ready, &loop, [&frame, &pixmap, &loop]() {
        pixmap = frame.pixmap();
        loop.quit();
    });
    connect(&frame, &QWaylandScreenCopyFrameV1::failed, &loop, &QEventLoop::quit);

    loop.exec();

    return pixmap;
}

QWaylandScreenCopyFrameV1::QWaylandScreenCopyFrameV1(QWaylandDisplay *display, struct ::zwlr_screencopy_frame_v1 *object)
    : QtWayland::zwlr_screencopy_frame_v1(object)
    , mDisplay(display)
{
}

QWaylandScreenCopyFrameV1::~QWaylandScreenCopyFrameV1()
{
    destroy();
    if (mBuffer) {
        wl_buffer_destroy(mBuffer);
        mBuffer = nullptr;
    }
    if (mBufferData) {
        munmap(mBufferData, mBufferSize);
        mBufferData = nullptr;
    }
}

QPixmap QWaylandScreenCopyFrameV1::pixmap()
{
    if (!mBufferReady)
        return QPixmap();

    mImage = QImage(mBufferData, mBufferDimensions.width(), mBufferDimensions.height(), mBufferDimensions.width() * 4, QImage::Format_ARGB32_Premultiplied);
    return QPixmap::fromImage(mImage.copy());
}

void QWaylandScreenCopyFrameV1::zwlr_screencopy_frame_v1_ready(uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec)
{
    mBufferReady = true;
	Q_EMIT ready();
}

void QWaylandScreenCopyFrameV1::QWaylandScreenCopyFrameV1::zwlr_screencopy_frame_v1_buffer(uint32_t format, uint32_t width, uint32_t height, uint32_t stride)
{
	if (mFormatGiven)
		return;

	mBufferDimensions = QSize(width, height);
	mBufferFormat = format;
	mBufferStride = stride;
    mBufferSize = mBufferDimensions.height() * mBufferStride;
	mFormatGiven = true;
}

void QWaylandScreenCopyFrameV1::zwlr_screencopy_frame_v1_buffer_done()
{
	if (mFormatGiven) {
		int fd = memfd_create("qtwayland_wl_shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
	    if (fd == -1) {
	    	qWarning("Failed to create memfd: %s", strerror(errno));
	    	return;
	    }

        ftruncate(fd, mBufferSize);
        mBufferData = (uchar *)mmap(nullptr, mBufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (mBufferData == MAP_FAILED) {
            qWarning("Failed to mmap memfd: %s", strerror(errno));
            return;
        }

        auto *shm_pool = mDisplay->shm()->create_pool(fd, mBufferSize);
        mBuffer = wl_shm_pool_create_buffer(shm_pool, 0, mBufferDimensions.width(), mBufferDimensions.height(), mBufferStride, mBufferFormat);

        close(fd);
        wl_shm_pool_destroy(shm_pool);

		copy(mBuffer);
	} else {
		Q_EMIT failed();
	}
}

} // namespace QtWaylandClient

QT_END_NAMESPACE
