// 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 <Qt3DRender/qrendercapture.h>
#include <Qt3DRender/private/qrendercapture_p.h>

#include <QPointer>
#include <QMutexLocker>

QT_BEGIN_NAMESPACE

namespace Qt3DRender {

/*!
 * \class Qt3DRender::QRenderCapture
 * \inheaderfile Qt3DRender/QRenderCapture
 * \inmodule Qt3DRender
 *
 * \brief Frame graph node for render capture.
 *
 * The QRenderCapture is used to capture rendering into an image at any render stage.
 * Capturing must be initiated by the user and one image is returned per capture request.
 * User can issue multiple render capture requests simultaniously, but only one request
 * is served per QRenderCapture instance per frame.
 *
 * \since 5.8
 */

/*!
 * \qmltype RenderCapture
 * \instantiates Qt3DRender::QRenderCapture
 * \inherits FrameGraphNode
 * \inqmlmodule Qt3D.Render
 * \since 5.8
 * \brief Capture rendering.
 */

/*!
 * \class Qt3DRender::QRenderCaptureReply
 * \inheaderfile Qt3DRender/QRenderCaptureReply
 * \inmodule Qt3DRender
 *
 * \brief Receives the result of render capture request.
 *
 * An object, which receives the image from QRenderCapture::requestCapture.
 *
 * \since 5.8
 */

/*!
 * \qmltype RenderCaptureReply
 * \instantiates Qt3DRender::QRenderCaptureReply
 * \inherits QObject
 * \inqmlmodule Qt3D.Render
 * \since 5.8
 * \brief Receives render capture result.
 */

/*!
 * \qmlproperty variant Qt3D.Render::RenderCaptureReply::image
 *
 * Holds the image, which was produced as a result of render capture.
 */

/*!
 * \qmlproperty int Qt3D.Render::RenderCaptureReply::captureId
 *
 * Holds the captureId, which was passed to the renderCapture.
 */

/*!
 * \qmlproperty bool Qt3D.Render::RenderCaptureReply::complete
 *
 * Holds the complete state of the render capture.
 */

/*!
 * \qmlmethod bool Qt3D.Render::RenderCaptureReply::saveImage(fileName)
 *
 * Saves the render capture result as an image to \a fileName.
 * Returns true if the image was successfully saved; otherwise returns false.
 *
 * \since 5.9
 */

/*!
 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture(int captureId)
 * \deprecated
 *
 * Used to request render capture. User can specify a \a captureId to identify
 * the request. The requestId does not have to be unique. Only one render capture result
 * is produced per requestCapture call even if the frame graph has multiple leaf nodes.
 * The function returns a QRenderCaptureReply object, which receives the captured image
 * when it is done. The user is responsible for deallocating the returned object.
 */

/*!
 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture()
 *
 * Used to request render capture. Only one render capture result is produced per
 * requestCapture call even if the frame graph has multiple leaf nodes.
 * The function returns a QRenderCaptureReply object, which receives the captured image
 * when it is done. The user is responsible for deallocating the returned object.
 */

/*!
 * \qmlmethod RenderCaptureReply Qt3D.Render::RenderCapture::requestCapture(Rect rect)
 *
 * Used to request render capture from a specified \a rect. Only one render capture
 * result is produced per requestCapture call even if the frame graph has multiple leaf nodes.
 * The function returns a QRenderCaptureReply object, which receives the captured image
 * when it is done. The user is responsible for deallocating the returned object.
 */

/*!
 * \internal
 */
QRenderCaptureReplyPrivate::QRenderCaptureReplyPrivate()
    : QObjectPrivate()
    , m_captureId(0)
    , m_complete(false)
{

}

/*!
 * The constructor creates an instance with the specified \a parent.
 */
QRenderCaptureReply::QRenderCaptureReply(QObject *parent)
    : QObject(* new QRenderCaptureReplyPrivate, parent)
{

}

/*!
 * \property QRenderCaptureReply::image
 *
 * Holds the image, which was produced as a result of render capture.
 */
QImage QRenderCaptureReply::image() const
{
    Q_D(const QRenderCaptureReply);
    return d->m_image;
}

/*!
 * \property QRenderCaptureReply::captureId
 *
 * Holds the captureId, which was passed to the renderCapture.
 */
int QRenderCaptureReply::captureId() const
{
    Q_D(const QRenderCaptureReply);
    return d->m_captureId;
}

/*!
 * \property QRenderCaptureReply::complete
 *
 * Holds the complete state of the render capture.
 */
bool QRenderCaptureReply::isComplete() const
{
    Q_D(const QRenderCaptureReply);
    return d->m_complete;
}

/*!
 * Saves the render capture result as an image to \a fileName.
 *
 * Returns true if the image was successfully saved; otherwise returns false.
 * \since 5.9
 */
bool QRenderCaptureReply::saveImage(const QString &fileName) const
{
    Q_D(const QRenderCaptureReply);
    if (d->m_complete)
    {
        return d->m_image.save(fileName);
    }
    return false;
}

/*!
 * \internal
 */
QRenderCapturePrivate::QRenderCapturePrivate()
    : QFrameGraphNodePrivate()
{
}

/*!
 * \internal
 */
QRenderCapturePrivate::~QRenderCapturePrivate()
{
}

/*!
 * \internal
 */
QRenderCaptureReply *QRenderCapturePrivate::createReply(int captureId)
{
    QMutexLocker lock(&m_mutex);
    QRenderCaptureReply *reply = new QRenderCaptureReply();
    reply->d_func()->m_captureId = captureId;
    m_waitingReplies.push_back(reply);
    return reply;
}

/*!
 * \internal
 */
QRenderCaptureReply *QRenderCapturePrivate::takeReply(int captureId)
{
    QRenderCaptureReply *reply = nullptr;
    QMutexLocker lock(&m_mutex);
    for (int i = 0; i < m_waitingReplies.size(); ++i) {
        if (m_waitingReplies[i]->d_func()->m_captureId == captureId) {
            reply = m_waitingReplies.takeAt(i);
            break;
        }
    }
    return reply;
}

/*!
 * \internal
 */
void QRenderCapturePrivate::setImage(QRenderCaptureReply *reply, const QImage &image)
{
    reply->d_func()->m_complete = true;
    reply->d_func()->m_image = image;
}

/*!
 * \internal
 */
void QRenderCapturePrivate::replyDestroyed(QRenderCaptureReply *reply)
{
    QMutexLocker lock(&m_mutex);
    m_waitingReplies.removeAll(reply);
}

/*!
 * The constructor creates an instance with the specified \a parent.
 */
QRenderCapture::QRenderCapture(Qt3DCore::QNode *parent)
    : QFrameGraphNode(*new QRenderCapturePrivate, parent)
{
}

/*!
 * \deprecated Used to request render capture. User can specify a \a captureId
 * to identify the request. The requestId does not have to be unique. Only one
 * render capture result is produced per requestCapture call even if the frame
 * graph has multiple leaf nodes. The function returns a QRenderCaptureReply
 * object, which receives the captured image when it is done. The user is
 * responsible for deallocating the returned object by calling deleteLater().
 */
QRenderCaptureReply *QRenderCapture::requestCapture(int captureId)
{
    Q_D(QRenderCapture);
    QRenderCaptureReply *reply = d->createReply(captureId);
    reply->setParent(this);
    QObject::connect(reply, &QObject::destroyed, this, [&, reply, d] (QObject *) {
        d->replyDestroyed(reply);
    });

    const QRenderCaptureRequest request = { captureId, QRect() };
    d->m_pendingRequests.push_back(request);
    d->update();

    return reply;
}

/*!
 * Used to request render capture from a specified \a rect. Only one render
 * capture result is produced per requestCapture call even if the frame graph
 * has multiple leaf nodes. The function returns a QRenderCaptureReply object,
 * which receives the captured image when it is done. The user is responsible
 * for deallocating the returned object by calling deleteLater().
 */
QRenderCaptureReply *QRenderCapture::requestCapture(const QRect &rect)
{
    Q_D(QRenderCapture);
    static int captureId = 1;
    QRenderCaptureReply *reply = d->createReply(captureId);
    reply->setParent(this);
    QObject::connect(reply, &QObject::destroyed, this, [&, reply, d] (QObject *) {
        d->replyDestroyed(reply);
    });

    const QRenderCaptureRequest request = { captureId, rect };
    d->m_pendingRequests.push_back(request);
    d->update();

    captureId++;

    return reply;
}

/*!
 * Used to request render capture. Only one render capture result is produced
 * per requestCapture call even if the frame graph has multiple leaf nodes. The
 * function returns a QRenderCaptureReply object, which receives the captured
 * image when it is done. The user is responsible for deallocating the returned
 * object by calling deleterLater().
 */
Qt3DRender::QRenderCaptureReply *QRenderCapture::requestCapture()
{
    return requestCapture(QRect());
}

} // Qt3DRender

QT_END_NAMESPACE

#include "moc_qrendercapture.cpp"
