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

#include "Legend.h"

#include <QwtLegendData>
#include <QwtText>
#include <QwtPlotItem>

#include <QTreeView>
#include <QLayout>
#include <QStyle>
#include <QStandardItemModel>
#include <QItemDelegate>
#include <QPainter>
#include <QMargins>

static void qwtRenderBackground( QPainter* painter,
    const QRectF& rect, const QWidget* widget )
{
    if ( widget->testAttribute( Qt::WA_StyledBackground ) )
    {
        QStyleOption opt;
        opt.initFrom( widget );
        opt.rect = rect.toAlignedRect();

        widget->style()->drawPrimitive(
            QStyle::PE_Widget, &opt, painter, widget);
    }
    else
    {
        const QBrush brush =
            widget->palette().brush( widget->backgroundRole() );

        painter->fillRect( rect, brush );
    }
}

class LegendTreeView : public QTreeView
{
  public:
    LegendTreeView( Legend* );

    QStandardItem* rootItem( int rtti );
    QStandardItem* insertRootItem( int rtti );

    QList< QStandardItem* > itemList( const QwtPlotItem* );

    virtual QSize sizeHint() const QWT_OVERRIDE;
    virtual QSize minimumSizeHint() const QWT_OVERRIDE;
};

LegendTreeView::LegendTreeView( Legend* legend )
    : QTreeView( legend )
{
    setFrameStyle( NoFrame );
    viewport()->setBackgroundRole( QPalette::Window );
    viewport()->setAutoFillBackground( false );

    setRootIsDecorated( true );
    setHeaderHidden( true );

    QStandardItemModel* model = new QStandardItemModel();

    setModel( model );

    // we want unstyled items
    setItemDelegate( new QItemDelegate( this ) );
}

QStandardItem* LegendTreeView::rootItem( int rtti )
{
    QStandardItemModel* mdl =
        qobject_cast< QStandardItemModel* >( model() );

    for ( int row = 0; row < mdl->rowCount(); row++ )
    {
        QStandardItem* item = mdl->item( row );
        if ( item->data() == rtti )
            return item;
    }

    return NULL;
}

QList< QStandardItem* > LegendTreeView::itemList(
    const QwtPlotItem* plotItem )
{
    QList< QStandardItem* > itemList;

    const QStandardItem* rootItem = this->rootItem( plotItem->rtti() );
    if ( rootItem )
    {
        for ( int i = 0; i < rootItem->rowCount(); i++ )
        {
            QStandardItem* item = rootItem->child( i );

            const QVariant key = item->data();

            if ( key.canConvert< qlonglong >() )
            {
                const qlonglong ptr = key.value< qlonglong >();
                if ( ptr == qlonglong( plotItem ) )
                    itemList += item;
            }
        }
    }

    return itemList;
}

QStandardItem* LegendTreeView::insertRootItem( int rtti )
{
    QStandardItem* item = new QStandardItem();
    item->setEditable( false );
    item->setData( rtti );

    switch( rtti )
    {
        case QwtPlotItem::Rtti_PlotTradingCurve:
        {
            item->setText( "Curves" );
            break;
        }
        case QwtPlotItem::Rtti_PlotZone:
        {
            item->setText( "Zones" );
            break;
        }
        case QwtPlotItem::Rtti_PlotMarker:
        {
            item->setText( "Events" );
            break;
        }
        default:
            break;
    }

    QStandardItemModel* mdl =
        qobject_cast< QStandardItemModel* >( model() );

    mdl->appendRow( item );
    setExpanded( mdl->index( mdl->rowCount() - 1, 0 ), true );

    return item;
}

QSize LegendTreeView::minimumSizeHint() const
{
    return QSize( -1, -1 );
}

QSize LegendTreeView::sizeHint() const
{
    QStyleOptionViewItem styleOption;
    styleOption.initFrom( this );

    const QAbstractItemDelegate* delegate = itemDelegate();

    const QStandardItemModel* mdl =
        qobject_cast< const QStandardItemModel* >( model() );

    int w = 0;
    int h = 0;

    for ( int row = 0; row < mdl->rowCount(); row++ )
    {
        const QStandardItem* rootItem = mdl->item( row );

        int wRow = 0;
        for ( int i = 0; i < rootItem->rowCount(); i++ )
        {
            const QSize hint = delegate->sizeHint( styleOption,
                rootItem->child( i )->index() );

            wRow = qMax( wRow, hint.width() );
            h += hint.height();
        }

        const QSize rootHint = delegate->sizeHint(
            styleOption, rootItem->index() );

        wRow = qMax( wRow + indentation(), rootHint.width() );
        if ( wRow > w )
            w = wRow;

        if ( rootIsDecorated() )
            w += indentation();

        h += rootHint.height();
    }

    const QMargins m = contentsMargins();

    w += m.left() + m.right();
    h += m.top() + m.bottom();

    return QSize( w, h );
}

Legend::Legend( QWidget* parent )
    : QwtAbstractLegend( parent )
{
    m_treeView = new LegendTreeView( this );

    QVBoxLayout* layout = new QVBoxLayout( this );
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->addWidget( m_treeView );

    connect( m_treeView, SIGNAL(clicked(const QModelIndex&)),
        this, SLOT(handleClick(const QModelIndex&)) );
}

Legend::~Legend()
{
}

void Legend::renderLegend( QPainter* painter,
    const QRectF& rect, bool fillBackground ) const
{
    if ( fillBackground )
    {
        if ( autoFillBackground() ||
            testAttribute( Qt::WA_StyledBackground ) )
        {
            qwtRenderBackground( painter, rect, m_treeView );
        }
    }

    QStyleOptionViewItem styleOption;
    styleOption.initFrom( this );
    styleOption.decorationAlignment = Qt::AlignCenter;

    const QAbstractItemDelegate* delegate = m_treeView->itemDelegate();

    const QStandardItemModel* mdl =
        qobject_cast< const QStandardItemModel* >( m_treeView->model() );

    painter->save();
    painter->translate( rect.topLeft() );

    for ( int row = 0; row < mdl->rowCount(); row++ )
    {
        const QStandardItem* rootItem = mdl->item( row );

        styleOption.rect = m_treeView->visualRect( rootItem->index() );
        if ( !styleOption.rect.isEmpty() )
            delegate->paint( painter, styleOption, rootItem->index() );

        for ( int i = 0; i < rootItem->rowCount(); i++ )
        {
            const QStandardItem* item = rootItem->child( i );

            styleOption.rect = m_treeView->visualRect( item->index() );
            if ( !styleOption.rect.isEmpty() )
            {
                delegate->paint( painter, styleOption, item->index() );
            }
        }
    }
    painter->restore();
}

bool Legend::isEmpty() const
{
    return m_treeView->model()->rowCount() == 0;
}

int Legend::scrollExtent( Qt::Orientation orientation ) const
{
    Q_UNUSED( orientation );

    return style()->pixelMetric( QStyle::PM_ScrollBarExtent );
}

void Legend::updateLegend( const QVariant& itemInfo,
    const QList< QwtLegendData >& legendData )
{
    QwtPlotItem* plotItem = qvariant_cast< QwtPlotItem* >( itemInfo );

    QStandardItem* rootItem = m_treeView->rootItem( plotItem->rtti() );
    QList< QStandardItem* > itemList = m_treeView->itemList( plotItem );

    while ( itemList.size() > legendData.size() )
    {
        QStandardItem* item = itemList.takeLast();
        rootItem->removeRow( item->row() );
    }

    if ( !legendData.isEmpty() )
    {
        if ( rootItem == NULL )
            rootItem = m_treeView->insertRootItem( plotItem->rtti() );

        while ( itemList.size() < legendData.size() )
        {
            QStandardItem* item = new QStandardItem();
            item->setEditable( false );
            item->setData( qlonglong( plotItem ) );
            item->setCheckable( true );
            item->setCheckState( plotItem->isVisible() ?
                Qt::Checked : Qt::Unchecked );

            itemList += item;
            rootItem->appendRow( item );
        }

        for ( int i = 0; i < itemList.size(); i++ )
            updateItem( itemList[i], legendData[i] );
    }
    else
    {
        if ( rootItem && rootItem->rowCount() == 0 )
            m_treeView->model()->removeRow( rootItem->row() );
    }

    m_treeView->updateGeometry();
}

void Legend::updateItem( QStandardItem* item, const QwtLegendData& legendData )
{
    const QVariant titleValue = legendData.value( QwtLegendData::TitleRole );

    QwtText title;
    if ( titleValue.canConvert< QwtText >() )
    {
        item->setText( title.text() );
        title = titleValue.value< QwtText >();
    }
    else if ( titleValue.canConvert< QString >() )
    {
        title.setText( titleValue.value< QString >() );
    }
    item->setText( title.text() );

    const QVariant iconValue = legendData.value( QwtLegendData::IconRole );

    QPixmap pm;
    if ( iconValue.canConvert< QPixmap >() )
        pm = iconValue.value< QPixmap >();

    item->setData(pm, Qt::DecorationRole);
}

void Legend::handleClick( const QModelIndex& index )
{
    const QStandardItemModel* model =
        qobject_cast< QStandardItemModel* >( m_treeView->model() );

    const QStandardItem* item = model->itemFromIndex( index );
    if ( item->isCheckable() )
    {
        const qlonglong ptr = item->data().value< qlonglong >();

        Q_EMIT checked( (QwtPlotItem*)ptr,
            item->checkState() == Qt::Checked, 0 );
    }
}

#include "moc_Legend.cpp"
