/******************************************************************************
 * QSkinny - Copyright (C) 2016 Uwe Rathmann
 * This file may be used under the terms of the QSkinny License, Version 1.0
 *****************************************************************************/

#include "QskRangeControl.h"
#include "QskAspect.h"
#include "QskFunctions.h"

class QskRangeControl::PrivateData
{
public:
    PrivateData():
        minimum( 0.0 ),
        maximum( 1.0 ),
        value( 0.0 ),
        stepSize( 0.1 ),
        pageSize( 1 ),
        snap( false ),
        readOnly( false )
    {
    }

    qreal minimum;
    qreal maximum;
    qreal value;
    qreal stepSize;
    int pageSize;
    bool snap : 1;
    bool readOnly : 1;
};

QskRangeControl::QskRangeControl( QQuickItem* parent ):
    QskControl( parent ),
    m_data( new PrivateData() )
{
    m_data->readOnly = true;
    setReadOnly( false );
}

QskRangeControl::~QskRangeControl()
{
}

void QskRangeControl::setMinimum( qreal minimum )
{
    if ( m_data->minimum == minimum )
        return;

    const auto oldRange = range();

    m_data->minimum = minimum;
    Q_EMIT minimumChanged( minimum );

    if ( isComponentComplete() )
        adjustRangeAndValue( false );

    const auto newRange = range();
    if ( oldRange != newRange )
        Q_EMIT rangeChanged( newRange );

    update();
}

qreal QskRangeControl::minimum() const
{
    return m_data->minimum;
}

void QskRangeControl::setMaximum( qreal maximum )
{
    if ( m_data->maximum == maximum )
        return;

    const auto oldRange = range();

    m_data->maximum = maximum;
    Q_EMIT maximumChanged( maximum );

    if ( isComponentComplete() )
        adjustRangeAndValue( true );

    const auto newRange = range();
    if ( oldRange != newRange )
        Q_EMIT rangeChanged( newRange );

    update();
}

qreal QskRangeControl::maximum() const
{
    return m_data->maximum;
}

void QskRangeControl::adjustRangeAndValue( bool increasing )
{
    if ( m_data->maximum < m_data->minimum )
    {
        if ( increasing )
        {
            m_data->minimum = m_data->maximum;
            Q_EMIT minimumChanged( m_data->minimum );
        }
        else
        {
            m_data->maximum = m_data->minimum;
            Q_EMIT maximumChanged( m_data->maximum );
        }
    }

    qreal newValue = m_data->value;

    if ( m_data->snap && m_data->stepSize != 0.0 )
        newValue = qRound( newValue / m_data->stepSize ) * m_data->stepSize;

    newValue = qBound( m_data->minimum, newValue, m_data->maximum );

    if ( newValue != m_data->value )
    {
        m_data->value = newValue;
        Q_EMIT valueChanged( newValue );
    }
}

qreal QskRangeControl::range() const
{
    return m_data->maximum - m_data->minimum;
}

qreal QskRangeControl::position() const
{
    return ( value() - minimum() ) / range();
}

qreal QskRangeControl::fixupValue( qreal value ) const
{
    return value;
}

void QskRangeControl::setValue( qreal value )
{
    if ( isComponentComplete() )
    {
        if ( m_data->snap && m_data->stepSize != 0.0 )
            value = qRound( value / m_data->stepSize ) * m_data->stepSize;

        value = qBound( m_data->minimum, value, m_data->maximum );
    }

    value = fixupValue( value );

    if ( !qskFuzzyCompare( value, m_data->value ) )
    {
        m_data->value = value;
        Q_EMIT valueChanged( value );

        update();
    }
}

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

void QskRangeControl::setStepSize( qreal stepSize )
{
    if ( qskFuzzyCompare( m_data->stepSize, stepSize ) )
        return;

    m_data->stepSize = stepSize;
    Q_EMIT stepSizeChanged( stepSize );

    update();
}

qreal QskRangeControl::stepSize() const
{
    return m_data->stepSize;
}

void QskRangeControl::setPageSize( int pageSize )
{
    if ( m_data->pageSize == pageSize )
        return;

    m_data->pageSize = pageSize;
    Q_EMIT pageSizeChanged( pageSize );

    update();
}

int QskRangeControl::pageSize() const
{
    return m_data->pageSize;
}

void QskRangeControl::setSnap( bool snap )
{
    if ( m_data->snap == snap )
        return;

    m_data->snap = snap;
    Q_EMIT snapChanged( snap );

    if ( snap )
    {
        // Re-align value
    }
}

bool QskRangeControl::snap() const
{
    return m_data->snap;
}

void QskRangeControl::setReadOnly( bool readOnly )
{
    if ( m_data->readOnly == readOnly )
        return;

    m_data->readOnly = readOnly;

    // we are killing user settings here !!
    setFocusPolicy( readOnly ? Qt::NoFocus : Qt::StrongFocus );
    setAcceptedMouseButtons( readOnly ? Qt::NoButton : Qt::LeftButton );
    setWheelEnabled( m_data->readOnly );

    Q_EMIT readOnlyChanged( readOnly );
}

bool QskRangeControl::isReadOnly() const
{
    return m_data->readOnly;
}

void QskRangeControl::stepUp()
{
    setValue( m_data->value + m_data->stepSize );
}

void QskRangeControl::stepDown()
{
    setValue( m_data->value - m_data->stepSize );
}

void QskRangeControl::pageUp()
{
    setValue( m_data->value + m_data->stepSize * m_data->pageSize );
}

void QskRangeControl::pageDown()
{
    setValue( m_data->value - m_data->stepSize * m_data->pageSize );
}

void QskRangeControl::keyPressEvent( QKeyEvent* event )
{
    if ( !isReadOnly() )
    {
        if ( event->key() == Qt::Key_Up || event->matches( QKeySequence::MoveToNextChar ) )
        {
            stepUp();
            return;
        }

        if ( event->key() == Qt::Key_Down || event->matches( QKeySequence::MoveToPreviousChar ) )
        {
            stepDown();
            return;
        }
    }

    Inherited::keyPressEvent( event );
}

#ifndef QT_NO_WHEELEVENT

void QskRangeControl::wheelEvent( QWheelEvent* event )
{
    if( isReadOnly() )
        return;

    const int steps = event->delta() / QWheelEvent::DefaultDeltasPerStep;
    setValue( m_data->value + steps * m_data->stepSize );
}

#endif

void QskRangeControl::componentComplete()
{
    Inherited::componentComplete();
    adjustRangeAndValue( true );
}

#include "moc_QskRangeControl.cpp"
