/****************************************************************************************
 * Copyright (c) 2009 Nikolaj Hald Nielsen <nhn@kde.org>                                *
 *                                                                                      *
 * This program 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 program 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 "InlineEditorWidget.h"

#include "core/support/Debug.h"
#include "moodbar/MoodbarManager.h"
#include "playlist/PlaylistDefines.h"
#include "playlist/layouts/LayoutManager.h"
#include "PrettyItemDelegate.h"
#include "SvgHandler.h"
#include "widgets/kratingwidget.h"

#include <KHBox>
#include <KVBox>

#include <QEvent>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
#include <QPainter>
#include <QPaintEvent>

using namespace Amarok;
using namespace Playlist;

const qreal Playlist::ALBUM_WIDTH = 50.0;
const qreal Playlist::SINGLE_TRACK_ALBUM_WIDTH = 40.0;
const qreal Playlist::MARGIN = 2.0;
const qreal Playlist::MARGINH = 6.0;
const qreal Playlist::MARGINBODY = 1.0;
const qreal Playlist::PADDING = 1.0;

InlineEditorWidget::InlineEditorWidget( QWidget * parent, const QModelIndex &index, PlaylistLayout layout, bool hasHeader )
    : KVBox( parent )
    , m_index( index )
    , m_layout( layout )
    , m_layoutChanged( false )
{
    setContentsMargins( 0, 0, 0, 0 );
    setSpacing( 0 );
    setAutoFillBackground ( false );
    int height = 0;

    QFontMetricsF nfm( font() );
    QFont boldfont( font() );
    boldfont.setBold( true );
    QFontMetricsF bfm( boldfont );

    int s_fontHeight = bfm.height();

    int rowCount = PrettyItemDelegate::rowsForItem( m_index );

    m_headerHeight = 0;

    height = MARGIN * 2 + rowCount * s_fontHeight + ( rowCount - 1 ) * PADDING;
    setFixedHeight( height );
    setFixedWidth( parent->width() );

    if( hasHeader )
        m_headerHeight = ( height * layout.layoutForPart( PlaylistLayout::Head) .rows() ) / rowCount - 1;

    //prevent editor closing when cliking a rating widget or pressing return in a line edit.
    setFocusPolicy( Qt::StrongFocus );

    createChildWidgets();
}

InlineEditorWidget::~InlineEditorWidget()
{
}

void InlineEditorWidget::createChildWidgets()
{
    DEBUG_BLOCK

    debug() << "width: " << width();

    //if we are a head item, create a blank widget to make space for the head info

    if ( m_headerHeight != 0 )
    {
        QWidget * headSpacer = new QWidget( this );
        headSpacer->setFixedHeight( m_headerHeight );
    }

    KHBox * trackBox = new KHBox( this );

    //For now, we don't allow editing of the "head" data, just the body
    LayoutItemConfig config = m_layout.layoutForItem( m_index );

    int rowCount = config.rows();

    if ( rowCount == 0 )
        return;

    int rowHeight = height() / rowCount;

    int imageSize = height() - MARGIN * 2;

    if ( config.showCover() )
    {
        //add a small "spacer" widget to offset the cover a little.
        QWidget * coverSpacer = new QWidget( trackBox );
        coverSpacer->setFixedWidth( MARGINH - MARGIN );

        QModelIndex coverIndex = m_index.model()->index( m_index.row(), CoverImage );
        QPixmap albumPixmap = coverIndex.data( Qt::DisplayRole ).value<QPixmap>();

        if ( albumPixmap.width() > albumPixmap.width() )
            albumPixmap = albumPixmap.scaledToWidth( imageSize );
        else
            albumPixmap = albumPixmap.scaledToHeight( imageSize );

        QModelIndex emblemIndex = m_index.model()->index( m_index.row(), SourceEmblem );
        QPixmap emblemPixmap = emblemIndex.data( Qt::DisplayRole ).value<QPixmap>();

        if ( !albumPixmap.isNull() )
        {
            QPainter painter( &albumPixmap );
            painter.drawPixmap( QRectF( 0, 0, 16, 16 ), emblemPixmap, QRectF( 0, 0 , 16, 16 ) );

            QLabel * coverLabel = new QLabel( trackBox );
            coverLabel->setPixmap( albumPixmap );
            coverLabel->setGeometry( QRect( 0, 0, height(), height() ) );
            coverLabel->setMaximumSize( height(), height() );
            coverLabel->setMargin ( MARGIN );
        }
    }

    KVBox * rowsWidget = new KVBox( trackBox );
    rowsWidget->setContentsMargins( 0, 0, 0, 0 );
    rowsWidget->setSpacing( 0 );

    for ( int i = 0; i < rowCount; i++ )
    {

        QSplitter *rowWidget = new QSplitter( rowsWidget );
        connect( rowWidget, SIGNAL( splitterMoved ( int, int ) ), this, SLOT( splitterMoved( int, int ) ) );

        m_splitterRowMap.insert( rowWidget, i );

        // the Oxygen widget style causes the frame around items too squashed
        const KConfigGroup configGlobale( KGlobal::config(), "General" );
        QString widgetStyle = configGlobale.readEntry( "widgetStyle", QString() );
        if( widgetStyle == "oxygen" ) {
            rowWidget->setContentsMargins( 0, -3, 0, -3 );
        } else {
            rowWidget->setContentsMargins( 0, 0, 0, 0 );
        }

        LayoutItemConfigRow row = config.row( i );

        const int elementCount = row.count();

        //we need to do a quick pass to figure out how much space is left for auto sizing elements
        qreal spareSpace = 1.0;
        int autoSizeElemCount = 0;
        for ( int k = 0; k < elementCount; ++k )
        {
            spareSpace -= row.element( k ).size();
            if ( row.element( k ).size() < 0.001 )
                autoSizeElemCount++;
        }

        qreal spacePerAutoSizeElem = spareSpace / (qreal) autoSizeElemCount;

        int itemIndex = 0;
        for ( int j = 0; j < elementCount; ++j )
        {
            LayoutItemConfigRowElement element = row.element( j );

            int value = element.value();

            QModelIndex textIndex = m_index.model()->index( m_index.row(), value );
            QString text = textIndex.data( Qt::DisplayRole ).toString();
            m_orgValues.insert( value, text );

            qreal itemWidth = 0.0;

            QRectF elementBox;

            qreal size;
            if ( element.size() > 0.0001 )
                size = element.size();
            else
                size = spacePerAutoSizeElem;

            if( size < 0.01 )
                size = 0.01;

            itemWidth = 100 * size;

            //special case for painting the rating...
            if ( value == Rating )
            {
                int rating = textIndex.data( Qt::DisplayRole ).toInt();

                KRatingWidget * ratingWidget = new KRatingWidget( 0 );
                rowWidget->addWidget( ratingWidget );
                rowWidget->setStretchFactor( itemIndex, itemWidth );
                ratingWidget->setRating( rating );
                ratingWidget->setAttribute( Qt::WA_NoMousePropagation, true );

                connect( ratingWidget, SIGNAL( ratingChanged( uint ) ), this, SLOT( ratingValueChanged() ) );

                m_editorRoleMap.insert( ratingWidget, value );

            }
            else if ( value == Divider )
            {
                debug() << "painting divider...";
                QPixmap left = The::svgHandler()->renderSvg(
                        "divider_left",
                        1, rowHeight ,
                        "divider_left" );

                QPixmap right = The::svgHandler()->renderSvg(
                        "divider_right",
                        1, rowHeight,
                        "divider_right" );

                QPixmap dividerPixmap( 2, rowHeight );
                dividerPixmap.fill( Qt::transparent );

                QPainter painter( &dividerPixmap );
                painter.drawPixmap( 0, 0, left );
                painter.drawPixmap( 1, 0, right );

                QLabel * dividerLabel = new QLabel( 0 );
                rowWidget->addWidget( dividerLabel );
                dividerLabel->setPixmap( dividerPixmap );
                dividerLabel->setAlignment( element.alignment() );
                rowWidget->setStretchFactor( itemIndex, itemWidth );
            }
            else if( value == Moodbar )
            {
                //we cannot ask the model for the moodbar directly as we have no
                //way of asking for a specific size. Instead just get the track from
                //the model and ask the moodbar manager ourselves.


                debug() << "painting moodbar in PrettyItemDelegate::paintItem";

                Meta::TrackPtr track = m_index.data( TrackRole ).value<Meta::TrackPtr>();

                if( The::moodbarManager()->hasMoodbar( track ) )
                {
                    QPixmap moodbar = The::moodbarManager()->getMoodbar( track, itemWidth, rowHeight - 8 );

                    QLabel * moodbarLabel = new QLabel( 0 );
                    moodbarLabel->setScaledContents( true );
                    rowWidget->addWidget( moodbarLabel );
                    moodbarLabel->setPixmap( moodbar );
                    rowWidget->setStretchFactor( itemIndex, itemWidth );
                }
            }
            else
            {
                    QLineEdit * edit = new QLineEdit( text, 0 );
                    rowWidget->addWidget( edit );
                    rowWidget->setStretchFactor( itemIndex, itemWidth );
                    edit->setAlignment( element.alignment() );
                    edit->installEventFilter(this);

                    connect( edit, SIGNAL( editingFinished() ), this, SLOT( editValueChanged() ) );

                    //check if this is a column that is editable. If not, make the
                    //line edit read only.
                    if ( !editableColumns.contains( value ) )
                    {
                        edit->setReadOnly( true );
                        edit->setDisabled( true );
                    }

                    m_editorRoleMap.insert( edit, value );
            }

            itemIndex++;
        }
    }
}

QPoint
InlineEditorWidget::centerImage( const QPixmap& pixmap, const QRectF& rect ) const
{
    const qreal pixmapRatio = ( qreal )pixmap.width() / ( qreal )pixmap.height();

    qreal moveByX = 0.0;
    qreal moveByY = 0.0;

    if ( pixmapRatio >= 1 )
        moveByY = ( rect.height() - ( rect.width() / pixmapRatio ) ) / 2.0;
    else
        moveByX = ( rect.width() - ( rect.height() * pixmapRatio ) ) / 2.0;

    return QPoint( moveByX, moveByY );
}

void
InlineEditorWidget::paintEvent( QPaintEvent * event )
{
    QPainter painter( this );
    painter.drawPixmap( 0, 0, The::svgHandler()->renderSvgWithDividers( "track", width(), height(), "track" ) );

    //if this is an editor for a head item, use the delegates paintItem function
    //to draw the head part, which currently cannot be edited this way

    if( m_headerHeight != 0 )
    {
        PrettyItemDelegate delegate;
        QStyleOptionViewItem option;
        option.rect = QRect( 0, 0, width(), m_headerHeight );
        delegate.paintItem( m_layout.layoutForPart( PlaylistLayout::Head ), &painter, option, m_index, true );
    }

    event->accept();
}

void InlineEditorWidget::editValueChanged()
{
    DEBUG_BLOCK

    QObject * senderObject = sender();

    QLineEdit * edit = dynamic_cast<QLineEdit *>( senderObject );
    if( !edit )
        return;

    int role = m_editorRoleMap.value( edit );

    //only save values if something has actually changed.
    if( m_orgValues.value( role ) != edit->text() )
    {
        debug() << "Storing changed value: " << edit->text();
        m_changedValues.insert( role, edit->text() );
    }


}

void InlineEditorWidget::ratingValueChanged()
{
    DEBUG_BLOCK

    KRatingWidget * edit = qobject_cast<KRatingWidget *>( sender() );
    if( !edit )
        return;

    int role = m_editorRoleMap.value( edit );
    m_changedValues.insert( role, QString::number( edit->rating() ) );
}

QMap<int, QString> InlineEditorWidget::changedValues()
{
    DEBUG_BLOCK
    if( m_layoutChanged )
        LayoutManager::instance()->updateCurrentLayout( m_layout );
    return m_changedValues;
}


void InlineEditorWidget::splitterMoved( int pos, int index )
{
    DEBUG_BLOCK

    Q_UNUSED( pos )
    Q_UNUSED( index )

    QSplitter * splitter = dynamic_cast<QSplitter *>( sender() );
    if ( !splitter )
        return;

    int row = m_splitterRowMap.value( splitter );
    debug() << "on row: " << row;

    //first, get total size of all items;
    QList<int> sizes = splitter->sizes();

    int total = 0;
    foreach( int size, sizes )
        total += size;

    //resize all items as the splitters take up some space, so we need to normalize the combined size to 1.
    QList<qreal> newSizes;

    foreach( int size, sizes )
    {
        qreal newSize = (qreal) size / (qreal) total;
        newSizes << newSize;
    }

    LayoutItemConfig itemConfig = m_layout.layoutForItem( m_index );

    LayoutItemConfigRow rowConfig = itemConfig.row( row );

    //and now we rebuild a new layout...
    LayoutItemConfigRow newRowConfig;

    for( int i = 0; i<rowConfig.count(); i++ )
    {
        LayoutItemConfigRowElement element = rowConfig.element( i );
        debug() << "item " << i << " old/new: " << element.size() << "/" << newSizes.at( i );
        element.setSize( newSizes.at( i ) );
        newRowConfig.addElement( element );
    }

    LayoutItemConfig newItemConfig;
    newItemConfig.setActiveIndicatorRow( itemConfig.activeIndicatorRow() );
    newItemConfig.setShowCover( itemConfig.showCover() );

    for( int i = 0; i<itemConfig.rows(); i++ )
    {
        if( i == row )
            newItemConfig.addRow( newRowConfig );
        else
            newItemConfig.addRow( itemConfig.row( i ) );
    }

    m_layout.setLayoutForPart( m_layout.partForItem( m_index ), newItemConfig );

    m_layoutChanged = true;
}

bool
InlineEditorWidget::eventFilter( QObject *obj, QEvent *event )
{
    QList<QWidget *> editWidgets = m_editorRoleMap.keys();

    QWidget * widget = qobject_cast<QWidget *>( obj );

    if( editWidgets.contains( widget ) )
    {
        if( event->type() == QEvent::KeyPress )
        {
            QKeyEvent * keyEvent = static_cast<QKeyEvent *>( event );
            if( keyEvent->key() == Qt::Key_Return )
            {
                debug() << "InlineEditorWidget ate a return press for a child widget";
                if( widget )
                {
                    widget->clearFocus ();
                    debug() << "emitting editingDone!";
                    emit editingDone( this );
                }

                return true;
            }
            else
                return false;
        }
        else
            return false;

    }
    else
        return KVBox::eventFilter( obj, event );
}

#include "InlineEditorWidget.moc"

