/******************************************************************************
 * QwtPolar Widget Library
 * Copyright (C) 2008   Uwe Rathmann
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the Qwt License, Version 1.0
 *****************************************************************************/

#include "qwt_polar_canvas.h"
#include "qwt_polar_plot.h"
#include "qwt_painter.h"

#include <qpainter.h>
#include <qevent.h>
#include <qpixmap.h>
#include <qstyle.h>
#include <qstyleoption.h>
#ifdef Q_WS_X11
#include <qx11info_x11.h>
#endif

static inline void qwtDrawStyledBackground(
    QWidget* widget, QPainter* painter )
{
    QStyleOption opt;
    opt.initFrom( widget );
    widget->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, widget );
}

static QWidget* qwtBackgroundWidget( QWidget* w )
{
    if ( w->parentWidget() == NULL )
        return w;

    if ( w->autoFillBackground() )
    {
        const QBrush brush = w->palette().brush( w->backgroundRole() );
        if ( brush.color().alpha() > 0 )
            return w;
    }

    if ( w->testAttribute( Qt::WA_StyledBackground ) )
    {
        QImage image( 1, 1, QImage::Format_ARGB32 );
        image.fill( Qt::transparent );

        QPainter painter( &image );
        painter.translate( -w->rect().center() );
        qwtDrawStyledBackground( w, &painter );
        painter.end();

        if ( qAlpha( image.pixel( 0, 0 ) ) != 0 )
            return w;
    }

    return qwtBackgroundWidget( w->parentWidget() );
}

class QwtPolarCanvas::PrivateData
{
  public:
    PrivateData()
        : backingStore( NULL )
    {
    }

    ~PrivateData()
    {
        delete backingStore;
    }

    QwtPolarCanvas::PaintAttributes paintAttributes;
    QPixmap* backingStore;
};

//! Constructor
QwtPolarCanvas::QwtPolarCanvas( QwtPolarPlot* plot )
    : QFrame( plot )
{
    m_data = new PrivateData;

#ifndef QT_NO_CURSOR
    setCursor( Qt::CrossCursor );
#endif
    setFocusPolicy( Qt::WheelFocus );

    setPaintAttribute( BackingStore, true );
}

//! Destructor
QwtPolarCanvas::~QwtPolarCanvas()
{
    delete m_data;
}

//! \return Parent plot widget
QwtPolarPlot* QwtPolarCanvas::plot()
{
    return qobject_cast< QwtPolarPlot* >( parent() );
}

//! \return Parent plot widget
const QwtPolarPlot* QwtPolarCanvas::plot() const
{
    return qobject_cast< QwtPolarPlot* >( parent() );
}

/*!
   \brief Changing the paint attributes

   \param attribute Paint attribute
   \param on On/Off

   The default setting enables BackingStore

   \sa testPaintAttribute(), paintCache()
 */
void QwtPolarCanvas::setPaintAttribute( PaintAttribute attribute, bool on )
{
    if ( bool( m_data->paintAttributes & attribute ) == on )
        return;

    if ( on )
        m_data->paintAttributes |= attribute;
    else
        m_data->paintAttributes &= ~attribute;

    switch( attribute )
    {
        case BackingStore:
        {
            if ( on )
            {
                if ( m_data->backingStore == NULL )
                    m_data->backingStore = new QPixmap();

                if ( isVisible() )
                {
                    const QRect cr = contentsRect();
#if QT_VERSION >= 0x050000
                    *m_data->backingStore = grab( cr );
#else
                    *m_data->backingStore = QPixmap::grabWidget( this, cr );
#endif
                }
            }
            else
            {
                delete m_data->backingStore;
                m_data->backingStore = NULL;
            }
            break;
        }
    }
}

/*!
   Test whether a paint attribute is enabled

   \param attribute Paint attribute
   \return true if the attribute is enabled
   \sa setPaintAttribute()
 */
bool QwtPolarCanvas::testPaintAttribute( PaintAttribute attribute ) const
{
    return ( m_data->paintAttributes & attribute ) != 0;
}

//! \return Backing store, might be null
const QPixmap* QwtPolarCanvas::backingStore() const
{
    return m_data->backingStore;
}

//! Invalidate the internal backing store
void QwtPolarCanvas::invalidateBackingStore()
{
    if ( m_data->backingStore )
        *m_data->backingStore = QPixmap();
}

/*!
   Paint event
   \param event Paint event
 */
void QwtPolarCanvas::paintEvent( QPaintEvent* event )
{
    QPainter painter( this );
    painter.setClipRegion( event->region() );

    if ( ( m_data->paintAttributes & BackingStore )
        && m_data->backingStore != NULL )
    {
        QPixmap& bs = *m_data->backingStore;
        if ( bs.size() != size() )
        {
            bs = QPixmap( size() );
#ifdef Q_WS_X11
            if ( bs.x11Info().screen() != x11Info().screen() )
                bs.x11SetScreen( x11Info().screen() );
#endif

            QPainter p;

            if ( testAttribute( Qt::WA_StyledBackground ) )
            {
                p.begin( &bs );
                qwtDrawStyledBackground( this, &p );
            }
            else
            {
                if ( autoFillBackground() )
                {
                    p.begin( &bs );
                    p.fillRect( rect(), palette().brush( backgroundRole() ) );
                }
                else
                {
                    QWidget* bgWidget = qwtBackgroundWidget( plot() );

                    QwtPainter::fillPixmap( bgWidget, bs,
                        mapTo( bgWidget, rect().topLeft() ) );

                    p.begin( &bs );
                }
            }

            plot()->drawCanvas( &p, contentsRect() );

            if ( frameWidth() > 0 )
                drawFrame( &p );
        }

        painter.drawPixmap( 0, 0, *m_data->backingStore );
    }
    else
    {
        qwtDrawStyledBackground( this, &painter );

        plot()->drawCanvas( &painter, contentsRect() );

        if ( frameWidth() > 0 )
            drawFrame( &painter );
    }
}

/*!
   Resize event
   \param event Resize event
 */
void QwtPolarCanvas::resizeEvent( QResizeEvent* event )
{
    QFrame::resizeEvent( event );

    for ( int scaleId = 0; scaleId < QwtPolar::ScaleCount; scaleId++ )
        plot()->updateScale( scaleId );
}

/*!
    Translate a point from widget into plot coordinates

    \param pos Point in widget coordinates of the plot canvas
    \return Point in plot coordinates

    \sa transform()
 */
QwtPointPolar QwtPolarCanvas::invTransform( const QPoint& pos ) const
{
    const QwtPolarPlot* pl = plot();

    const QwtScaleMap azimuthMap = pl->scaleMap( QwtPolar::Azimuth );
    const QwtScaleMap radialMap = pl->scaleMap( QwtPolar::Radius );

    const QPointF center = pl->plotRect().center();

    double dx = pos.x() - center.x();
    double dy = -( pos.y() - center.y() );

    const QwtPointPolar polarPos = QwtPointPolar( QPoint( dx, dy ) ).normalized();

    double azimuth = azimuthMap.invTransform( polarPos.azimuth() );

    // normalize the azimuth
    double min = azimuthMap.s1();
    double max = azimuthMap.s2();
    if ( max < min )
        qSwap( min, max );

    if ( azimuth < min )
    {
        azimuth += max - min;
    }
    else if ( azimuth > max )
    {
        azimuth -= max - min;
    }

    const double radius = radialMap.invTransform( polarPos.radius() );

    return QwtPointPolar( azimuth, radius );
}

/*!
    Translate a point from plot into widget coordinates

    \param polarPos Point in plot coordinates
    \return Point in widget coordinates
    \sa transform()
 */
QPoint QwtPolarCanvas::transform( const QwtPointPolar& polarPos ) const
{
    const QwtPolarPlot* pl = plot();

    const QwtScaleMap azimuthMap = pl->scaleMap( QwtPolar::Azimuth );
    const QwtScaleMap radialMap = pl->scaleMap( QwtPolar::Radius );

    const double radius = radialMap.transform( polarPos.radius() );
    const double azimuth = azimuthMap.transform( polarPos.azimuth() );

    const QPointF pos = qwtPolar2Pos(
        pl->plotRect().center(), radius, azimuth );

    return pos.toPoint();
}

#if QWT_MOC_INCLUDE
#include "moc_qwt_polar_canvas.cpp"
#endif
