/*
 * This file is part of the KDE project
 * Copyright (C) 2001 Martin R. Jones <mjones@kde.org>
 *               2001 Carsten Pfeiffer <pfeiffer@kde.org>
 *               2008 Rafael Fernández López <ereslibre@kde.org>
 *
 * You can Freely distribute this program under the GNU Library General Public
 * License. See the file "COPYING" for the exact licensing terms.
 */

#include "kimagefilepreview.h"

#include <QtGui/QLayout>
#include <QtGui/QLabel>
#include <QtGui/QPainter>
#include <QtGui/QComboBox>
#include <QtGui/QCheckBox>
#include <QtGui/QResizeEvent>
#include <QtCore/QTimer>
#include <QtCore/QTimeLine>

#include <kglobalsettings.h>
#include <kconfig.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <klocale.h>
#include <kfileitem.h>
#include <kio/previewjob.h>
#include <kconfiggroup.h>

#include <config-kfile.h>

/**** KImageFilePreview ****/

class KImageFilePreview::KImageFilePreviewPrivate
{
public:
    KImageFilePreviewPrivate()
        : m_job(0)
        , clear(true)
    {
        m_timeLine = new QTimeLine(150);
        m_timeLine->setCurveShape(QTimeLine::EaseInCurve);
        m_timeLine->setDirection(QTimeLine::Forward);
        m_timeLine->setFrameRange(0, 100);
    }

    ~KImageFilePreviewPrivate()
    {
        delete m_timeLine;
    }

    void _k_slotResult( KJob* );
    void _k_slotFailed( const KFileItem& );
    void _k_slotStepAnimation( int frame );
    void _k_slotFinished( );
    void _k_slotActuallyClear( );

    KUrl currentURL;
    KUrl lastShownURL;
    QLabel *imageLabel;
    KIO::PreviewJob *m_job;
    QTimeLine *m_timeLine;
    QPixmap m_pmCurrent;
    QPixmap m_pmTransition;
    float m_pmCurrentOpacity;
    float m_pmTransitionOpacity;
    bool clear;
};

KImageFilePreview::KImageFilePreview( QWidget *parent )
    : KPreviewWidgetBase(parent), d(new KImageFilePreviewPrivate)
{
    QVBoxLayout *vb = new QVBoxLayout( this );
    vb->setMargin( 0 );

    d->imageLabel = new QLabel(this);
    d->imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    d->imageLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
    vb->addWidget(d->imageLabel);

    setSupportedMimeTypes( KIO::PreviewJob::supportedMimeTypes() );
    setMinimumWidth( 50 );

    connect(d->m_timeLine, SIGNAL(frameChanged(int)), this, SLOT(_k_slotStepAnimation(int)));
    connect(d->m_timeLine, SIGNAL(finished()), this, SLOT(_k_slotFinished()));
}

KImageFilePreview::~KImageFilePreview()
{
    if (d->m_job) {
        d->m_job->kill();
    }

    delete d;
}

void KImageFilePreview::showPreview()
{
    // Pass a copy since clearPreview() will clear currentURL
    KUrl url = d->currentURL;
    showPreview( url, true );
}

// called via KPreviewWidgetBase interface
void KImageFilePreview::showPreview( const KUrl& url )
{
    showPreview( url, false );
}

void KImageFilePreview::showPreview( const KUrl &url, bool force )
{
    if (!url.isValid() ||
        (d->lastShownURL.isValid() &&
         url.equals(d->lastShownURL, KUrl::CompareWithoutTrailingSlash) &&
         d->currentURL.isValid()))
        return;

    d->clear = false;
    d->currentURL = url;
    d->lastShownURL = url;

    int w = d->imageLabel->contentsRect().width() - 4;
    int h = d->imageLabel->contentsRect().height() - 4;

    if (d->m_job) {
        disconnect(d->m_job, SIGNAL(result(KJob *)),
                    this, SLOT( _k_slotResult( KJob * )));
        disconnect(d->m_job, SIGNAL(gotPreview(const KFileItem&,
                                                const QPixmap& )), this,
                SLOT( gotPreview( const KFileItem&, const QPixmap& ) ));

        disconnect(d->m_job, SIGNAL(failed(const KFileItem&)),
                    this, SLOT(_k_slotFailed(const KFileItem&)));

        d->m_job->kill();
    }

    d->m_job = createJob(url, w, h);
    if ( force ) // explicitly requested previews shall always be generated!
        d->m_job->setIgnoreMaximumSize(true);

    connect(d->m_job, SIGNAL(result(KJob *)),
                this, SLOT( _k_slotResult( KJob * )));
    connect(d->m_job, SIGNAL(gotPreview(const KFileItem&,
                                        const QPixmap& )),
                SLOT( gotPreview( const KFileItem&, const QPixmap& ) ));

    connect(d->m_job, SIGNAL(failed(const KFileItem&)),
                this, SLOT(_k_slotFailed(const KFileItem&)));
}

void KImageFilePreview::resizeEvent( QResizeEvent * )
{
    clearPreview();
    d->currentURL = KUrl(); // force this to actually happen
    showPreview( d->lastShownURL );
}

QSize KImageFilePreview::sizeHint() const
{
    return QSize( 100, 200 );
}

KIO::PreviewJob * KImageFilePreview::createJob( const KUrl& url, int w, int h )
{
    if (url.isValid()) {
        KFileItemList items;
        items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, url, true));

        KIO::PreviewJob *previewJob = KIO::filePreview(items, QSize(w, h));
        previewJob->setOverlayIconAlpha(0);
        previewJob->setScaleType(KIO::PreviewJob::Scaled);
        return previewJob;
    } else {
        return 0;
    }
}

void KImageFilePreview::gotPreview( const KFileItem& item, const QPixmap& pm )
{
    if (item.url() == d->currentURL) {  // should always be the case
        if (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
            if (d->m_timeLine->state() == QTimeLine::Running) {
                d->m_timeLine->setCurrentTime(0);
            }

            d->m_pmTransition = pm;
            d->m_pmTransitionOpacity = 0;
            d->m_pmCurrentOpacity = 1;
            d->m_timeLine->setDirection(QTimeLine::Forward);
            d->m_timeLine->start();
        }
        else
        {
            d->imageLabel->setPixmap(pm);
        }
    }
}

void KImageFilePreview::KImageFilePreviewPrivate::_k_slotFailed( const KFileItem& item )
{
    if ( item.isDir() )
        imageLabel->clear();
    else if (item.url() == currentURL) // should always be the case
        imageLabel->setPixmap(SmallIcon( "image-missing", KIconLoader::SizeLarge,
                                         KIconLoader::DisabledState ));
}

void KImageFilePreview::KImageFilePreviewPrivate::_k_slotResult( KJob *job )
{
    if (job == m_job) {
        m_job = 0L;
    }
}

void KImageFilePreview::KImageFilePreviewPrivate::_k_slotStepAnimation( int frame )
{
    Q_UNUSED(frame)

    QPixmap pm(QSize(qMax(m_pmCurrent.size().width(), m_pmTransition.size().width()),
                     qMax(m_pmCurrent.size().height(), m_pmTransition.size().height())));
    pm.fill(Qt::transparent);

    QPainter p(&pm);
    p.setOpacity(m_pmCurrentOpacity);

    //If we have a current pixmap
    if (!m_pmCurrent.isNull())
        p.drawPixmap(QPoint(((float) pm.size().width() - m_pmCurrent.size().width()) / 2.0,
                        ((float) pm.size().height() - m_pmCurrent.size().height()) / 2.0), m_pmCurrent);
    if (!m_pmTransition.isNull()) {
        p.setOpacity(m_pmTransitionOpacity);
        p.drawPixmap(QPoint(((float) pm.size().width() - m_pmTransition.size().width()) / 2.0,
                            ((float) pm.size().height() - m_pmTransition.size().height()) / 2.0), m_pmTransition);
    }
    p.end();

    imageLabel->setPixmap(pm);

    m_pmCurrentOpacity = qMax(m_pmCurrentOpacity - 0.4, 0.0); // krazy:exclude=qminmax
    m_pmTransitionOpacity = qMin(m_pmTransitionOpacity + 0.4, 1.0); //krazy:exclude=qminmax
}

void KImageFilePreview::KImageFilePreviewPrivate::_k_slotFinished()
{
    m_pmCurrent = m_pmTransition;
    m_pmTransitionOpacity = 0;
    m_pmCurrentOpacity = 1;
    m_pmTransition = QPixmap();
    // The animation might have lost some frames. Be sure that if the last one
    // was dropped, the last image shown is the opaque one.
    imageLabel->setPixmap(m_pmCurrent);
    clear = false;
}

void KImageFilePreview::clearPreview()
{
    if (d->m_job) {
        d->m_job->kill();
        d->m_job = 0L;
    }

    if (d->clear || d->m_timeLine->state() == QTimeLine::Running) {
        return;
    }

    if (KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects) {
        d->m_pmTransition = QPixmap();
        //If we add a previous preview then we run the animation
        if (!d->m_pmCurrent.isNull()) {
            d->m_timeLine->setCurrentTime(0);
            d->m_timeLine->setDirection(QTimeLine::Backward);
            d->m_timeLine->start();
        }
        d->currentURL = KUrl();
        d->clear = true;
    }
    else
    {
        d->imageLabel->clear();
    }
}

#include "kimagefilepreview.moc"
