// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qattribute.h"
#include "qattribute_p.h"
#include <Qt3DCore/qbuffer.h>

QT_BEGIN_NAMESPACE

namespace Qt3DCore {

QAttributePrivate::QAttributePrivate()
    : QNodePrivate()
    , m_buffer(nullptr)
    , m_name()
    , m_vertexBaseType(QAttribute::Float)
    , m_vertexSize(1)
    , m_count(0)
    , m_byteStride(0)
    , m_byteOffset(0)
    , m_divisor(0)
    , m_attributeType(QAttribute::VertexAttribute)
    , m_dirty(false)
{
}

void QAttributePrivate::update()
{
    if (!m_blockNotifications)
        m_dirty = true;
    QNodePrivate::update();
}

QAttributePrivate *QAttributePrivate::get(QAttribute *q)
{
    return q->d_func();
}

/*!
 * \qmltype Attribute
 * \nativetype Qt3DCore::QAttribute
 * \inqmlmodule Qt3D.Core
 * \brief Defines an attribute and how data should be read from a Buffer.
 *
 * There are 3 types of attributes.
 * \list
 * \li VertexAttribute: used to define data to be read on a per vertex basis
 * \li IndexAttribute: used to define vertex indices when indexed draw calls are
 * to be used
 *\li DrawIndirectAttribute: used to specify the DrawIndirect buffer to be used
 * when indirect draw calls are to be used
 * \endlist
 *
 * \note when an attribute is of type DrawIndirectAttribute, only count, stride
 * and offset are relevant.
 *
 * When providing your own attributes, it may make sense to name your attribute
 * using helpers such as QAttribute::defaultPositionAttributeName() as that
 * will ensure your geometry will be compatible with picking and the various
 * materials provided in the Qt3DExtras module.
 */

/*!
 * \class Qt3DCore::QAttribute
 * \inheaderfile Qt3DCore/QAttribute
 * \inmodule Qt3DCore
 *
 * \inherits Qt3DCore::QNode
 *
 * \brief Defines an attribute and how data should be read from a QBuffer.
 *
 * There are 3 types of attributes.
 * \list
 * \li VertexAttribute: used to define data to be read on a per vertex basis
 * \li IndexAttribute: used to define vertex indices when indexed draw calls are
 * to be used
 *\li DrawIndirectAttribute: used to specify the DrawIndirect buffer to be used
 * when indirect draw calls are to be used
 * \endlist
 *
 * \note when an attribute is of type DrawIndirectAttribute, only count, stride
 * and offset are relevant.
 *
 * When providing your own attributes, it may make sense to name your attribute
 * using helpers such as QAttribute::defaultPositionAttributeName() as that
 * will ensure your geometry will be compatible with picking and the various
 * materials provided in the Qt3DExtras module.
 *
 * \sa QBuffer
 */

/*!
 * \typedef Qt3DCore::QBufferPtr
 * \relates Qt3DCore::QAttribute
 */

/*!
 * \enum Qt3DCore::QAttribute::AttributeType
 *
 * The type of the attribute.
 *
 * \value VertexAttribute
 * \value IndexAttribute
 * \value DrawIndirectAttribute
 */

/*!
 * \enum Qt3DCore::QAttribute::VertexBaseType
 *
 * The type of the data.
 *
 * \value Byte
 * \value UnsignedByte
 * \value Short
 * \value UnsignedShort
 * \value Int
 * \value UnsignedInt
 * \value HalfFloat
 * \value Float
 * \value Double
 */

/*!
 * Constructs a new QAttribute with \a parent.
 */
QAttribute::QAttribute(QNode *parent)
    : QNode(*new QAttributePrivate(), parent)
{
}

/*!
 * Constructs a new QAttribute from \a buf of \a type, \a dataSize, \a count, \a offset,
 * and \a stride with \a parent.
 */
QAttribute::QAttribute(QBuffer *buf, VertexBaseType type, uint dataSize, uint count, uint offset, uint stride, QNode *parent)
    : QAttribute(parent)
{
    Q_D(QAttribute);
    setBuffer(buf);
    d->m_count = count;
    d->m_byteOffset = offset;
    d->m_vertexBaseType = type;
    d->m_vertexSize = dataSize;
    d->m_byteStride = stride;
}


/*!
 * Constructs a new QAttribute named \a name from \a buf of \a type, \a
 * dataSize, \a count, \a offset, and \a stride with \a parent.
 */
QAttribute::QAttribute(QBuffer *buf, const QString &name, VertexBaseType type, uint dataSize, uint count, uint offset, uint stride, QNode *parent)
    : QAttribute(parent)
{
    Q_D(QAttribute);
    setBuffer(buf);
    d->m_name = name;
    d->m_count = count;
    d->m_byteOffset = offset;
    d->m_vertexBaseType = type;
    d->m_vertexSize = dataSize;
    d->m_byteStride = stride;
}

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

/*!
 * \property Qt3DCore::QAttribute::buffer
 *
 * Holds the buffer.
 */
QBuffer *QAttribute::buffer() const
{
    Q_D(const QAttribute);
    return d->m_buffer;
}

/*!
 * \property Qt3DCore::QAttribute::name
 *
 * Holds the name.
 */
QString QAttribute::name() const
{
    Q_D(const QAttribute);
    return d->m_name;
}

/*!
 * \property Qt3DCore::QAttribute::vertexSize
 *
 * Holds the data size, it can only be 1 to 4 units (scalars and vectors),
 * 9 units (3x3 matrices) or 16 units (4x4 matrices).
 */
uint QAttribute::vertexSize() const
{
    Q_D(const QAttribute);
    return d->m_vertexSize;
}

/*!
 * \property Qt3DCore::QAttribute::vertexBaseType
 *
 * Holds the data type.
 */
QAttribute::VertexBaseType QAttribute::vertexBaseType() const
{
    Q_D(const QAttribute);
    return d->m_vertexBaseType;
}

/*!
 * \property Qt3DCore::QAttribute::count
 *
 * Holds the count.
 */
uint QAttribute::count() const
{
    Q_D(const QAttribute);
    return d->m_count;
}

/*!
 * \property Qt3DCore::QAttribute::byteStride
 *
 * Holds the byte stride.
 */
uint QAttribute::byteStride() const
{
    Q_D(const QAttribute);
    return d->m_byteStride;
}

/*!
 * \property Qt3DCore::QAttribute::byteOffset
 *
 * Holds the byte offset.
 */
uint QAttribute::byteOffset() const
{
    Q_D(const QAttribute);
    return d->m_byteOffset;
}

/*!
 * \property Qt3DCore::QAttribute::divisor
 *
 * Holds the divisor.
 */
uint QAttribute::divisor() const
{
    Q_D(const QAttribute);
    return d->m_divisor;
}

/*!
 * \property Qt3DCore::QAttribute::attributeType
 *
 * Holds the attribute type.
 */
QAttribute::AttributeType QAttribute::attributeType() const
{
    Q_D(const QAttribute);
    return d->m_attributeType;
}

void QAttribute::setBuffer(QBuffer *buffer)
{
    Q_D(QAttribute);
    if (d->m_buffer == buffer)
        return;

    if (d->m_buffer)
        d->unregisterDestructionHelper(d->m_buffer);

    // We need to add it as a child of the current node if it has been declared inline
    // Or not previously added as a child of the current node so that
    // 1) The backend gets notified about it's creation
    // 2) When the current node is destroyed, it gets destroyed as well
    if (buffer && !buffer->parent())
        buffer->setParent(this);

    d->m_buffer = buffer;

    // Ensures proper bookkeeping
    if (d->m_buffer)
        d->registerDestructionHelper(d->m_buffer, &QAttribute::setBuffer, d->m_buffer);

    emit bufferChanged(buffer);
}

void QAttribute::setName(const QString &name)
{
    Q_D(QAttribute);
    if (d->m_name == name)
        return;

    d->m_name = name;
    emit nameChanged(name);
}

void QAttribute::setVertexBaseType(VertexBaseType type)
{
    Q_D(QAttribute);

    if (d->m_vertexBaseType == type)
        return;

    d->m_vertexBaseType = type;
    emit vertexBaseTypeChanged(type);
    emit dataTypeChanged(type);
}

void QAttribute::setVertexSize(uint size)
{
    Q_D(QAttribute);
    if (d->m_vertexSize == size)
        return;
    Q_ASSERT((size >= 1 && size <= 4) || (size == 9) || (size == 16));
    d->m_vertexSize = size;
    emit vertexSizeChanged(size);
    emit dataSizeChanged(size);
}

void QAttribute::setCount(uint count)
{
    Q_D(QAttribute);
    if (d->m_count == count)
        return;

    d->m_count = count;
    emit countChanged(count);
}

void QAttribute::setByteStride(uint byteStride)
{
    Q_D(QAttribute);
    if (d->m_byteStride == byteStride)
        return;

    d->m_byteStride = byteStride;
    emit byteStrideChanged(byteStride);
}

void QAttribute::setByteOffset(uint byteOffset)
{
    Q_D(QAttribute);
    if (d->m_byteOffset == byteOffset)
        return;

    d->m_byteOffset = byteOffset;
    emit byteOffsetChanged(byteOffset);
}

void QAttribute::setDivisor(uint divisor)
{
    Q_D(QAttribute);
    if (d->m_divisor == divisor)
        return;

    d->m_divisor = divisor;
    emit divisorChanged(divisor);
}

void QAttribute::setAttributeType(AttributeType attributeType)
{
    Q_D(QAttribute);
    if (d->m_attributeType == attributeType)
        return;

    d->m_attributeType = attributeType;
    emit attributeTypeChanged(attributeType);
}

/*!
 * \property Qt3DCore::QAttribute::defaultPositionAttributeName
 * \brief the name of the default position attribute.
 * \readonly
 *
 * Returns the name of the default position attribute: "vertexPosition".
 */

/*!
 * \brief QAttribute::defaultPositionAttributeName
 * \return the name of the default position attribute
 */
QString QAttribute::defaultPositionAttributeName()
{
    return QStringLiteral("vertexPosition");
}

/*!
 * \property Qt3DCore::QAttribute::defaultNormalAttributeName
 * \brief the name of the default normal attribute.
 * \readonly
 *
 * Returns the name of the default normal attribute: "vertexNormal".
 */

/*!
 * \brief QAttribute::defaultNormalAttributeName
 * \return the name of the default normal attribute
 */
QString QAttribute::defaultNormalAttributeName()
{
    return QStringLiteral("vertexNormal");
}

/*!
 * \property Qt3DCore::QAttribute::defaultColorAttributeName
 * \brief the name of the default color attribute.
 * \readonly
 *
 * Returns the name of the default color attribute: "vertexColor".
 */

/*!
 * \brief QAttribute::defaultColorAttributeName
 * \return the name of the default color attribute
 */
QString QAttribute::defaultColorAttributeName()
{
    return QStringLiteral("vertexColor");
}

/*!
 * \property Qt3DCore::QAttribute::defaultTextureCoordinateAttributeName
 * \brief the name of the default texture coordinate attribute.
 * \readonly
 *
 * Returns the name of the default texture coordinate attribute: "vertexTexCoord".
 */

/*!
 * \brief QAttribute::defaultTextureCoordinateAttributeName
 * \return the name of the default texture coordinate attribute
 */
QString QAttribute::defaultTextureCoordinateAttributeName()
{
    return QStringLiteral("vertexTexCoord");
}

/*!
 * \property Qt3DCore::QAttribute::defaultTextureCoordinate1AttributeName
 * \brief the name of the default attribute for the second layer of texture coordinates.
 * \readonly
 *
 * Returns the name of the default attribute for the second layer of texture coordinates:
 * "vertexTexCoord1".
 */

/*!
 * \brief QAttribute::defaultTextureCoordinate1AttributeName
 * \return the name of the default attribute for the second layer of texture
 * coordinates
 */
QString QAttribute::defaultTextureCoordinate1AttributeName()
{
    return QStringLiteral("vertexTexCoord1");
}

/*!
 * \property Qt3DCore::QAttribute::defaultTextureCoordinate2AttributeName
 * \brief the name of the default attribute for the third layer of texture coordinates.
 * \readonly
 *
 * Returns the name of the default attribute for the third layer of texture coordinates:
 * "vertexTexCoord2".
 */

/*!
 * \brief QAttribute::defaultTextureCoordinate2AttributeName
 * \return the name of the default attribute for the third layer of texture
 * coordinates
 */
QString QAttribute::defaultTextureCoordinate2AttributeName()
{
    return QStringLiteral("vertexTexCoord2");
}

/*!
 * \property Qt3DCore::QAttribute::defaultTangentAttributeName
 * \brief the name of the default tangent attribute.
 * \readonly
 *
 * Returns the name of the default tangent attribute: "vertexTangent".
 */

/*!
 * \brief QAttribute::defaultTangentAttributeName
 * \return the name of the default tangent attribute
 */
QString QAttribute::defaultTangentAttributeName()
{
    return QStringLiteral("vertexTangent");
}

/*!
 * \property Qt3DCore::QAttribute::defaultJointIndicesAttributeName
 * \brief the name of the default joint indices attribute.
 * \readonly
 *
 * Returns the name of the default joint indices attribute: "vertexJointIndices".
 */

/*!
 * \brief QAttribute::defaultJointIndicesAttributeName
 * \return the name of the default joint indices attribute
 */
QString QAttribute::defaultJointIndicesAttributeName()
{
    return QStringLiteral("vertexJointIndices");
}

/*!
 * \property Qt3DCore::QAttribute::defaultJointWeightsAttributeName
 * \brief the name of the default joint weights attribute.
 * \readonly
 *
 * Returns the name of the default joint weights attribute: "vertexJointWeights".
 */

/*!
 * \brief QAttribute::defaultJointIndicesAttributeName
 * \return the name of the default joint weights attribute
 */
QString QAttribute::defaultJointWeightsAttributeName()
{
    return QStringLiteral("vertexJointWeights");
}

/*!
\fn Qt3DCore::QAttribute::dataSizeChanged(uint vertexSize)

The signal is emitted with \a vertexSize when the dataSize changes.
*/
/*!
\fn Qt3DCore::QAttribute::dataTypeChanged(Qt3DCore::QAttribute::VertexBaseType vertexBaseType)

The signal is emitted with \a vertexBaseType when the dataType changed.
*/

} // Qt3DCore

QT_END_NAMESPACE

#include "moc_qattribute.cpp"
