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

#include "QskProgressIndicator.h"

#include "QskIntervalF.h"
#include "QskFunctions.h"
#include "QskAnimator.h"
#include "QskAspect.h"

QSK_SUBCONTROL( QskProgressIndicator, Groove )
QSK_SUBCONTROL( QskProgressIndicator, Fill )

namespace
{
    class PositionAnimator : public QskAnimator
    {
      public:
        PositionAnimator( QskProgressIndicator* indicator )
            : m_indicator( indicator )
        {
            setAutoRepeat( true );
            setDuration( 1300 );

            setWindow( indicator->window() );
        }

        void advance( qreal value ) override
        {
            if ( m_indicator->setPositionHint( QskProgressIndicator::Fill, value ) )
                m_indicator->update();
        }

      private:
        QskProgressIndicator* m_indicator;
    };
}

class QskProgressIndicator::PrivateData
{
  public:
    void updateIndeterminateAnimator( QskProgressIndicator* indicator )
    {
        if ( !isIndeterminate )
        {
            delete animator;
            animator = nullptr;

            return;
        }

        if ( indicator->window() && indicator->isVisible() )
        {
            if ( animator == nullptr )
                animator = new PositionAnimator( indicator );

            animator->start();
        }
        else
        {
            if ( animator )
                animator->stop();
        }
    }

    PositionAnimator* animator = nullptr;

    qreal value = 0.0;
    qreal origin = 0.0;

    bool hasOrigin = false;
    bool isIndeterminate = false;
};

QskProgressIndicator::QskProgressIndicator( qreal min, qreal max, QQuickItem* parent )
    : QskBoundedControl( min, max, parent )
    , m_data( new PrivateData )
{
    m_data->value = minimum();

    connect( this, &QskBoundedControl::boundariesChanged,
        this, &QskProgressIndicator::adjustValue );
}

QskProgressIndicator::QskProgressIndicator( QQuickItem* parent )
    : QskProgressIndicator( 0.0, 100.0, parent )
{
}

QskProgressIndicator::QskProgressIndicator( const QskIntervalF& boundaries, QQuickItem* parent )
    : QskProgressIndicator( boundaries.lowerBound(), boundaries.upperBound(), parent )
{
}

QskProgressIndicator::~QskProgressIndicator()
{
    delete m_data->animator;
}

bool QskProgressIndicator::isIndeterminate() const
{
    return m_data->isIndeterminate;
}

void QskProgressIndicator::setIndeterminate( bool on )
{
    if ( on == m_data->isIndeterminate )
        return;

    m_data->isIndeterminate = on;
    m_data->updateIndeterminateAnimator( this );

    update();
    Q_EMIT indeterminateChanged( on );
}

void QskProgressIndicator::setFillGradient( const QskGradient& gradient )
{
    setGradientHint( Fill, gradient );
}

void QskProgressIndicator::resetFillGradient()
{
    resetColor( Fill );
}

QskGradient QskProgressIndicator::fillGradient() const
{
    return gradientHint( Fill );
}

void QskProgressIndicator::setExtent( qreal extent )
{
    if ( extent < 0.0 )
        extent = 0.0;

    if ( setMetric( Groove | QskAspect::Size, extent ) )
        Q_EMIT extentChanged( extent );
}

void QskProgressIndicator::resetExtent()
{
    if ( resetMetric( Groove | QskAspect::Size ) )
        Q_EMIT extentChanged( extent() );
}

qreal QskProgressIndicator::extent() const
{
    auto grooveSize = metric( Groove | QskAspect::Size );
    auto fillSize = metric( Fill | QskAspect::Size );
    return qMax( grooveSize, fillSize );
}

void QskProgressIndicator::setOrigin( qreal origin )
{
    if ( isComponentComplete() )
        origin = boundedValue( origin );

    if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) )
    {
        m_data->hasOrigin = true;
        m_data->origin = origin;

        update();
        Q_EMIT originChanged( origin );
    }
}

void QskProgressIndicator::resetOrigin()
{
    if ( m_data->hasOrigin )
    {
        m_data->hasOrigin = false;

        update();
        Q_EMIT originChanged( origin() );
    }
}

qreal QskProgressIndicator::origin() const
{
    if ( m_data->hasOrigin )
    {
        return boundedValue( m_data->origin );
    }

    return minimum();
}

void QskProgressIndicator::setValue( qreal value )
{
    if ( isComponentComplete() )
        value = boundedValue( value );

    setValueInternal( value );
}

qreal QskProgressIndicator::value() const
{
    return m_data->value;
}

void QskProgressIndicator::setValueAsRatio( qreal ratio )
{
    ratio = qBound( 0.0, ratio, 1.0 );
    setValue( minimum() + ratio * boundaryLength() );
}

qreal QskProgressIndicator::valueAsRatio() const
{
    return valueAsRatio( m_data->value );
}

void QskProgressIndicator::componentComplete()
{
    Inherited::componentComplete();
    adjustValue();
}

void QskProgressIndicator::adjustValue()
{
    if ( isComponentComplete() )
        setValueInternal( boundedValue( m_data->value ) );
}

void QskProgressIndicator::setValueInternal( qreal value )
{
    if ( !qskFuzzyCompare( value, m_data->value ) )
    {
        m_data->value = value;
        Q_EMIT valueChanged( value );

        update();
    }
}

void QskProgressIndicator::itemChange( QQuickItem::ItemChange change,
    const QQuickItem::ItemChangeData& value )
{
    switch( static_cast< int >( change ) )
    {
        case QQuickItem::ItemVisibleHasChanged:
        case QQuickItem::ItemSceneChange:
        {
            m_data->updateIndeterminateAnimator( this );
            break;
        }
    }

    Inherited::itemChange( change, value );
}

#include "moc_QskProgressIndicator.cpp"
