// Copyright (C) 2026 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 "qobjectregistry_p.h"

#include <private/qabstractobjectregistryref_p.h>
#include <private/qobjectregistrysingleton_p.h>

#include <QtQml/qqmlengine.h>

QT_BEGIN_NAMESPACE

/*!
    \qmltype ObjectRegistry
    \inqmlmodule QtQml.DesignSupport
    \ingroup qmldesignsupport_qml
    \brief This type is used to register objects for later access based on a key string.

    The object registry provides a QML engine-wide reference for objects. You register each object
    with a string key, which you can use later to retrieve the object through one of the following
    reference types: ObjectRegistryRef and MultiObjectRegistryRef. C++ classes are also provided
    for accessing registered objects: QObjectRegistryRef and QMultiObjectRegistryRef.

    The same key can be used to register multiple objects. A typical use case for this is
    registering a list or repeater delegate.

    The registered object is automatically removed from the registry on either object
    destruction or ObjectRegistry instance destruction. Changing the target object or the key
    removes the old combination from the registry.

    Do not register the same object and key combination more than once. Only one registration per
    combination is stored.

    The registration can be done either with attached property on the registered object or
    using separate ObjectRegistry object with target and key:

    \snippet doc_src_objectregistry.cpp 6

    \sa ObjectRegistryRef, MultiObjectRegistryRef, QObjectRegistryRef, QMultiObjectRegistryRef
*/

/*!
    \qmlproperty string ObjectRegistry::key
    This property specifies the key of the registered object.
*/

/*!
    \qmlproperty QtObject ObjectRegistry::target
    This property specifies the target object to register.
*/

QObjectRegistryAttachedType::QObjectRegistryAttachedType(QObject *parent)
    : QObject(parent)
{
    m_registry = QObjectRegistrySingleton::registryForObject(parent);
}

QObjectRegistryAttachedType::~QObjectRegistryAttachedType()
{
    if (m_registry)
        m_registry->remove(m_key, parent());
}

QString QObjectRegistryAttachedType::key() const
{
    return m_key;
}

void QObjectRegistryAttachedType::setKey(const QString &key)
{
    if (m_key == key)
        return;

    if (m_qmlSetupInProgress) {
        m_key = key;
        emit keyChanged();
        return;
    }

    if (m_registry)
        m_registry->remove(m_key, parent());

    m_key = key;

    if (m_registry)
        m_registry->add(m_key, parent());

    emit keyChanged();
}

void QObjectRegistryAttachedType::classBegin()
{
    m_qmlSetupInProgress = true;
}

void QObjectRegistryAttachedType::componentComplete()
{
    m_qmlSetupInProgress = false;

    if (m_registry)
        m_registry->add(m_key, parent());
}

QObjectRegistry::QObjectRegistry(QObject *parent)
    : QObject(parent)
{
}

QObjectRegistry::~QObjectRegistry()
{
    if (m_registry)
        m_registry->remove(m_key, m_target);
}

QObjectRegistryAttachedType *QObjectRegistry::qmlAttachedProperties(QObject *object)
{
    return new QObjectRegistryAttachedType(object);
}

QString QObjectRegistry::key() const
{
    return m_key;
}

void QObjectRegistry::setKey(const QString &key)
{
    if (m_key == key)
        return;

    if (!m_target) {
        m_key = key;
        emit keyChanged();
        return; // Postpone actual handling until we have both key and target
    }

    if (m_registry)
        m_registry->remove(m_key, m_target);

    m_key = key;

    if (m_registry)
        m_registry->add(m_key, m_target);

    emit keyChanged();
}

QObject *QObjectRegistry::target() const
{
    return m_target;
}

void QObjectRegistry::setTarget(QObject *target)
{
    if (m_target == target)
        return;

    if (m_key.isEmpty()) {
        m_target = target;
        emit targetChanged();
        return; // Postpone actual handling until we have both key and target
    }

    if (!m_registry)
        m_registry = QObjectRegistrySingleton::registryForObject(this);

    if (m_registry)
        m_registry->remove(m_key, m_target);

    m_target = target;

    if (m_registry)
        m_registry->add(m_key, m_target);

    emit targetChanged();
}

QT_END_NAMESPACE

