/*****************************************************************************
 * Qwt Polar Examples - Copyright (C) 2008   Uwe Rathmann
 * This file may be used under the terms of the 3-clause BSD License
 *****************************************************************************/

#include "Plot.h"

#include <QwtSeriesData>
#include <QwtSymbol>
#include <QwtLegend>
#include <QwtPolarGrid>
#include <QwtPolarCurve>
#include <QwtPolarMarker>
#include <QwtScaleEngine>

#include <QPen>

static const QwtInterval s_radialInterval( 0.0, 10.0 );
static const QwtInterval s_azimuthInterval( 0.0, 360.0 );

namespace
{
    class Data : public QwtSeriesData< QwtPointPolar >
    {
      public:
        Data( const QwtInterval& radialInterval,
                const QwtInterval& azimuthInterval, size_t size )
            : m_radialInterval( radialInterval )
            , m_azimuthInterval( azimuthInterval )
            , m_size( size )
        {
        }

        virtual size_t size() const QWT_OVERRIDE
        {
            return m_size;
        }

      protected:
        QwtInterval m_radialInterval;
        QwtInterval m_azimuthInterval;
        size_t m_size;
    };

    class SpiralData : public Data
    {
      public:
        SpiralData( const QwtInterval& radialInterval,
                const QwtInterval& azimuthInterval, size_t size )
            : Data( radialInterval, azimuthInterval, size )
        {
        }

        virtual QwtPointPolar sample( size_t i ) const QWT_OVERRIDE
        {
            const double stepA = 4 * m_azimuthInterval.width() / m_size;
            const double a = m_azimuthInterval.minValue() + i * stepA;

            const double stepR = m_radialInterval.width() / m_size;
            const double r = m_radialInterval.minValue() + i * stepR;

            return QwtPointPolar( a, r );
        }

        virtual QRectF boundingRect() const QWT_OVERRIDE
        {
            if ( cachedBoundingRect.width() < 0.0 )
                cachedBoundingRect = qwtBoundingRect( *this );

            return cachedBoundingRect;
        }
    };

    class RoseData : public Data
    {
      public:
        RoseData( const QwtInterval& radialInterval,
                const QwtInterval& azimuthInterval, size_t size )
            : Data( radialInterval, azimuthInterval, size )
        {
        }

        virtual QwtPointPolar sample( size_t i ) const QWT_OVERRIDE
        {
            const double stepA = m_azimuthInterval.width() / m_size;
            const double a = m_azimuthInterval.minValue() + i * stepA;

            const double d = a / 360.0 * M_PI;
            const double r = m_radialInterval.maxValue() * qAbs( qSin( 4 * d ) );

            return QwtPointPolar( a, r );
        }

        virtual QRectF boundingRect() const QWT_OVERRIDE
        {
            if ( cachedBoundingRect.width() < 0.0 )
                cachedBoundingRect = qwtBoundingRect( *this );

            return cachedBoundingRect;
        }
    };
}

Plot::Plot( QWidget* parent )
    : QwtPolarPlot( QwtText( "Polar Plot Demo" ), parent )
{
    setAutoReplot( false );
    setPlotBackground( Qt::darkBlue );

    // scales
    setScale( QwtPolar::Azimuth,
        s_azimuthInterval.minValue(), s_azimuthInterval.maxValue(),
        s_azimuthInterval.width() / 12 );

    setScaleMaxMinor( QwtPolar::Azimuth, 2 );
    setScale( QwtPolar::Radius,
        s_radialInterval.minValue(), s_radialInterval.maxValue() );

    // grids, axes

    m_grid = new QwtPolarGrid();
    m_grid->setPen( QPen( Qt::white ) );
    for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
    {
        m_grid->showGrid( scaleId );
        m_grid->showMinorGrid( scaleId );

        QPen minorPen( Qt::gray );
#if 0
        minorPen.setStyle( Qt::DotLine );
#endif
        m_grid->setMinorGridPen( scaleId, minorPen );
    }
    m_grid->setAxisPen( QwtPolar::AxisAzimuth, QPen( Qt::black ) );

    m_grid->showAxis( QwtPolar::AxisAzimuth, true );
    m_grid->showAxis( QwtPolar::AxisLeft, false );
    m_grid->showAxis( QwtPolar::AxisRight, true );
    m_grid->showAxis( QwtPolar::AxisTop, true );
    m_grid->showAxis( QwtPolar::AxisBottom, false );
    m_grid->showGrid( QwtPolar::Azimuth, true );
    m_grid->showGrid( QwtPolar::Radius, true );
    m_grid->attach( this );

    // curves

    for ( int curveId = 0; curveId < PlotSettings::NumCurves; curveId++ )
    {
        m_curve[curveId] = createCurve( curveId );
        m_curve[curveId]->attach( this );
    }

    // markers
    QwtPolarMarker* marker = new QwtPolarMarker();
    marker->setPosition( QwtPointPolar( 57.3, 4.72 ) );
    marker->setSymbol( new QwtSymbol( QwtSymbol::Ellipse,
        QBrush( Qt::white ), QPen( Qt::green ), QSize( 9, 9 ) ) );
    marker->setLabelAlignment( Qt::AlignHCenter | Qt::AlignTop );

    QwtText text( "Marker" );
    text.setColor( Qt::black );
    QColor bg( Qt::white );
    bg.setAlpha( 200 );
    text.setBackgroundBrush( QBrush( bg ) );

    marker->setLabel( text );
    marker->attach( this );

    QwtLegend* legend = new QwtLegend;
    insertLegend( legend,  QwtPolarPlot::BottomLegend );
}

PlotSettings Plot::settings() const
{
    PlotSettings s;
    for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
    {
        s.flags[PlotSettings::MajorGridBegin + scaleId] =
            m_grid->isGridVisible( scaleId );
        s.flags[PlotSettings::MinorGridBegin + scaleId] =
            m_grid->isMinorGridVisible( scaleId );
    }
    for ( int axisId = 0; axisId < QwtPolar::AxesCount; axisId++ )
    {
        s.flags[PlotSettings::AxisBegin + axisId] =
            m_grid->isAxisVisible( axisId );
    }

    s.flags[PlotSettings::AutoScaling] =
        m_grid->testGridAttribute( QwtPolarGrid::AutoScaling );

    s.flags[PlotSettings::Logarithmic] =
        scaleEngine( QwtPolar::Radius )->transformation();

    const QwtScaleDiv* sd = scaleDiv( QwtPolar::Radius );
    s.flags[PlotSettings::Inverted] = sd->lowerBound() > sd->upperBound();

    s.flags[PlotSettings::Antialiasing] =
        m_grid->testRenderHint( QwtPolarItem::RenderAntialiased );

    for ( int curveId = 0; curveId < PlotSettings::NumCurves; curveId++ )
    {
        s.flags[PlotSettings::CurveBegin + curveId] =
            m_curve[curveId]->isVisible();
    }

    return s;
}

void Plot::applySettings( const PlotSettings& s )
{
    for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
    {
        m_grid->showGrid( scaleId,
            s.flags[PlotSettings::MajorGridBegin + scaleId] );
        m_grid->showMinorGrid( scaleId,
            s.flags[PlotSettings::MinorGridBegin + scaleId] );
    }

    for ( int axisId = 0; axisId < QwtPolar::AxesCount; axisId++ )
    {
        m_grid->showAxis( axisId,
            s.flags[PlotSettings::AxisBegin + axisId] );
    }

    m_grid->setGridAttribute( QwtPolarGrid::AutoScaling,
        s.flags[PlotSettings::AutoScaling] );

    const QwtInterval interval =
        scaleDiv( QwtPolar::Radius )->interval().normalized();
    if ( s.flags[PlotSettings::Inverted] )
    {
        setScale( QwtPolar::Radius,
            interval.maxValue(), interval.minValue() );
    }
    else
    {
        setScale( QwtPolar::Radius,
            interval.minValue(), interval.maxValue() );
    }

    if ( s.flags[PlotSettings::Logarithmic] )
    {
        setScaleEngine( QwtPolar::Radius, new QwtLogScaleEngine() );
    }
    else
    {
        setScaleEngine( QwtPolar::Radius, new QwtLinearScaleEngine() );
    }

    m_grid->setRenderHint( QwtPolarItem::RenderAntialiased,
        s.flags[PlotSettings::Antialiasing] );

    for ( int curveId = 0; curveId < PlotSettings::NumCurves; curveId++ )
    {
        m_curve[curveId]->setRenderHint( QwtPolarItem::RenderAntialiased,
            s.flags[PlotSettings::Antialiasing] );
        m_curve[curveId]->setVisible(
            s.flags[PlotSettings::CurveBegin + curveId] );
    }

    replot();
}

QwtPolarCurve* Plot::createCurve( int id ) const
{
    const int numPoints = 200;

    QwtPolarCurve* curve = new QwtPolarCurve();
    curve->setStyle( QwtPolarCurve::Lines );
    //curve->setLegendAttribute( QwtPolarCurve::LegendShowLine, true );
    //curve->setLegendAttribute( QwtPolarCurve::LegendShowSymbol, true );

    switch( id )
    {
        case PlotSettings::Spiral:
        {
            curve->setTitle( "Spiral" );
            curve->setPen( QPen( Qt::yellow, 2 ) );
            curve->setSymbol( new QwtSymbol( QwtSymbol::Rect,
                QBrush( Qt::cyan ), QPen( Qt::white ), QSize( 3, 3 ) ) );
            curve->setData(
                new SpiralData( s_radialInterval, s_azimuthInterval, numPoints ) );
            break;
        }
        case PlotSettings::Rose:
        {
            curve->setTitle( "Rose" );
            curve->setPen( QPen( Qt::red, 2 ) );
            curve->setSymbol( new QwtSymbol( QwtSymbol::Rect,
                QBrush( Qt::cyan ), QPen( Qt::white ), QSize( 3, 3 ) ) );
            curve->setData(
                new RoseData( s_radialInterval, s_azimuthInterval, numPoints ) );
            break;
        }
    }
    return curve;
}

#include "moc_Plot.cpp"
