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

#include "QskPlotCorridorData.h"

namespace
{
    inline int upperIndex( const QskPlotCorridorData* data, qreal value  )
    {
        const int indexMax = data->count() - 1;

        if ( indexMax < 0 || data->sampleAt( indexMax ).value < value )
            return -1;

        int indexMin = 0;
        int n = indexMax;

        while ( n > 0 )
        {
            const int half = n >> 1;
            const int indexMid = indexMin + half;

            if ( value < data->sampleAt( indexMid ).value )
            {
                n = half;
            }
            else
            {
                indexMin = indexMid + 1;
                n -= half + 1;
            }
        }

        return indexMin;
    }

    QRectF boundingRect( const QskPlotCorridorData* data )
    {
        const auto count = data->count();
        if ( count <= 0 )
            return QRectF();

        auto boundary = data->sampleAt( 0 ).boundary;

        for ( int i = 1; i < count; i++ )
            boundary.unite( data->sampleAt( i ).boundary );

        const auto x1 = data->sampleAt( 0 ).value;
        const auto x2 = data->sampleAt( count - 1 ).value;

        return QRectF( x1, boundary.lowerBound(),
            x2 - x1,  boundary.length() ).normalized();
    }
}

QskPlotCorridorData::QskPlotCorridorData( QObject* parent )
    : QObject( parent )
{
}

QskPlotCorridorData::~QskPlotCorridorData()
{
}

QRectF QskPlotCorridorData::boundingRect() const
{
    if ( m_boundingRect.isNull() )
        m_boundingRect = ::boundingRect( this );

    return m_boundingRect;
}

int QskPlotCorridorData::upperIndex( qreal value ) const
{
    const int n = count();

    if ( n == 0 )
        return -1;

    auto index = ::upperIndex( this, value );
    if ( ( index == -1 ) && ( value == sampleAt( n - 1 ).value ) )
        index = n - 1;

    return index;
}

QskPlotCorridorSample QskPlotCorridorData::interpolatedSample( qreal value ) const
{
    const int n = count();
    if ( n == 0 )
        return QskPlotCorridorSample();

    if ( n == 1 )
        return { value, { 0.0, 0.0 } };
    
    int index = 0;

    if ( n > 2 )
    {
        index = upperIndex( value );
        if ( index > 0 )
            index--;
    }

    const auto s1 = sampleAt( index );
    const auto s2 = sampleAt( index + 1 );

    const auto dv = s2.value - s1.value;
    if ( dv == 0.0 )
        return s2;

    const auto t = ( value - s1.value ) / dv;
    return { value, s1.boundary.interpolated( s2.boundary, t ) };
}

QskPlotCorridorSamples::QskPlotCorridorSamples( QObject* parent )
    : QskPlotCorridorData( parent )
{
}

QskPlotCorridorSamples::QskPlotCorridorSamples(
    const QVector< QskPlotCorridorSample >& samples, QObject* parent )
    : QskPlotCorridorData( parent )
    , m_samples( samples )
{
}

void QskPlotCorridorSamples::setSamples( const QVector< QskPlotCorridorSample >& samples )
{
    m_samples = samples;
    m_boundingRect = QRectF(); // invalidating

    Q_EMIT changed();
}

#include "moc_QskPlotCorridorData.cpp"
