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

#include "qquick3dxrspatialanchor_p.h"

#if defined(Q_OS_VISIONOS)
#include "visionos/qquick3dxranchormanager_visionos_p.h"
#else
#include "openxr/qquick3dxranchormanager_openxr_p.h"
#endif

QT_BEGIN_NAMESPACE

/*!
    \qmltype XrSpatialAnchor
    \inherits QtObject
    \inqmlmodule QtQuick3D.Xr
    \brief Tracks a specific location or object in real space.

    This type represents a spatial anchor that tracks
    a specific location or object in real space. It provides information about
    the anchor's position, rotation, classification, and bounds.

    Spatial anchors are accessed through an \l XrSpatialAnchorListModel.

    \note The system provides Anchor objects. They cannot be created in QML.

    See the \l{Qt Quick 3D - XR Spatial Anchors Example} for how to use this type.

    \section1 Platform notes

    \section2 Meta Quest Devices

    This API makes use of the Meta Quest Scene and Spatial Anchor APIs.
    You need to add the following permissions to your app's \c AndroidManifest.xml file:

    \badcode
        <uses-permission android:name="com.oculus.permission.USE_ANCHOR_API"/>
        <uses-permission android:name="com.oculus.permission.USE_SCENE"/>
    \endcode

    \note You need to manually set up a Space before running an app, or anchors will
    not be available.
 */

/*!
    \qmlproperty enumeration XrSpatialAnchor::Classification
    \ingroup xr-anchors
    \brief The classification of the spatial anchor.
    \readonly

    The Classification enum provides a set of predefined category types that describe
    the purpose or context of a spatial anchor.

    \value Classification.Unknown The label has not been set or identified.
    \value Classification.Wall The anchor represents a wall.
    \value Classification.Ceiling The anchor represents a ceiling.
    \value Classification.Floor The anchor represents a floor.
    \value Classification.Table The anchor represents a table.
    \value Classification.Seat The anchor represents a seat.
    \value Classification.Window The anchor represents a window.
    \value Classification.Door The anchor represents a door.
    \value Classification.Other The anchor was not identified as any of the above types. See: \l classificationString

    The following table shows the mapping between the classification type in \qxr,
    OpenXR, and VisionOS. If the classification type from the system falls outside of the defined types,
    then the \e Type is set to \c Other, and the system type is provided by the \l classificationString property.

    \note The classification string can also be \c{Other}.

    \table
    \header
        \li Type
        \li OpenXR
        \li VisionOS
        \li Description
    \row
        \li Unknown
        \li -
        \li -
        \li The label has not been set or identified.
    \row
        \li Wall
        \li WALL_FACE
        \li Wall
        \li The anchor represents a wall.
    \row
        \li Ceiling
        \li CEILING
        \li Ceiling
        \li The anchor represents a ceiling.
    \row
        \li Floor
        \li FLOOR
        \li Floor
        \li The anchor represents a floor.
    \row
        \li Table
        \li TABLE
        \li Table
        \li The anchor represents a table.
    \row
        \li Seat
        \li COUCH
        \li Seat
        \li The anchor represents a seat.
    \row
        \li Window
        \li WINDOW_FRAME
        \li Window
        \li The anchor represents a window.
    \row
        \li Door
        \li DOOR_FRAME
        \li Door
        \li The anchor represents a door.
    \row
        \li Other
        \li -
        \li -
        \li The anchor represents something else. See: \l classificationString
    \endtable
 */

QQuick3DXrSpatialAnchor::QQuick3DXrSpatialAnchor(QtQuick3DXr::XrSpaceId space, QUuid &uuid, QObject *parent)
    : QObject(parent)
    , m_space(space)
    , m_uuid(uuid)
{
}

QQuick3DXrSpatialAnchor::~QQuick3DXrSpatialAnchor()
{
}

/*!
    \qmlproperty vector3d XrSpatialAnchor::offset3D
    \brief The 3D offset of the spatial anchor.
    \readonly

    This property provides the 3D offset of the anchor's bounds (in meters) from the anchor's \l position.

    \sa offset3D, has3DBounds
 */

QVector3D QQuick3DXrSpatialAnchor::offset3D() const
{
    return m_offset3D;
}

void QQuick3DXrSpatialAnchor::setOffset3D(const QVector3D &newOffset)
{
    if (m_offset3D == newOffset)
        return;
    m_offset3D = newOffset;
    emit offset3DChanged();
}

/*!
    \qmlproperty vector3d XrSpatialAnchor::extent3D
    \brief The 3D extent of the spatial anchor.
    \readonly

    This property specifies the spatial anchor's volume in three dimensions (width, height, and depth).
    It is valid when \l has3DBounds is \c true.

    \sa offset3D, has3DBounds
 */

QVector3D QQuick3DXrSpatialAnchor::extent3D() const
{
    return m_extent3D;
}

void QQuick3DXrSpatialAnchor::setExtent3D(const QVector3D &newExtent)
{
    if (m_extent3D == newExtent)
        return;
    m_extent3D = newExtent;
    emit extent3DChanged();
}

/*!
    \qmlproperty vector3d XrSpatialAnchor::position
    \brief The position of the spatial anchor.

    \readonly
    This property returns the 3D position (in meters) of the spatial anchor's origin within the
    session's coordinate system.
 */

QVector3D QQuick3DXrSpatialAnchor::position() const
{
    return m_position;
}

void QQuick3DXrSpatialAnchor::setPosition(const QVector3D &newPosition)
{
    if (m_position == newPosition)
        return;
    m_position = newPosition;
    emit positionChanged();
}

/*!
    \qmlproperty quaternion XrSpatialAnchor::rotation
    \brief The orientation of the spatial anchor.
    \readonly

    This property provides the spatial anchor's rotation (as a quaternion).
 */

QQuaternion QQuick3DXrSpatialAnchor::rotation() const
{
    return m_rotation;
}

void QQuick3DXrSpatialAnchor::setRotation(const QQuaternion &newRotation)
{
    if (m_rotation == newRotation)
        return;
    m_rotation = newRotation;
    emit rotationChanged();
}

/*!
    \qmlproperty enumeration XrSpatialAnchor::classification
    \brief The classification type of the spatial anchor.
    \readonly

    This property returns the \l {XrSpatialAnchor::Classification}{classification type} for this anchor
    (for example,\c Table or \c Floor) describing the anchor's purpose or context.

    \note The classification type coming from the system might not be in the set of labels
    defined by the \l Classification enum, in which case the type will be set to \c Other
    and the \l classificationString property will contain the original label.

    \sa classificationString
 */

QQuick3DXrSpatialAnchor::Classification QQuick3DXrSpatialAnchor::classification() const
{
    return m_classification;
}

void QQuick3DXrSpatialAnchor::setClassification(Classification newClassification)
{
    if (m_classification == newClassification)
        return;
    m_classification = newClassification;
    emit classificationChanged();
}

/*!
    \qmlproperty string XrSpatialAnchor::classificationString
    \brief The classification type of the spatial anchor as a string.
    \readonly

    This property returns the classification type as a string if one exists.
    If the classification type is not in the set of types defined by the \l Classification enums, the
    label is set to \c Other, and this property can be used to access the type as it was reported by
    the system.

    \note This string can be empty or change, depending on the system and how the anchor gets classified.

    \sa classification
*/

QString QQuick3DXrSpatialAnchor::classificationString() const
{
    return m_classificationString;
}

void QQuick3DXrSpatialAnchor::setClassificationString(const QString &newClassificationString)
{
    if (m_classificationString == newClassificationString)
        return;
    m_classificationString = newClassificationString;
    emit classificationStringChanged();
}

/*!
    \qmlproperty bool XrSpatialAnchor::has2DBounds
    \brief Indicates whether the spatial anchor has 2D bounds.
    \readonly

    This property holds \c true if the spatial anchor has 2D bounds,
    described by \l offset2D and \l extent2D, indicating that it
    represents a flat surface (for example, a floor or wall).

    Otherwise, it returns false.
    \sa offset2D, extent2D, has3DBounds
 */

bool QQuick3DXrSpatialAnchor::has2DBounds() const
{
    return m_has2DBounds;
}

void QQuick3DXrSpatialAnchor::setBounds2D(const QVector2D &offset, const QVector2D &extent)
{
    if (qFuzzyCompare(m_offset2D, offset) && qFuzzyCompare(m_extent2D, extent))
        return;

    m_offset2D = offset;
    m_extent2D = extent;

    // FIXME: verify
    m_has2DBounds = true;

    emit has2DBoundsChanged();
}

/*!
    \qmlproperty bool XrSpatialAnchor::has3DBounds
    \brief Indicates whether the spatial anchor has 3D bounds.
    \readonly

    This property returns \c true if the spatial anchor has 3D bounds, indicating
    that it represents a volume (for example, a table or a cupboard).
    The bounds are described by \l offset3D and \l extent3D.

    Otherwise, it returns \c false.
    \sa offset3D, extent3D, has2DBounds
 */


bool QQuick3DXrSpatialAnchor::has3DBounds() const
{
    return m_has3DBounds;
}

void QQuick3DXrSpatialAnchor::setBounds3D(const QVector3D &offset, const QVector3D &extent)
{
    if (qFuzzyCompare(m_offset3D, offset) && qFuzzyCompare(m_extent3D, extent))
        return;

    m_offset3D = offset;
    m_extent3D = extent;

    // FIXME: Store the 3D bounds and verify
    m_has3DBounds = true;

    emit has3DBoundsChanged();
}

/*!
    \qmlproperty vector2d XrSpatialAnchor::offset2D
    \brief The 2D offset of the spatial anchor.

    \readonly
    This property holds the offset of the anchor's bounds within
    the X/Z plane. It is valid when \l has2DBounds is \c true.

    \sa has2DBounds, extent2D
*/

QVector2D QQuick3DXrSpatialAnchor::offset2D() const
{
    return m_offset2D;
}

/*!
    \qmlproperty vector2d XrSpatialAnchor::extent2D
    \brief The 2D extent of the spatial anchor.
    \readonly

    This property holds the spatial anchor's size in two dimensions (width and height) within
    the X/Z plane. It is valid when \l has2DBounds is \c true.

    \sa has2DBounds, offset2D
 */

QVector2D QQuick3DXrSpatialAnchor::extent2D() const
{
    return m_extent2D;
}

/*!
    \qmlproperty string XrSpatialAnchor::identifier
    \brief A unique identifier for this spatial anchor.
    \readonly

    This property holds a unique identifier associated with the
    spatial anchor. This is the same identifier referenced by a \l XrSpatialAnchorListModel.
 */

QString QQuick3DXrSpatialAnchor::identifier() const
{
    return QString::fromLatin1(m_uuid.toRfc4122());
}

QSet<QUuid> QQuick3DXrSpatialAnchor::roomLayoutUuids() const
{
    return m_roomLayoutUuids;
}

void QQuick3DXrSpatialAnchor::setRoomLayoutUuids(const QSet<QUuid> &newRoomLayoutUuids)
{
    m_roomLayoutUuids = newRoomLayoutUuids;
}

QSet<QUuid> QQuick3DXrSpatialAnchor::spaceContainerUuids() const
{
    return m_spaceContainerUuids;
}

void QQuick3DXrSpatialAnchor::setSpaceContainerUuids(const QSet<QUuid> &newSpaceContainerUuids)
{
    m_spaceContainerUuids = newSpaceContainerUuids;
}

QT_END_NAMESPACE
