// Copyright (C) 2016 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
// Qt-Security score:significant

#include "qsignaltransition.h"
#include "qsignaltransition_p.h"
#include "qstate.h"
#include "qstate_p.h"
#include "qstatemachine.h"
#include "qstatemachine_p.h"
#include <qdebug.h>

QT_BEGIN_NAMESPACE

/*!
  \class QSignalTransition
  \inmodule QtStateMachine

  \brief The QSignalTransition class provides a transition based on a Qt signal.

  \since 4.6
  \ingroup statemachine

  Typically you would use the overload of QState::addTransition() that takes a
  sender and signal as arguments, rather than creating QSignalTransition
  objects directly. QSignalTransition is part of \l{Qt State Machine Overview}
  {Qt State Machine Framework}.

  You can subclass QSignalTransition and reimplement eventTest() to make a
  signal transition conditional; the event object passed to eventTest() will
  be a QStateMachine::SignalEvent object. Example:

  \code
  class CheckedTransition : public QSignalTransition
  {
  public:
      CheckedTransition(QCheckBox *check)
          : QSignalTransition(check, SIGNAL(stateChanged(int))) {}
  protected:
      bool eventTest(QEvent *e) {
          if (!QSignalTransition::eventTest(e))
              return false;
          QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
          return (se->arguments().at(0).toInt() == Qt::Checked);
      }
  };

  ...

  QCheckBox *check = new QCheckBox();
  check->setTristate(true);

  QState *s1 = new QState();
  QState *s2 = new QState();
  CheckedTransition *t1 = new CheckedTransition(check);
  t1->setTargetState(s2);
  s1->addTransition(t1);
  \endcode
*/

/*!
    \property QSignalTransition::senderObject

    \brief the sender object that this signal transition is associated with
*/

/*!
    \property QSignalTransition::signal

    \brief the signal that this signal transition is associated with
*/

QSignalTransitionPrivate::QSignalTransitionPrivate()
{
    signalIndex = -1;
}

void QSignalTransitionPrivate::unregister()
{
    Q_Q(QSignalTransition);
    if ((signalIndex == -1) || !machine())
        return;
    QStateMachinePrivate::get(machine())->unregisterSignalTransition(q);
}

void QSignalTransitionPrivate::maybeRegister()
{
    Q_Q(QSignalTransition);
    if (QStateMachine *mach = machine())
        QStateMachinePrivate::get(mach)->maybeRegisterSignalTransition(q);
}

/*!
  Constructs a new signal transition with the given \a sourceState.
*/
QSignalTransition::QSignalTransition(QState *sourceState)
    : QAbstractTransition(*new QSignalTransitionPrivate, sourceState)
{
}

/*!
  Constructs a new signal transition associated with the given \a signal of
  the given \a sender, and with the given \a sourceState.
*/
QSignalTransition::QSignalTransition(const QObject *sender, const char *signal,
                                     QState *sourceState)
    : QAbstractTransition(*new QSignalTransitionPrivate, sourceState)
{
    Q_D(QSignalTransition);
    d->senderObject.setValueBypassingBindings(sender);
    d->signal.setValueBypassingBindings(signal);
    d->maybeRegister();
}

/*!
  \fn template <typename PointerToMemberFunction> QSignalTransition::QSignalTransition(const QObject *sender, PointerToMemberFunction signal, QState *sourceState)
  \since 5.7
  \overload

  Constructs a new signal transition associated with the given \a signal of
  the given \a sender object and with the given \a sourceState.
  This constructor is enabled if the compiler supports delegating constructors,
  as indicated by the presence of the macro Q_COMPILER_DELEGATING_CONSTRUCTORS.
*/

/*!
  Destroys this signal transition.
*/
QSignalTransition::~QSignalTransition()
{
}

/*!
  Returns the sender object associated with this signal transition.
*/
const QObject *QSignalTransition::senderObject() const
{
    Q_D(const QSignalTransition);
    return d->senderObject;
}

/*!
  Sets the \a sender object associated with this signal transition.
*/
void QSignalTransition::setSenderObject(const QObject *sender)
{
    Q_D(QSignalTransition);
    d->senderObject.removeBindingUnlessInWrapper();
    if (sender == d->senderObject.valueBypassingBindings())
        return;
    d->unregister();
    d->senderObject.setValueBypassingBindings(sender);
    d->maybeRegister();
    d->senderObject.notify();
    emit senderObjectChanged(QPrivateSignal());
}

QBindable<const QObject*> QSignalTransition::bindableSenderObject()
{
    Q_D(QSignalTransition);
    return &d->senderObject;
}

/*!
  Returns the signal associated with this signal transition.
*/
QByteArray QSignalTransition::signal() const
{
    Q_D(const QSignalTransition);
    return d->signal;
}

/*!
  Sets the \a signal associated with this signal transition.
*/
void QSignalTransition::setSignal(const QByteArray &signal)
{
    Q_D(QSignalTransition);
    d->signal.removeBindingUnlessInWrapper();
    if (signal == d->signal.valueBypassingBindings())
        return;
    d->unregister();
    d->signal.setValueBypassingBindings(signal);
    d->maybeRegister();
    d->signal.notify();
    emit signalChanged(QPrivateSignal());
}

QBindable<QByteArray> QSignalTransition::bindableSignal()
{
    Q_D(QSignalTransition);
    return &d->signal;
}

/*!
  \reimp

  The default implementation returns \c true if the \a event is a
  QStateMachine::SignalEvent object and the event's sender and signal index
  match this transition, and returns \c false otherwise.
*/
bool QSignalTransition::eventTest(QEvent *event)
{
    Q_D(const QSignalTransition);
    if (event->type() == QEvent::StateMachineSignal) {
        if (d->signalIndex == -1)
            return false;
        QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(event);
        return (se->sender() == d->senderObject.value())
            && (se->signalIndex() == d->signalIndex);
    }
    return false;
}

/*!
  \reimp
*/
void QSignalTransition::onTransition(QEvent *event)
{
    Q_UNUSED(event);
}

/*!
  \reimp
*/
bool QSignalTransition::event(QEvent *e)
{
    return QAbstractTransition::event(e);
}

/*!
  \fn QSignalTransition::senderObjectChanged()
  \since 5.4

  This signal is emitted when the senderObject property is changed.

  \sa QSignalTransition::senderObject
*/

/*!
  \fn QSignalTransition::signalChanged()
  \since 5.4

  This signal is emitted when the signal property is changed.

  \sa QSignalTransition::signal
*/

void QSignalTransitionPrivate::callOnTransition(QEvent *e)
{
    Q_Q(QSignalTransition);

    if (e->type() == QEvent::StateMachineSignal) {
        QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent *>(e);
        int savedSignalIndex = se->m_signalIndex;
        se->m_signalIndex = originalSignalIndex;
        q->onTransition(e);
        se->m_signalIndex = savedSignalIndex;
    } else {
        q->onTransition(e);
    }
}


QT_END_NAMESPACE

#include "moc_qsignaltransition.cpp"
