// Copyright (C) 2025 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 "qqstylekit_p.h"
#include "qqstylekitstyle_p.h"
#include "qqstylekittheme_p.h"
#include "qqstylekitreader_p.h"

#include <QtGui/QStyleHints>

QT_BEGIN_NAMESPACE

QPointer<QQStyleKitAttached> QQStyleKitAttached::s_instance;
bool QQStyleKitAttached::s_transitionsEnabled = true;

/*!
    \qmltype StyleKit
    \inqmlmodule Qt.labs.StyleKit
    \brief A singleton for setting and accessing the current style.

    \c StyleKit is a singleton for setting the application \l Style.

    \snippet PlainStyleMain.qml 1

    It also provides properties for global StyleKit configuration and debugging.

    \labs

    \sa Style, StyleVariation
*/

/*!
    \qmlproperty Style StyleKit::style

    The active style for the application.

    Set this to a \l Style instance to apply it to all \l {Qt Quick Controls}.

    \snippet PlainStyleMain.qml 1

    \note For best startup performance, set the style on the root \l ApplicationWindow, before
    any child controls.

    \sa styleUrl, Style
*/

/*!
    \qmlproperty url StyleKit::styleUrl

    An alternative to \l style that loads a \l Style from a URL.

    \sa style
*/

/*!
    \qmlproperty bool StyleKit::transitionsEnabled

    Controls whether transitions are enabled globally.

    Set this to \c false to disable all transitions in the
    current style.

    The default value is \c true.

    \sa {ControlStyle::transition}
*/

/*!
    \qmlproperty StyleKitDebug StyleKit::debug
    \readonly

    A run-time debugging tool that can be used to trace
    style properties for a specific \l Control.
*/

/*!
    \qmlmethod bool StyleKit::styleLoaded()

    Returns \c true when the active \l style and its current \l {Style::}{theme} have both
    finished loading. This can be used to defer initialization logic that
    depends on resolved style properties.

    \sa style, {Style::}{theme}
*/

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

QQStyleKitAttached *QQStyleKit::qmlAttachedProperties(QObject *obj)
{
    /* QQStyleKitAttached is a singleton. It doesn't matter where it's
     * used from in the application, it will always represent the same
     * application global style and theme. */
    if (!QQStyleKitAttached::s_instance)
        QQStyleKitAttached::s_instance = new QQStyleKitAttached(QGuiApplication::instance());
    if (!QQStyleKitAttached::s_instance->m_engine && obj)
        QQStyleKitAttached::s_instance->m_engine = qmlEngine(obj);
    return QQStyleKitAttached::s_instance.get();
}

QQStyleKitAttached::QQStyleKitAttached(QObject *parent)
    : QObject(parent)
{
    connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this](){
        if (m_style && QString::compare(m_style->themeName(), "System"_L1, Qt::CaseInsensitive) == 0)
            m_style->recreateTheme();
    });
}

QQStyleKitAttached::~QQStyleKitAttached()
{
    QQStyleKitAttached::s_instance = nullptr;
    if (m_ownsStyle) {
        delete m_style;
        m_style = nullptr;
    }
}

QQStyleKitStyle *QQStyleKitAttached::style() const
{
    return m_style;
}

void QQStyleKitAttached::setStyle(QQStyleKitStyle *style)
{
    if (m_style == style)
        return;

    if (m_ownsStyle)
        m_style->deleteLater();

    m_style = style;
    m_style->reapplyStyle();

    emit styleChanged();
}

QUrl QQStyleKitAttached::styleUrl() const
{
    return m_styleUrl;
}

void QQStyleKitAttached::setStyleUrl(const QUrl &styleUrl)
{
    if (m_styleUrl == styleUrl)
        return;

    m_styleUrl = styleUrl;

    Q_ASSERT(m_engine);
    QQmlComponent component(m_engine, styleUrl, this);
    if (!component.errors().isEmpty()) {
        qmlWarning(this) << "Could not create a Style from url '" << styleUrl << "': " << component.errorString();
        return;
    }

    /* Avoid creating anything other than StyleKit Style objects by checking the metaobject
     * of the root type before creating the object. This to avoid instantiating items or other
     * object that could cause unwanted side effects. */
    QQmlComponentPrivate *componentPrivate = QQmlComponentPrivate::get(&component);
    const auto compilationUnit = componentPrivate->compilationUnit();
    const auto propertyCache = compilationUnit ? compilationUnit->rootPropertyCache() : nullptr;
    const auto firstMetaObject = propertyCache ? propertyCache->firstCppMetaObject() : nullptr;
    if (!firstMetaObject || !firstMetaObject->inherits(&QQStyleKitStyle::staticMetaObject)) {
        qmlWarning(this) << "Could not create a Style from url '" << styleUrl << "': the root item is not a Style.";
        return;
    }

    auto *style = qobject_cast<QQStyleKitStyle *>(component.create());
    if (!style) {
        qmlWarning(this) << "Could not create a Style from url '" << styleUrl << "': failed to create an instance of the resolved Component.";
        return;
    }

    style->componentComplete();
    setStyle(style);
    m_ownsStyle = true;
    emit styleUrlChanged();
}

bool QQStyleKitAttached::transitionsEnabled() const
{
    return s_transitionsEnabled;
}

void QQStyleKitAttached::setTransitionsEnabled(bool enabled)
{
    if (enabled == s_transitionsEnabled)
        return;

    s_transitionsEnabled = enabled;
    emit transitionsEnabledChanged();
}

QQStyleKitDebug *QQStyleKitAttached::debug() const
{
    return &const_cast<QQStyleKitAttached *>(this)->m_debug;
}

bool QQStyleKitAttached::styleLoaded() const
{
    return m_style && m_style->loaded();
}

QT_END_NAMESPACE

#include "moc_qqstylekit_p.cpp"

