/***************************************************************************
                          thumbnailprovider.cpp -  description
                             -------------------
    begin                : Sun Jan 21 2007
    copyright            : (C) 2007 by Pedro Ferreira
                           (C) 2007 by Diederik van der Boor
    email                : pedro.ferreira@fe.up.pt
                           "vdboor" --at-- "codingdomain.com"
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "thumbnailprovider.h"

#include "kmessdebug.h"

#include <QPixmap>
#include <QTextDocument>
#include <QTimer>

#include <KIconLoader>
#include <KIO/PreviewJob>
#include <KMimeType>



/**
 * @brief The constructor
 *
 * Starts a KIO::PreviewJob to get the preview.
 * When the job completed or failed, gotResult() is fired.
 *
 * @param  fileName  Name of the file to request.
 * @param  size      Size of the thumbnails. Windows Live Messenger also uses 96x96 thumbnails for all files.
 *                   Depending on it's "file preview size" setting, it scales the received images down afterwards.
 */
ThumbnailProvider::ThumbnailProvider( const QString &fileName, int size )
: QObject( 0 )
, fileName_( fileName )
, resultError_( false )
, size_( size )
{
#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "requesting thumbnail for" << fileName;
#endif

  // The quick method (also displays a transfer dialog):
  // KIO::NetAccess::download( "thumbnail://" + fileName, tempFile, 0 )
  // thumbnailImage_ = QImage( tempFile );
  // KIO::NetAccess::removeTempFile( tempFile );


  // The complex (but async) method:
  // Wrap file in a file list, use KIO::PreviewJob
  KUrl url( fileName );
  fileList_ = KUrl::List( url );

  // Remove preview for text files
  // TODO: preview should work, but doesn't
  enabledPlugins_ = KIO::PreviewJob::availablePlugins();
  enabledPlugins_.removeAll( "textthumbnail" );

#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "enabled preview plugins: " << enabledPlugins_;
#endif

  // Get file type
  KMimeType::Ptr type = KMimeType::findByUrl( url );

#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "file mime type '" << type->name() << "'";
#endif

  // For some file types, don't generate a preview
  // TODO: improve, files like .h and .cpp have a different mime type.
  if( type->name() == "text/plain" )
  {
#ifdef KMESSDEBUG_THUMBNAILPROVIDER
    kmDebug() << "not creating previews for this file type.";
#endif
    // Make slot call delayed, so clients can still connect to signals first.
    QTimer::singleShot( 0, this, SLOT(slotFailed()) );
    return;
  }

  // Create the preview job
  KIO::PreviewJob *previewJob = KIO::filePreview( fileList_, size, size,
                                                  100,   // 100% alpha
                                                  true,  // scale (default value)
                                                  true,  // save (default value)
                                                  &enabledPlugins_ );

  // Connect signals.
  connect( previewJob, SIGNAL(      gotPreview(const KFileItem&, const QPixmap&) ),
           this,         SLOT(  slotGotPreview(const KFileItem&, const QPixmap&) ));
  connect( previewJob, SIGNAL(          failed(const KFileItem&) ),
           this,         SLOT(      slotFailed(const KFileItem&) ));

  // The KIO::PreviewJob auto deletes itself.
}



/**
 * @brief Indicate whether a thumbnail was created.
 */
bool ThumbnailProvider::isSuccessful() const
{
  return ! resultError_;
}



/**
 * @brief Return the generated image as byte array.
 */
const QByteArray &ThumbnailProvider::getData() const
{
  return thumbnailData_;
}



/**
 * @brief Generate the fallback image.
 *
 * This can be called when isSuccessful() returns false.
 * It overwrites the internal image, so it can be requested with getData() and getImage().
 */
void ThumbnailProvider::generateFallbackImage()
{
  QString      iconTitle( KMimeType::iconNameForUrl( KUrl( fileName_ ) ) );
  KIconLoader *loader    = KIconLoader::global();
  QString      iconPath ( loader->iconPath( iconTitle, -size_, false ) );
  storeImage( QImage( iconPath ) );
}



/**
 * @brief Return the generated image.
 */
const QImage &ThumbnailProvider::getImage() const
{
  return thumbnailImage_;
}



/**
 * Return the HTML image tag.
 */
QString ThumbnailProvider::getImageTag( const QString &altText ) const
{
  if( thumbnailData_.isNull() || thumbnailImage_.isNull() )
  {
    return QString::null;
  }

  QString previewData( thumbnailData_.toBase64() );
  return "<img src=\"data:image/png;base64," + previewData + "\""
         " width=\""  + QString::number( thumbnailImage_.width() )  + "\""
         " height=\"" + QString::number( thumbnailImage_.height() ) + "\""
         " alt=\""    + Qt::escape( altText ) + "\" />";
}



/**
 * @brief Called when a preview is available.
 */
void ThumbnailProvider::slotGotPreview( const KFileItem& /*fileItem*/, const QPixmap &preview )
{
#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "thumbnail generated.";
#endif

  // The KFileItem already gets deleted by KIO

  // Downloads QPixmap from the X server again to a QImage in the client memory.
  // So far this can't be avoided because KIO::PreviewJob already generates a QPixmap.

  // Resize canvas to square size for Windows Live Messenger.
  // This way the image is not chopped by the standard small preview size.
  if( preview.width() != preview.height() )
  {
    int size = qMax( preview.width(), preview.height() );
    // The smooth transformation is slower but better than Qt::FastTransformation
    QPixmap resized = preview.scaled( size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
    storeImage( resized.toImage() );
    
    // Create square canvas with transparent background
//     QImage fixedImage( size, size, QImage::Format_ARGB32 );
//     fixedImage.fill( qRgba( 0xff, 0xff, 0xff, 0xff ) );  // white transparent.

    // Qt bug: the fill() works in stand-alone apps, but not here.
    // Found at openSUSE, Qt 3.3.8, KDE 3.5.6. The following the scanlines with the same color.
//     memset( fixedImage.bits(), 0, size * size * 4 );

    /*
    uint **imageBits = (uint**) fixedImage.jumpTable();
    for( int y = 0; y < fixedImage.height(); y++ )
    {
      uint *scanLine = (uint*) imageBits[y];
      for( int x = 0; x < fixedImage.width(); x++ )
      {
        scanLine[x] = 0;
      }
    }
    */

    // Copy image data centered in new image.
//     bitBlt( &fixedImage, ( size - image.width() ) / 2, ( size - image.height() ) / 2,  // dest, dx, dy
//             &image, 0, 0, -1, -1, 0 );  // src, x, y, width, height, conversion_flags

//     storeImage( fixedImage );
  }
  else
  {
    storeImage( preview.toImage() );
  }

  // Store and emit result
  emit gotResult();
}



/**
 * @brief Called when a preview won't be generated.
 */
void ThumbnailProvider::slotFailed()
{
#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "thumbnail won't not be generated.";
#endif

  resultError_ = true;
  emit gotResult();
}



/**
 * @brief Called when a preview could not be generated.
 */
void ThumbnailProvider::slotFailed( const KFileItem &/*fileItem*/ )
{
#ifdef KMESSDEBUG_THUMBNAILPROVIDER
  kmDebug() << "thumbnail could not be generated.";
#endif

  // The KFileItem already gets deleted by KIO.

  // No file name, get generic file icon instead.
  resultError_ = true;
  emit gotResult();
}



/**
 * @brief Store the image in the instance fields.
 */
void ThumbnailProvider::storeImage( const QImage &image )
{
  // Set state
  resultError_    = false;
  thumbnailImage_ = image;

  // Scale down if needed. Don't scale large images up
  // The client may scale the image down though depending on the settings.
  if( thumbnailImage_.width() > size_ || thumbnailImage_.height() > size_ )
  {
    // The smooth transformation is slower but better than Qt::FastTransformation
    thumbnailImage_ = thumbnailImage_.scaled( size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation );
  }

  // Also save as PNG data for the file transfers.
  QBuffer buffer( &thumbnailData_ );
  buffer.open( QIODevice::WriteOnly );
  thumbnailImage_.save( &buffer, "PNG" );
}



#include "thumbnailprovider.moc"
