/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qquickpageindicator_p.h"
#include "qquickcontrol_p_p.h"

#include <QtCore/qmath.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>

QT_BEGIN_NAMESPACE

/*!
    \qmltype PageIndicator
    \inherits Control
    \instantiates QQuickPageIndicator
    \inqmlmodule QtQuick.Controls
    \since 5.7
    \ingroup qtquickcontrols2-indicators
    \brief Indicates the currently active page.

    PageIndicator is used to indicate the currently active page
    in a container of multiple pages. PageIndicator consists of
    delegate items that present pages.

    \image qtquickcontrols2-pageindicator.png

    \code
    Column {
        StackLayout {
            id: stackLayout

            Page {
                // ...
            }
            Page {
                // ...
            }
            Page {
                // ...
            }
        }

        PageIndicator {
            currentIndex: stackLayout.currentIndex
            count: stackLayout.count
        }
    }
    \endcode

    \sa SwipeView, {Customizing PageIndicator}, {Indicator Controls}
*/

class QQuickPageIndicatorPrivate : public QQuickControlPrivate, public QQuickItemChangeListener
{
    Q_DECLARE_PUBLIC(QQuickPageIndicator)

public:
    void handlePress(const QPointF &point) override;
    void handleMove(const QPointF &point) override;
    void handleRelease(const QPointF &point) override;
    void handleUngrab() override;

    QQuickItem *itemAt(const QPointF &pos) const;
    void updatePressed(bool pressed, const QPointF &pos = QPointF());
    void setContextProperty(QQuickItem *item, const QString &name, const QVariant &value);

    void itemChildAdded(QQuickItem *, QQuickItem *child) override;

    int count = 0;
    int currentIndex = 0;
    bool interactive = false;
    QQmlComponent *delegate = nullptr;
    QQuickItem *pressedItem = nullptr;
};

void QQuickPageIndicatorPrivate::handlePress(const QPointF &point)
{
    QQuickControlPrivate::handlePress(point);
    if (interactive)
        updatePressed(true, point);
}

void QQuickPageIndicatorPrivate::handleMove(const QPointF &point)
{
    QQuickControlPrivate::handleMove(point);
    if (interactive)
        updatePressed(true, point);
}

void QQuickPageIndicatorPrivate::handleRelease(const QPointF &point)
{
    Q_Q(QQuickPageIndicator);
    QQuickControlPrivate::handleRelease(point);
    if (interactive) {
        if (pressedItem && contentItem)
            q->setCurrentIndex(contentItem->childItems().indexOf(pressedItem));
        updatePressed(false);
    }
}

void QQuickPageIndicatorPrivate::handleUngrab()
{
    QQuickControlPrivate::handleUngrab();
    if (interactive)
        updatePressed(false);
}

QQuickItem *QQuickPageIndicatorPrivate::itemAt(const QPointF &pos) const
{
    Q_Q(const QQuickPageIndicator);
    if (!contentItem || !q->contains(pos))
        return nullptr;

    QPointF contentPos = q->mapToItem(contentItem, pos);
    QQuickItem *item = contentItem->childAt(contentPos.x(), contentPos.y());
    while (item && item->parentItem() != contentItem)
        item = item->parentItem();
    if (item && !QQuickItemPrivate::get(item)->isTransparentForPositioner())
        return item;

    // find the nearest
    qreal distance = qInf();
    QQuickItem *nearest = nullptr;
    const auto childItems = contentItem->childItems();
    for (QQuickItem *child : childItems) {
        if (QQuickItemPrivate::get(child)->isTransparentForPositioner())
            continue;

        QPointF center = child->boundingRect().center();
        QPointF pt = contentItem->mapToItem(child, contentPos);

        qreal len = QLineF(center, pt).length();
        if (len < distance) {
            distance = len;
            nearest = child;
        }
    }
    return nearest;
}

void QQuickPageIndicatorPrivate::updatePressed(bool pressed, const QPointF &pos)
{
    QQuickItem *prevItem = pressedItem;
    pressedItem = pressed ? itemAt(pos) : nullptr;
    if (prevItem != pressedItem) {
        setContextProperty(prevItem, QStringLiteral("pressed"), false);
        setContextProperty(pressedItem, QStringLiteral("pressed"), pressed);
    }
}

void QQuickPageIndicatorPrivate::setContextProperty(QQuickItem *item, const QString &name, const QVariant &value)
{
    QQmlContext *context = qmlContext(item);
    if (context && context->isValid()) {
        context = context->parentContext();
        if (context && context->isValid())
            context->setContextProperty(name, value);
    }
}

void QQuickPageIndicatorPrivate::itemChildAdded(QQuickItem *, QQuickItem *child)
{
    if (!QQuickItemPrivate::get(child)->isTransparentForPositioner())
        setContextProperty(child, QStringLiteral("pressed"), false);
}

QQuickPageIndicator::QQuickPageIndicator(QQuickItem *parent)
    : QQuickControl(*(new QQuickPageIndicatorPrivate), parent)
{
}

/*!
    \qmlproperty int QtQuick.Controls::PageIndicator::count

    This property holds the number of pages.
*/
int QQuickPageIndicator::count() const
{
    Q_D(const QQuickPageIndicator);
    return d->count;
}

void QQuickPageIndicator::setCount(int count)
{
    Q_D(QQuickPageIndicator);
    if (d->count == count)
        return;

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

/*!
    \qmlproperty int QtQuick.Controls::PageIndicator::currentIndex

    This property holds the index of the current page.
*/
int QQuickPageIndicator::currentIndex() const
{
    Q_D(const QQuickPageIndicator);
    return d->currentIndex;
}

void QQuickPageIndicator::setCurrentIndex(int index)
{
    Q_D(QQuickPageIndicator);
    if (d->currentIndex == index)
        return;

    d->currentIndex = index;
    emit currentIndexChanged();
}

/*!
    \qmlproperty bool QtQuick.Controls::PageIndicator::interactive

    This property holds whether the control is interactive. An interactive page indicator
    reacts to presses and automatically changes the \l {currentIndex}{current index}
    appropriately.

    \snippet qtquickcontrols2-pageindicator-interactive.qml 1

    \note Page indicators are typically quite small (in order to avoid
    distracting the user from the actual content of the user interface). They
    can be hard to click, and might not be easily recognized as interactive by
    the user. For these reasons, they are best used to complement primary
    methods of navigation (such as \l SwipeView), not replace them.

    The default value is \c false.
*/
bool QQuickPageIndicator::isInteractive() const
{
    Q_D(const QQuickPageIndicator);
    return d->interactive;
}

void QQuickPageIndicator::setInteractive(bool interactive)
{
    Q_D(QQuickPageIndicator);
    if (d->interactive == interactive)
        return;

    d->interactive = interactive;
    if (interactive) {
        setAcceptedMouseButtons(Qt::LeftButton);
#if QT_CONFIG(cursor)
        setCursor(Qt::ArrowCursor);
#endif
    } else {
        setAcceptedMouseButtons(Qt::NoButton);
#if QT_CONFIG(cursor)
        unsetCursor();
#endif
    }
    emit interactiveChanged();
}

/*!
    \qmlproperty Component QtQuick.Controls::PageIndicator::delegate

    This property holds a delegate that presents a page.

    The following properties are available in the context of each delegate:
    \table
        \row \li \b index : int \li The index of the item
        \row \li \b pressed : bool \li Whether the item is pressed
    \endtable
*/
QQmlComponent *QQuickPageIndicator::delegate() const
{
    Q_D(const QQuickPageIndicator);
    return d->delegate;
}

void QQuickPageIndicator::setDelegate(QQmlComponent *delegate)
{
    Q_D(QQuickPageIndicator);
    if (d->delegate == delegate)
        return;

    d->delegate = delegate;
    emit delegateChanged();
}

void QQuickPageIndicator::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
{
    Q_D(QQuickPageIndicator);
    QQuickControl::contentItemChange(newItem, oldItem);
    if (oldItem)
        QQuickItemPrivate::get(oldItem)->removeItemChangeListener(d, QQuickItemPrivate::Children);
    if (newItem)
        QQuickItemPrivate::get(newItem)->addItemChangeListener(d, QQuickItemPrivate::Children);
}

#if QT_CONFIG(quicktemplates2_multitouch)
void QQuickPageIndicator::touchEvent(QTouchEvent *event)
{
    Q_D(QQuickPageIndicator);
    if (d->interactive)
        QQuickControl::touchEvent(event);
    else
        event->ignore(); // QTBUG-61785
}
#endif

#if QT_CONFIG(accessibility)
QAccessible::Role QQuickPageIndicator::accessibleRole() const
{
    return QAccessible::Indicator;
}
#endif

QT_END_NAMESPACE
