#include "legend.h"
#include <qwt_legend_data.h>
#include <qwt_text.h>
#include <qwt_plot_item.h>
#include <qtreeview.h>
#include <qlayout.h>
#include <qstyle.h>
#include <qstandarditemmodel.h>
#include <qitemdelegate.h>
#include <qpainter.h>

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;
    virtual QSize minimumSizeHint() const;
};

LegendTreeView::LegendTreeView( Legend *legend ):
    QTreeView( legend )
{
    setFrameStyle( NoFrame );
    viewport()->setBackgroundRole(QPalette::Background);
    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();
    }

    int left, right, top, bottom;
    getContentsMargins( &left, &top, &right, &bottom );

    w += left + right;
    h += top + bottom;

    return QSize( w, h );
}

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

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

    connect( d_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, d_treeView );
        }
    }

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

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

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

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

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

        styleOption.rect = d_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 = d_treeView->visualRect( item->index() );
            if ( !styleOption.rect.isEmpty() )
            {
                delegate->paint( painter, styleOption, item->index() );
            }
        }
    }
    painter->restore();
}

bool Legend::isEmpty() const
{
    return d_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 = d_treeView->rootItem( plotItem->rtti() );
    QList<QStandardItem *> itemList = d_treeView->itemList( plotItem );

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

    if ( !legendData.isEmpty() )
    {
        if ( rootItem == NULL )
            rootItem = d_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 )
            d_treeView->model()->removeRow( rootItem->row() );
    }

    d_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 *>( d_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 );
    }
}
