/******************************************************************************
*
* Copyright (C) 2002 Hugo PEREIRA <mailto: hugo.pereira@free.fr>
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT
* Any WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program.  If not, see <http://www.gnu.org/licenses/>.
*
*******************************************************************************/

#include "HistogramWidget.h"
#include "Singleton.h"
#include "XmlOptions.h"

#include <QApplication>
#include <QPainter>
#include <QScrollArea>
#include <QScrollBar>

#include <cmath>

//________________________________________________________
HistogramWidget::HistogramWidget( QScrollArea* parent ):
    QWidget( parent ),
    Counter( "HistogramWidget::HistogramWidget" ),
    parent_( parent ),
    divColor_( "DIV_COLOR" ),
    visibleHeight_(0)
{
    Debug::Throw( "HistogramWidget::HistogramWidget.\n" );
    setBackgroundRole( QPalette::Base );
    connect( Singleton::get().application(), SIGNAL(configurationChanged()), SLOT(_updateConfiguration()) );
    _updateConfiguration();
}


//________________________________________________________
void HistogramWidget::_updateConfiguration()
{
    Debug::Throw( "HistogramWidget::_updateConfiguration.\n" );
    divX_ = XmlOptions::get().get<unsigned int>( "DIV_X" );
    divY_ = XmlOptions::get().get<unsigned int>( "DIV_Y" );
    divColor_.update();

    // update datasets colors
    for( DataSet::List::iterator iter = _dataSets().begin(); iter != _dataSets().end(); ++iter )
    { iter->color().update(); }

    update();
}

//________________________________________________________
void HistogramWidget::paintEvent( QPaintEvent* event )
{
    Debug::Throw( "HistogramWidget::paintEvent.\n" );
    QPainter painter( this );
    painter.setClipRect( event->rect() );
    painter.setRenderHint(QPainter::Antialiasing, false);

    // widget usable height
    int height( visibleHeight_ ? visibleHeight_ : HistogramWidget::height() );

    // draw divisions
    QPen pen;
    pen.setJoinStyle( Qt::RoundJoin );
    pen.setWidthF( 1.0 );
    pen.setBrush( divColor_.background() );
    painter.setPen( pen );

    // horizontal divisions
    for( unsigned int i=1; i<divY_; i++ )
    {
        int line = static_cast<int>( double(i*height)/divY_ );
        painter.drawLine( 0, line, width()-1, line );
    }

    // vertical divisions
    for( unsigned int i=1; i<divX_; i++ )
    {
        unsigned int col = static_cast<unsigned int>( double(i*width())/divX_ );
        painter.drawLine( col, 0, col, height-1 );
    }

    painter.setRenderHint(QPainter::Antialiasing, true);

    // adjust values to width
    for( DataSet::List::iterator iter = _dataSets().begin(); iter != _dataSets().end(); ++iter )
    { while( int( iter->values().size()) > width() ) iter->values().erase( iter->values().begin() ); }

    //! get values maximum and ratio
    double maximum(0);
    int index = 0;
    forever
    {
        double value( 0 );
        bool valid( false );
        foreach( const DataSet& dataSet, _dataSets() )
        {
            if( index >= dataSet.values().size() ) continue;
            valid = true;
            value += dataSet.values()[index];

        }

        if( !valid ) break;
        maximum = qMax( maximum, value );
        index ++;
    }

    // if all datasets are empty return
    if( index != 0 )
    {

        double ratio( maximum > 0 ? static_cast<double>( height-1 )/maximum  : 1 );

        //! allocate a polygon for each dataset
        QVector<QPolygonF> polygons( _dataSets().size(), QPolygonF() );

        // fill polygons
        double x_min(0);
        double y_min( height-1 );
        double x( x_min );

        index = 0;
        forever
        {
            bool valid( false );
            double y_max = y_min;
            for( int i = 0; i < _dataSets().size(); i++ )
            {

                if( index >= dataSet(i).values().size() )
                {
                    if( i < _dataSets().size()-1 ) y_max -= qMax( 1.0, ratio*dataSet(i).values().back() );
                    continue;
                }

                valid = true;
                y_max -= qMax( 1.0, ratio*dataSet(i).values()[index] );
                polygons[i] << QPointF( x, y_max );

            }

            if( !valid ) break;
            x+=1;
            index++;
        }

        // close polygons
        for( int i = int( _dataSets().size() ) -1; i>=0 ; i-- )
        {
            if( polygons[i].isEmpty() ) continue;

            if( i==0 )
            {

                polygons[i] << QPointF( polygons[i].back().x(), y_min );
                polygons[i] << QPointF( x_min, y_min );
                polygons[i] << polygons[i].front();

            } else {

                if( !polygons[i-1].isEmpty() )
                {
                    if( polygons[i].size() > polygons[i-1].size() )
                    {
                        polygons[i] << QPointF( polygons[i].back().x(), y_min );
                        polygons[i] << QPointF( polygons[i-1].back().x(), y_min );
                    }

                    for( int index= qMin( polygons[i].size(), polygons[i-1].size() )-1; index >= 0; index-- )
                    { polygons[i] << polygons[i-1][index] + QPointF( 0, -1 ); }

                } else {
                    polygons[i] << QPointF( polygons[i].back().x(), y_min );
                    polygons[i] << QPointF( x_min, y_min );
                }

                polygons[i] << polygons[i].front();

            }

            // draw polygon
            pen.setColor( dataSet(i).color().foreground() );

            painter.setPen( pen );
            painter.setBrush( dataSet(i).color().background() );
            painter.drawPolygon( polygons[i] );

        }

        // draw axis labels
        if( maximum > 0 )
        {

            painter.setPen( divColor_.foreground() );
            int x( parent_->horizontalScrollBar()->value() + parent_->width() - 10 );
            const double interval( maximum/divY_ );
            for( unsigned int i=0; i<divY_; i++ )
            {

                unsigned int y = static_cast<unsigned int>( double(i*height)/divY_ );
                double value = std::floor(0.5 + 100*interval*( divY_ - i ) )/100;

                // calculate text position
                const QString text( QString::number( value ) );
                const QPoint bottomRight( x, y-2 );
                QRect textRect( fontMetrics().boundingRect( text ) );
                textRect.moveBottomRight( bottomRight );

                // render text
                painter.drawText( textRect, Qt::AlignRight|Qt::AlignBottom, text );

            }

        }

    }

    painter.end();
    _paintText( event->rect() );

}

//______________________________________________________________________
void HistogramWidget::resizeEvent( QResizeEvent* event )
{
    Debug::Throw( "HistogramWidget::resizeEvent.\n" );
    for( int i=0; i<_dataSets().size(); i++ )
    { dataSet(i).color().updateGradient( event->size() ); }

    return QWidget::resizeEvent( event );

}

//__________________________________________________________
void HistogramWidget::Color::update( void )
{

    Debug::Throw( "HistogramWidgetWidget::Color::update.\n" );
    QColor color( XmlOptions::get().get<QString>(name_) );
    if( !color.isValid() ) color = QApplication::palette().color( QPalette::WindowText );

    ( foreground_ = color ).setAlpha( XmlOptions::get().get<int>( "TRANSPARENCY_FOREGROUND_INTENSITY" ) );
    ( background_ = color ).setAlpha( XmlOptions::get().get<int>( "TRANSPARENCY_LOAD_INTENSITY" ) );

    // update gradient
    use_gradient_ = XmlOptions::get().get<bool>( "TRANSPARENCY_USE_GRADIENT" );
    gradient_ = QLinearGradient( gradient_.start(), gradient_.finalStop() );
    gradient_.setColorAt(0, background_.light(150) );
    gradient_.setColorAt(1, background_ );

}

//__________________________________________________________
void HistogramWidget::Color::updateGradient( const QSize& size )
{
    Debug::Throw( "HistogramWidgetWidget::Color::updateGradient.\n" );
    gradient_.setFinalStop( 0, size.height() );
}

//__________________________________________________________
void HistogramWidget::_paintText( QRect clip )
{

    QPainter painter( this );
    painter.setRenderHints(QPainter::Antialiasing);
    painter.setClipRect( clip );
    painter.translate( 10 + parent_->horizontalScrollBar()->value(), -10 );

    // bounding rect
    const QRectF boundingRect( QRectF( painter.boundingRect( rect(), Qt::AlignBottom | Qt::AlignLeft, text_ ) )
        .adjusted( -5.5, -5.5, 5.5, 5.5 ) );

    const QColor border( palette().color( QPalette::ToolTipBase ) );
    QColor background( border );
    background.setAlpha( background.alphaF() * 200 );

    painter.setPen( Qt::NoPen );
    painter.setBrush( background );
    painter.drawRoundedRect( boundingRect, 4, 4 );

    painter.setBrush( Qt::NoBrush );
    painter.setPen( QPen( border, 1.1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
    painter.drawRoundedRect( boundingRect, 3.5, 3.5 );

    painter.setPen( palette().color( QPalette::ToolTipText ) );
    painter.drawText( rect(), Qt::AlignBottom | Qt::AlignLeft, text_ );

    painter.end();
}
