//==============================================
//  copyright            : (C) 2003-2005 by Will Stokes
//==============================================
//  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.
//==============================================

//Systemwide includes
#include <qlabel.h>
#include <qfiledialog.h>
#include <qcheckbox.h>
#include <qlayout.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qlayout.h>
#include <qfileinfo.h>
#include <qmutex.h>
#include <qthread.h>
#include <qevent.h>
#include <qapplication.h>

//Projectwide includes
#include "addPhotosDialog.h"
#include "../../config.h"
#include "../../backend/tools/imageTools.h"

#define MIN_WIDTH 240
#define MIN_HEIGHT 180

#define UPDATE_PREVIEW_DETAILS QEvent::User

//================================
//Qt requires us to pass information for GUI posting from the worker thread to the 
//GUI thread via events as opposed to directly posting the events ourselves. in order
//to update the file preview we'll construct custom UpdatePreviewEvents that contain
//the updated preview image and file details.
class UpdatePreviewEvent : public QCustomEvent
{
public:
  UpdatePreviewEvent( QImage image, QString details ) : QCustomEvent( UPDATE_PREVIEW_DETAILS )
  {
    this->image = image;
    this->details = details;
  }

  //returns the preview image
  QImage getImage() const { return image; }
  
  //returns the file details string
  QString getDetails() const { return details; }

private:
  QImage image;
  QString details;
};
//================================
GeneratePreviewThread::GeneratePreviewThread( FilePreview* previewWidget )
{
  //we'll need to store a previewWidget handle to 
  //posting update events when updates are
  //ready to be shown
  this->previewWidget = previewWidget;

  //by default worker thread isn't busy yet
  updating = false;
  queue = QString::null;
}
//================================
void GeneratePreviewThread::start( QString filename )
{
  //get lock
  lockingMutex.lock();
  
  //if currently animating then append job to queue
  if(updating)
  {
    queue = filename;
    lockingMutex.unlock();
    return; 
  }
  //else set animating to true, actually initiate job
  else
  {
    updating = true;
    this->filename = filename;
    lockingMutex.unlock();
    QThread::start();
  }
}
//================================
void GeneratePreviewThread::run()
{
  //since it is possible for another job
  //to be added to the queue while processing this one, it is necessary
  //to loop until the queue is empty
  while(true)
  {
    //------------------------------------------
    //Get image type extension and convert to caps  
    QString extension = QFileInfo(filename).extension(false).upper();
    bool validExtension = ( (extension.compare("GIF") == 0) ||
                            (extension.compare("JPG") == 0) ||
                            (extension.compare("JPEG") == 0) ||
                            (extension.compare("PNG") == 0) ||
                            (extension.compare("XPM") == 0) );
    //------------------------------------------
    //Scale the image to fit nicely on the screen, aka < 300x225
    QImage scaledImage;
    if( validExtension )
    {
      scaleImage(filename, scaledImage, MIN_WIDTH, MIN_HEIGHT );
    }
    //------------------------------------------
    //Get image resolution
    QString imageRes = "";
    if(validExtension)
    {
      QSize res;
      getImageSize( filename, res );
      imageRes = QString("%1 x %2").arg(res.width()).arg(res.height());
    }
    //------------------------------------------
    //Determine file size and construct a nicely formatted size string
    QString fileSize = "?";
    QFileInfo info;
    info.setFile( filename );
    int sizeOnDisk = info.size();
    
    if(sizeOnDisk < 1024)
      fileSize = QString("%1 Byte%2").arg(sizeOnDisk).arg( sizeOnDisk == 0 || sizeOnDisk > 1 ? "s" : "");
    else if( sizeOnDisk/1024 < 1024)
      //    fileSize = QString("%1 Kb").arg( ((float)*sizeOnDisk)/1024 );
      fileSize = QString("%1 Kb").arg( ((float)((100*sizeOnDisk)/1024))/100 );
    else if( sizeOnDisk/(1024*1024) < 1024)
      fileSize = QString("%1 Mb").arg( ((float)((100*sizeOnDisk)/(1024*1024)))/100 );
    else
      fileSize = QString("%1 Gigs").arg( ((float)((100*sizeOnDisk)/(1024*1024*1024)))/100 );   
    //------------------------------------------
    //Setup image details string  
    QString fileDetails = QString("%1 %2, %3")
                                  .arg(imageRes)
                                  .arg(extension)
                                  .arg(fileSize);
    //------------------------------------------
    //Post UPDATE_PREVIEW_DETAILS event
    UpdatePreviewEvent* upe = new UpdatePreviewEvent( scaledImage, fileDetails );
    QApplication::postEvent( previewWidget, upe );
    //------------------------------------------
    //get lock
    lockingMutex.lock();
    
    //if the queue is empty we're done!
    if( queue.isNull() )
    {
      updating = false;
      lockingMutex.unlock();
      return; 
    }
    //clear queue and process pending job
    else
    {
      filename = queue;
      queue = QString::null;
      lockingMutex.unlock();
    }
    
  } //end while(true)
}
//================================
FilePreview::FilePreview(QWidget* parent) : QWidget(parent) 
{
  //create widgets for display preview image and details
  filePreview = new QLabel( this );  
  fileDetails = new QLabel( this );  
  
  QGridLayout* grid = new QGridLayout( this, 4, 3 );
  grid->setRowStretch( 0, 1 );
  grid->addWidget( filePreview, 1, 1, Qt::AlignHCenter );
  grid->addWidget( fileDetails, 2, 1, Qt::AlignHCenter );
  grid->setRowStretch( 3, 1 );

  grid->setColStretch( 0, 1 );
  grid->setColStretch( 2, 1 );
 
  //create a generator thread that will be used for actually generating 
  //preview images and constructing details strings
  generatorThread = new GeneratePreviewThread(this);
}
//==============================================
FilePreview::~FilePreview()
{
  //make sure generator thread is done!
  generatorThread->wait();
  delete generatorThread;
  generatorThread = NULL;
}
//==============================================
QSize FilePreview::minimumSizeHint () const
{
  QFontMetrics fm( font() );
  return QSize(MIN_WIDTH, MIN_HEIGHT + 2*fm.height() );
}
//==============================================
void FilePreview::customEvent( QCustomEvent * e )
{
  //handle UpdatePrevewEvents that are sent from the worker thread
  //by update the preview image and details that are shown
  if ( e->type() == UPDATE_PREVIEW_DETAILS ) 
  {  
    UpdatePreviewEvent* upe = (UpdatePreviewEvent*)e;

    if( !upe->getImage().isNull() )
    {
      QPixmap scaledPixmap;
      scaledPixmap.convertFromImage( upe->getImage() );
      filePreview->setPixmap( scaledPixmap );
    }
    
    fileDetails->setText( upe->getDetails() );
  }
}
//==============================================
void FilePreview::updatePreview( const QString& filename )
{
  //handle requests to update the preview information by asking
  //the generator thread to handle them. by using
  //an auxiallary thread we can process requests very quickly while
  //any current work being done to generate an image preview continues
  if( generatorThread != NULL)
  {
    generatorThread->start( filename );
  }
}
//==============================================
AddPhotosDialog::AddPhotosDialog(QString path, QWidget *parent, const char* name ) :
                                 QFileDialog(path,
                                 tr("Images") + " (*.gif *.jpg *.jpeg *.png *.xpm *.GIF *.JPG *.JPEG *.PNG *.XPM)",
                                 parent,name)
 {
   //setup filter filter and modes
   setMode( QFileDialog::ExistingFiles );
   setViewMode( QFileDialog::List );
 
   filePreview = new FilePreview();
   setContentsPreviewEnabled( true );
   setContentsPreview( filePreview, filePreview );
   setPreviewMode( QFileDialog::Contents );
   
   //create label and checkbox asking user if they want to
   //set image descriptions from filenames
   setDescriptions = new QCheckBox( tr("Use filenames for descriptions."), this );
   setDescriptions->setChecked( false );
   addWidgets( NULL, setDescriptions, NULL );

   //set window description
  setCaption( tr("Add Photos") );
  
  connect( this, SIGNAL( fileHighlighted(const QString&)),
           this, SLOT( updatePreview(const QString&)) );
}
//==============================================
QStringList AddPhotosDialog::getFilenames(bool& setDescriptionsBool)
{
  if( exec() == QDialog::Accepted )
  {
    setDescriptionsBool = setDescriptions->isChecked();
    return selectedFiles();
  }
  else {  return QStringList(); }
}
//==============================================
void AddPhotosDialog::updatePreview(const QString& filename)
{
  filePreview->updatePreview( filename ); 
}
//==============================================
