// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default


#include "qquick3dxrhandinput_p.h"
#include "qquick3dxrinputmanager_p.h"
#include "qquick3dxrhandmodel_p.h"

#if defined(Q_OS_VISIONOS)
# include "visionos/qquick3dxrinputmanager_visionos_p.h"
#else
# include "openxr/qopenxrinputmanager_p.h"
#endif

QT_BEGIN_NAMESPACE

/*!
    \qmltype XrHandModel
    \inherits Model
    \inqmlmodule QtQuick3D.Xr
    \brief Represents a 3D model for a hand.

    Contains an animated 3D model that tracks the user's hands.

    XrHandModel is only visible when hand tracking is active.

    \note XrHandModel depends on hand-tracking data from the underlying
    system and is, therefore, not available on all platforms.

    \section1 Platform Notes

    \section2 Apple Vision Pro

    The Apple Vision Pro will overlay video of the user's hands directly,
    and the XrHandModel will not have any content.

    \section2 Meta devices

    This required the following feature and permission lines to be added
    in the AndroidManifest.qml file of your project.

    \badcode
        <uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
        <uses-feature android:name="oculus.software.handtracking" android:required="false" />
    \endcode

    Use \c true instead of \c false in the \c uses-feature tag if your app's interaction
    is implemented only for hands.
*/

QQuick3DXrHandModel::QQuick3DXrHandModel(QQuick3DNode *parent)
    : QQuick3DModel(parent)
{
}

void QQuick3DXrHandModel::updatePose()
{
    if (auto *skin = QQuick3DModel::skin()) {
        auto jointListProp = skin->joints();
        int count = jointListProp.count(&jointListProp);
        const auto positions = m_handTracker->jointPositions();
        const auto rotations = m_handTracker->jointRotations();
        for (int i = 0; i < count; ++i) {
            auto *joint = jointListProp.at(&jointListProp, i);
            joint->setPosition(positions.at(i));
            joint->setRotation(rotations.at(i));
        }
    } else {
        static bool warned = false;
        if (!warned) {
            qWarning() << "No skin available for hand model";
            warned = true;
        }
    }
}

void QQuick3DXrHandModel::setupModel()
{
    if (m_initialized) {
        qWarning() << "XrHandModel does not support changing hand";
        return;
    }
    QQuick3DXrInputManager *inputMan = QQuick3DXrInputManager::instance();
    if (m_hand == Hand::RightHand)
        m_handTracker = inputMan->rightHandInput();
    else if (m_hand == Hand::LeftHand)
        m_handTracker = inputMan->leftHandInput();
    if (!m_handTracker)
        return;

    QQuick3DXrInputManagerPrivate::get(inputMan)->setupHandModel(this);

    connect(m_handTracker, &QQuick3DXrHandInput::jointDataUpdated, this, &QQuick3DXrHandModel::updatePose);
    connect(m_handTracker, &QQuick3DXrHandInput::isHandTrackingChanged, this, [this](){
        setVisible(m_handTracker->isHandTrackingActive());
    });
    setVisible(m_handTracker->isActive());
    m_initialized = true;
}

void QQuick3DXrHandModel::componentComplete()
{
    setupModel();
    QQuick3DModel::componentComplete();
}

/*!
    \qmlproperty enumeration XrHandModel::hand
    \brief Specifies which hand the model is showing

    \value XrHandModel.LeftHand The left hand.
    \value XrHandModel.RightHand The right hand.
    \value XrHandModel.Unknown No hand is shown.

    \default XrHandModel.Unknown

    The value of this property is compatible with \l{XrController::controller}{XrController.controller}.

    \warning This property must be set when the XrHandModel is constructed.
    Changing hands later is not currently supported.
*/
QQuick3DXrHandModel::Hand QQuick3DXrHandModel::hand() const
{
    return m_hand;
}

void QQuick3DXrHandModel::setHand(Hand newHand)
{
    if (m_hand == newHand)
        return;
    m_hand = newHand;
    emit handChanged();
}
QT_END_NAMESPACE
