/******************************************************************************
 * QSkinny - Copyright (C) The authors
 *           SPDX-License-Identifier: BSD-3-Clause
 *****************************************************************************/

#include "QskPageIndicator.h"
#include "QskSkinlet.h"
#include "QskEvent.h"

QSK_SUBCONTROL( QskPageIndicator, Panel )
QSK_SUBCONTROL( QskPageIndicator, Bullet )

QSK_SYSTEM_STATE( QskPageIndicator, Selected, QskAspect::FirstSystemState << 1 )

static int qskKeyIncrement(
    const QskPageIndicator* indicator, const QKeyEvent* event )
{
    if ( qskIsStandardKeyInput( event, QKeySequence::MoveToNextChar ) )
        return 1;

    if ( qskIsStandardKeyInput( event, QKeySequence::MoveToPreviousChar ) )
        return -1;

    const auto key = event->key();

    if ( indicator->orientation() == Qt::Horizontal )
    {
        const bool mirrored = indicator->layoutMirroring();

        if ( key == Qt::Key_Left )
            return mirrored ? 1 : -1;

        if ( key == Qt::Key_Right )
            return mirrored ? -1 : 1;
    }
    else
    {
        if ( key == Qt::Key_Up )
            return -1;

        if ( key == Qt::Key_Down )
            return 1;
    }

    return 0;
}


class QskPageIndicator::PrivateData
{
  public:
    PrivateData( int count )
        : count( count )
        , orientation( Qt::Horizontal )
    {
    }

    qreal currentIndex = -1;
    int pressedIndex = -1;

    int count;
    Qt::Orientation orientation : 2;
};

QskPageIndicator::QskPageIndicator( int count, QQuickItem* parent )
    : Inherited( parent )
    , m_data( new PrivateData( count ) )
{
    // as we don't stretch the bullets
    initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
}

QskPageIndicator::QskPageIndicator( QQuickItem* parent )
    : QskPageIndicator( 0, parent )
{
}

QskPageIndicator::~QskPageIndicator()
{
}

int QskPageIndicator::count() const
{
    return m_data->count;
}

qreal QskPageIndicator::currentIndex() const
{
    return m_data->currentIndex;
}

Qt::Orientation QskPageIndicator::orientation() const
{
    return m_data->orientation;
}

void QskPageIndicator::setOrientation( Qt::Orientation orientation )
{
    if ( orientation != m_data->orientation )
    {
        m_data->orientation = orientation;

        resetImplicitSize();
        update();

        Q_EMIT orientationChanged( orientation );
    }
}

void QskPageIndicator::setCount( int count )
{
    if ( count != m_data->count )
    {
        m_data->count = count;

        resetImplicitSize();
        update();

        Q_EMIT countChanged( count );
    }
}

void QskPageIndicator::setCurrentIndex( qreal index )
{
    if ( index < 0 || index >= m_data->count )
        index = -1;

    if ( index != m_data->currentIndex )
    {
        m_data->currentIndex = index;
        update();

        Q_EMIT currentIndexChanged( index );
    }
}

qreal QskPageIndicator::valueRatioAt( int index ) const
{
    if ( m_data->currentIndex >= 0.0 && index >= 0 )
    {
        qreal pos = m_data->currentIndex;

        if ( index == 0 && pos > m_data->count - 1 )
            pos -= m_data->count;

        const qreal diff = 1.0 - std::abs( pos - index );
        return std::max( diff, 0.0 );
    }

    return 0.0;
}

QRectF QskPageIndicator::bulletRect( int index ) const
{
    return effectiveSkinlet()->sampleRect(
        this, contentsRect(), QskPageIndicator::Bullet, index );
}

int QskPageIndicator::indexAtPosition( const QPointF& pos ) const
{
    return effectiveSkinlet()->sampleIndexAt(
        this, contentsRect(), QskPageIndicator::Bullet, pos );
}

QskAspect::Variation QskPageIndicator::effectiveVariation() const
{
    return static_cast< QskAspect::Variation >( m_data->orientation );
}

void QskPageIndicator::mousePressEvent( QMouseEvent* event )
{
    if ( event->button() == Qt::LeftButton )
    {
        /*
            The bullets are usually small and therefore hard to click - with
            touch input almost impossible. It might be better to
            increment in direction of the mouse position ?

            A swipe gesture might make more sense, TODO ...
         */
        const auto pos = qskMousePosition( event );
        m_data->pressedIndex = indexAtPosition( pos );

        return;
    }

    Inherited::mousePressEvent( event );
}

void QskPageIndicator::mouseUngrabEvent()
{
    m_data->pressedIndex = -1;
}

void QskPageIndicator::mouseReleaseEvent( QMouseEvent* event )
{
    if ( event->button() == Qt::LeftButton )
    {
        const auto index = m_data->pressedIndex;
        m_data->pressedIndex = -1;

        if ( index >= 0 )
        {
            const auto pos = qskMousePosition( event );
            if ( indexAtPosition( pos ) == index )
                Q_EMIT pageRequested( index );
        }

        return;
    }

    Inherited::mouseReleaseEvent( event );
}

void QskPageIndicator::keyPressEvent( QKeyEvent* event )
{
    if ( const int increment = qskKeyIncrement( this, event ) )
    {
        incrementRequested( increment );
        return;
    }

    Inherited::keyPressEvent( event );
}

#ifndef QT_NO_WHEELEVENT

void QskPageIndicator::wheelEvent( QWheelEvent* event )
{
    incrementRequested( qskWheelSteps( event ) );
}

#endif

void QskPageIndicator::incrementRequested( int offset )
{
    const auto n = m_data->count;

    if ( offset == 0 || n == 0 )
        return;

    int index = m_data->currentIndex;
    if ( index < 0 && offset < 0 )
        index = n;

    // do we need a cycling on/off attribute, TODO ...

    index = ( index + offset ) % n;
    if ( index < 0 )
        index += n;

    if ( index != m_data->currentIndex )
        Q_EMIT pageRequested( index );
}

#include "moc_QskPageIndicator.cpp"
