// Copyright (C) 2017 Ford Motor Company
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qremoteobjectpendingcall.h"
#include "qremoteobjectpendingcall_p.h"

#include "qremoteobjectreplica_p.h"

#include <QtCore/qcoreapplication.h>

#include <private/qobject_p.h>

QT_BEGIN_NAMESPACE

QT_IMPL_METATYPE_EXTERN(QRemoteObjectPendingCall)

QRemoteObjectPendingCallData::QRemoteObjectPendingCallData(int serialId, QRemoteObjectReplicaImplementation *replica)
    : replica(replica)
    , serialId(serialId)
    , error(QRemoteObjectPendingCall::InvalidMessage)
    , watcherHelper(nullptr)
{
}

QRemoteObjectPendingCallData::~QRemoteObjectPendingCallData()
{
}

void QRemoteObjectPendingCallWatcherHelper::add(QRemoteObjectPendingCallWatcher *watcher)
{
    connect(this, &QRemoteObjectPendingCallWatcherHelper::finished, watcher, [watcher]() {
        emit watcher->finished(watcher);
    }, Qt::QueuedConnection);
}

void QRemoteObjectPendingCallWatcherHelper::emitSignals()
{
    emit finished();
}

/*!
    \class QRemoteObjectPendingCall
    \inmodule QtRemoteObjects
    \brief Encapsulates the result of an asynchronous method call.
*/

QRemoteObjectPendingCall::QRemoteObjectPendingCall()
    : d(new QRemoteObjectPendingCallData)
{
}

QRemoteObjectPendingCall::~QRemoteObjectPendingCall()
{
}

QRemoteObjectPendingCall::QRemoteObjectPendingCall(const QRemoteObjectPendingCall& other)
    : d(other.d)
{
}

QRemoteObjectPendingCall::QRemoteObjectPendingCall(QRemoteObjectPendingCallData *dd)
    : d(dd)
{
}

QRemoteObjectPendingCall &QRemoteObjectPendingCall::operator=(const QRemoteObjectPendingCall &other)
{
    d = other.d;
    return *this;
}

/*!
    Returns the return value of the remote call.

    returnValue will only be valid when the remote call has finished and there
    are no \l {error}s.
*/
QVariant QRemoteObjectPendingCall::returnValue() const
{
    if (!d)
        return QVariant();

    QMutexLocker locker(&d->mutex);
    return d->returnValue;
}

/*!
    \enum QRemoteObjectPendingCall::Error

    This enum type specifies the possible error values for a remote call:

    \value NoError
           No error occurred.
    \value InvalidMessage
           The default error state prior to the remote call finishing.
*/

/*!
    Returns the error, if any, from the remote call.
*/
QRemoteObjectPendingCall::Error QRemoteObjectPendingCall::error() const
{
    if (!d)
        return QRemoteObjectPendingCall::InvalidMessage;

    QMutexLocker locker(&d->mutex);
    return d->error;
}

/*!
    Returns true if the remote call has finished, false otherwise.

    A finished call will include a returnValue or \l error.
*/
bool QRemoteObjectPendingCall::isFinished() const
{
    if (!d)
        return true; // considered finished

    QMutexLocker locker(&d->mutex);
    return d->error != InvalidMessage;
}

/*!
    Blocks for up to \a timeout milliseconds, until the remote call has finished.

    Returns \c true on success, \c false otherwise.
*/
bool QRemoteObjectPendingCall::waitForFinished(int timeout)
{
    if (!d)
        return false;

    if (d->error != QRemoteObjectPendingCall::InvalidMessage)
        return true; // already finished

    QMutexLocker locker(&d->mutex);
    if (!d->replica)
        return false;

    return d->replica->waitForFinished(*this, timeout);
}

QRemoteObjectPendingCall QRemoteObjectPendingCall::fromCompletedCall(const QVariant &returnValue)
{
    QRemoteObjectPendingCallData *data = new QRemoteObjectPendingCallData;
    data->returnValue = returnValue;
    data->error = NoError;
    return QRemoteObjectPendingCall(data);
}

class QRemoteObjectPendingCallWatcherPrivate: public QObjectPrivate
{
public:
    Q_DECLARE_PUBLIC(QRemoteObjectPendingCallWatcher)
};

/*!
    \class QRemoteObjectPendingCallWatcher
    \inmodule QtRemoteObjects
    \brief Provides a QObject-based API for watching a QRemoteObjectPendingCall.

    QRemoteObjectPendingCallWatcher provides a signal indicating when a QRemoteObjectPendingCall
    has finished, allowing for convenient, non-blocking handling of the call.
*/

QRemoteObjectPendingCallWatcher::QRemoteObjectPendingCallWatcher(const QRemoteObjectPendingCall &call, QObject *parent)
    : QObject(*new QRemoteObjectPendingCallWatcherPrivate, parent)
    , QRemoteObjectPendingCall(call)
{
    if (d) {
        QMutexLocker locker(&d->mutex);
        if (!d->watcherHelper) {
            d->watcherHelper.reset(new QRemoteObjectPendingCallWatcherHelper);
            if (d->error != QRemoteObjectPendingCall::InvalidMessage) {
                // cause a signal emission anyways
                QMetaObject::invokeMethod(d->watcherHelper.data(), "finished", Qt::QueuedConnection);
            }
        }
        d->watcherHelper->add(this);
    }
}

QRemoteObjectPendingCallWatcher::~QRemoteObjectPendingCallWatcher()
{
}

/*!
    Returns true if the remote call has finished, false otherwise.

    A finished call will include a returnValue or error.
*/
bool QRemoteObjectPendingCallWatcher::isFinished() const
{
    if (!d)
        return true; // considered finished

    QMutexLocker locker(&d->mutex);
    return d->error != QRemoteObjectPendingCall::InvalidMessage;
}

/*!
    Blocks until the remote call has finished.
*/
void QRemoteObjectPendingCallWatcher::waitForFinished()
{
    if (d) {
        QRemoteObjectPendingCall::waitForFinished();

        // our signals were queued, so deliver them
        QCoreApplication::sendPostedEvents(d->watcherHelper.data(), QEvent::MetaCall);
        QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
    }
}

/*!
    \fn QRemoteObjectPendingCallWatcher::finished(QRemoteObjectPendingCallWatcher *self)

    This signal is emitted when the remote call has finished. \a self is the pointer to
    the watcher object that emitted the signal. A finished call will include a
    returnValue or error.
*/

/*!
    \class QRemoteObjectPendingReply
    \inmodule QtRemoteObjects
    \brief A templated version of QRemoteObjectPendingCall.
*/

/*! \fn template <typename T> T QRemoteObjectPendingReply<T>::returnValue() const

    Returns a strongly typed version of the return value of the remote call.
*/

QT_END_NAMESPACE

#include "moc_qremoteobjectpendingcall.cpp"
