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

#include "qtimelineanimationnode_p.h"

QT_BEGIN_NAMESPACE

/*!
    \qmltype TimelineAnimationNode
    \inherits QBlendTreeNode
    \nativetype QTimelineAnimationNode
    \inqmlmodule QtQuick.Timeline.BlendTrees
    \ingroup qtqmltypes

    \brief A blend tree source node that plays a timeline animation.

    TimelineAnimationNode is a blend tree source node that plays a timeline
    animation and outputs the animation's frame data. This node wraps a
    TimelineAnimation and its associated Timeline and provides a way to
    intercept the animation's frame data and output it to the blend tree.
*/

/*!
    \qmlproperty TimelineAnimation TimelineAnimationNode::animation

    This property holds the timeline animation to play.
*/

/*!
    \qmlproperty Timeline TimelineAnimationNode::timeline

    This property holds the timeline that the animation is played on.
*/

/*!
    \qmlproperty real TimelineAnimationNode::currentFrame

    This property holds the current frame of the animation.
*/

QTimelineAnimationNode::QTimelineAnimationNode(QObject *parent)
    : QBlendTreeNode(parent)
{

}

QQuickTimelineAnimation *QTimelineAnimationNode::animation() const
{
    return m_animation;
}

void QTimelineAnimationNode::setAnimation(QQuickTimelineAnimation *newAnimation)
{
    if (m_animation == newAnimation)
        return;

    if (m_animation)
        disconnect(m_animationDestroyedConnection);

    m_animation = newAnimation;

    if (m_animation)
        m_animationDestroyedConnection = connect(m_animation,
                                                 &QObject::destroyed,
                                                 this,
                                                 [this] {setAnimation(nullptr);});

    updateAnimationTarget();
    updateFrameData();
    Q_EMIT animationChanged();
}

QQuickTimeline *QTimelineAnimationNode::timeline() const
{
    return m_timeline;
}

void QTimelineAnimationNode::setTimeline(QQuickTimeline *newTimeline)
{
    if (m_timeline == newTimeline)
        return;

    if (m_timeline)
        disconnect(m_timelineDestroyedConnection);

    m_timeline = newTimeline;

    if (m_timeline)
        m_timelineDestroyedConnection = connect(m_timeline,
                                                 &QObject::destroyed,
                                                 this,
                                                 [this] {setTimeline(nullptr);});

    updateFrameData();
    Q_EMIT timelineChanged();
}

qreal QTimelineAnimationNode::currentFrame() const
{
    return m_currentFrame;
}

void QTimelineAnimationNode::setCurrentFrame(qreal newCurrentFrame)
{
    if (qFuzzyCompare(m_currentFrame, newCurrentFrame))
        return;
    m_currentFrame = newCurrentFrame;
    updateFrameData();
    Q_EMIT currentFrameChanged();
}

static QHash<QQmlProperty, QVariant> getFrameData(QQuickTimeline *timeline, qreal frame)
{
    QHash<QQmlProperty, QVariant> frameData;
    if (timeline) {
        QQmlListReference keyframeGroups(timeline, "keyframeGroups");
        if (keyframeGroups.isValid() && keyframeGroups.isReadable()) {
            for (int i = 0; i < keyframeGroups.count(); ++i) {
                QQuickKeyframeGroup *keyframeGroup = qobject_cast<QQuickKeyframeGroup *>(keyframeGroups.at(i));
                if (keyframeGroup && keyframeGroup->target()) {
                    QQmlProperty qmlProperty(keyframeGroup->target(), keyframeGroup->property());
                    QVariant value = keyframeGroup->evaluate(frame);
                    frameData.insert(qmlProperty, value);
                }
            }
        }
    }

    return frameData;
}

void QTimelineAnimationNode::updateFrameData()
{
    if (!m_animation || !m_timeline)
        return;

    m_frameData = getFrameData(m_timeline, m_currentFrame);
    Q_EMIT frameDataChanged();
}

void QTimelineAnimationNode::updateAnimationTarget()
{
    if (!m_animation)
        return;
    // Property should already be set to "currentFrame"
    m_animation->setTargetObject(this);
}

QT_END_NAMESPACE
