/* ============================================================
 *
 * This file is a part of kipi-plugins project
 * http://www.kipi-plugins.org
 *
 * Date        : 2003-10-01
 * Description : Acquire image dialog
 *
 * Copyright (C) 2003-2008 by Gilles Caulier <caulier dot gilles at gmail dot 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, 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.
 * 
 * ============================================================ */

// C Ansi includes

extern "C"
{
#include <tiffio.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
}

// Include files for TQt

#undef Unsorted // x headers suck - make qdir.h work with --enable-final
#include <tqvbox.h>
#include <tqlayout.h>
#include <tqdir.h>
#include <tqwidget.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqgroupbox.h>
#include <tqstring.h>
#include <tqwhatsthis.h>
#include <tqtextedit.h>
#include <tqimage.h>
#include <tqpixmap.h>
#include <tqcombobox.h>
#include <tqlistbox.h>
#include <tqfile.h>
#include <tqapplication.h>
#include <tqvgroupbox.h>

// Include files for KDE

#include <klocale.h>
#include <klineedit.h>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <knuminput.h>
#include <kinstance.h>
#include <kconfig.h>
#include <klistbox.h>
#include <klineeditdlg.h>
#include <ksqueezedtextlabel.h>
#include <kio/netaccess.h>
#include <kimageio.h>
#include <ktempfile.h>
#include <kdeversion.h>
#include <kdebug.h>
#include <kfiletreeview.h>
#include <kapplication.h>
#include <kaboutdata.h>
#include <khelpmenu.h>
#include <kiconloader.h>
#include <kpopupmenu.h>

// Include files for libKipi.

#include <libkipi/imageinfo.h>

// Local includes

#include "pluginsversion.h"
#include "acquireimagedialog.h"
#include "acquireimagedialog.moc"

namespace KIPIAcquireImagesPlugin
{

// Used by slotOK() method.

#undef NETACCESS_WIDGET
#if KDE_VERSION >= 0x30200
#define NETACCESS_WIDGET , this
#else
#define NETACCESS_WIDGET
#endif

AcquireImageDialog::AcquireImageDialog( KIPI::Interface* interface, TQWidget *parent, const TQImage &img)
                  : KDialogBase( IconList, i18n("Save Target Image Options"), Help|Ok|Cancel,
                    Ok, parent, "AcquireImageDialog", true, false ), m_interface( interface )
{
    KImageIO::registerFormats();
    m_qimageScanned = img;

    setupImageOptions();
    setupAlbumsList();
    readSettings();
    slotImageFormatChanged(m_imagesFormat->currentText());
    page_setupImageOptions->setFocus();
    resize( 600, 400 );

    // About data and help button.

    m_about = new KIPIPlugins::KPAboutData(I18N_NOOP("Acquire images"),
                                           0,
                                           KAboutData::License_GPL,
                                           I18N_NOOP("A Kipi plugin to acquire images"),
                                           "(c) 2003-2008, Gilles Caulier");

    m_about->addAuthor("Gilles Caulier", I18N_NOOP("Author and maintainer"),
                       "caulier dot gilles at gmail dot com");

    m_helpButton = actionButton( Help );
    KHelpMenu* helpMenu = new KHelpMenu(this, m_about, false);
    helpMenu->menu()->removeItemAt(0);
    helpMenu->menu()->insertItem(i18n("Plugin Handbook"), this, TQT_SLOT(slotHelp()), 0, -1, 0);
    m_helpButton->setPopup( helpMenu->menu() );
}

AcquireImageDialog::~AcquireImageDialog()
{
    delete m_about;
}

void AcquireImageDialog::slotHelp()
{
    KApplication::kApplication()->invokeHelp("acquireimages", "kipi-plugins");
}    

void AcquireImageDialog::readSettings(void)
{
    // Read all settings from configuration file.

    m_config = new KConfig("kipirc");
    m_config->setGroup("AcquireImages Settings");
    m_FileName->setText(m_config->readPathEntry("DefaultImageFileName", i18n("image")));
    m_imageCompression->setValue(m_config->readNumEntry("ImageCompression", 75));
    m_imagesFormat->setCurrentText(m_config->readEntry("ImageFormat", "TIFF"));

    delete m_config;
    
    // Get the image files filters from the hosts app.
     
    m_ImagesFilesSort = m_interface->fileExtensions();    
}

void AcquireImageDialog::writeSettings(void)
{
    // Write all settings in configuration file.

    m_config = new KConfig("kipirc");
    m_config->setGroup("AcquireImages Settings");
    m_config->writePathEntry("DefaultImageFileName", m_FileName->text());
    m_config->writeEntry("ImageCompression", m_imageCompression->value());
    m_config->writeEntry("ImageFormat", m_imagesFormat->currentText());
    m_config->sync();
    delete m_config;
}

void AcquireImageDialog::setupImageOptions(void)
{
    TQString whatsThis;

    page_setupImageOptions = addPage( i18n("Target Image"),
                                      i18n("Target Image Options"),
                                      BarIcon("image", KIcon::SizeMedium ) );

    TQVBoxLayout *vlay = new TQVBoxLayout( page_setupImageOptions, 0, spacingHint() );

    //---------------------------------------------

    TQGroupBox * groupBox1 = new TQGroupBox( page_setupImageOptions );
    groupBox1->setFlat(false);
    groupBox1->setTitle(i18n("File Name && Caption"));
    TQWhatsThis::add( groupBox1, i18n("<p>The target image preview with the file name and caption.") );
    TQGridLayout* grid2 = new TQGridLayout( groupBox1, 3, 3 , 20, 10);

    m_ImageFileName = new TQLabel( i18n("File name (without suffix):"), groupBox1);
    grid2->addMultiCellWidget(m_ImageFileName, 0, 0, 0, 3);

    m_FileName = new TQLineEdit(i18n("acquired_image"), groupBox1);
    TQWhatsThis::add( m_FileName, i18n("<p>Enter here the target image file name without suffix "
                                      "(that will be automatically added to the file name according "
                                      "to the file-format option.)") );
    m_ImageFileName->setBuddy(m_FileName);
    grid2->addMultiCellWidget(m_FileName, 1, 1, 0, 3);

    m_ImageComments = new TQLabel( i18n("Caption:"), groupBox1);
    grid2->addMultiCellWidget(m_ImageComments, 2, 2, 0, 3);

    m_CommentsEdit = new TQTextEdit(groupBox1);
    m_CommentsEdit->setMaximumHeight( 200 );
    TQWhatsThis::add( m_CommentsEdit, i18n("<p>Enter here the target image's caption.") );
    grid2->addMultiCellWidget(m_CommentsEdit, 3, 3, 0, 2);

    m_preview = new TQLabel( groupBox1, "preview" );
    m_preview->setFixedHeight( 120 );
    m_preview->setAlignment( TQt::AlignHCenter | TQt::AlignVCenter );
    m_preview->setSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Preferred ) );
    TQWhatsThis::add( m_preview, i18n( "<p>The preview of the target image." ) );
    m_preview->setScaledContents( false );
    TQImage scanned = m_qimageScanned.smoothScale((m_qimageScanned.width() * 100) / m_qimageScanned.height(), 100);
    TQPixmap pix;
    pix.convertFromImage(scanned);
    m_preview->setPixmap(pix);
    grid2->addMultiCellWidget(m_preview, 3, 3, 3, 3);

    vlay->addWidget( groupBox1 );

    //---------------------------------------------

    TQGroupBox * groupBox2 = new TQGroupBox( i18n("Saving Options"), page_setupImageOptions );
    groupBox2->setColumnLayout(0, Qt::Vertical );
    groupBox2->layout()->setSpacing( 6 );
    groupBox2->layout()->setMargin( 11 );
    TQWhatsThis::add( groupBox2, i18n("<p>The saving options of the target image.") );

    TQVBoxLayout * groupBox2Layout = new TQVBoxLayout( groupBox2->layout() );
    groupBox2Layout->setAlignment( TQt::AlignTop );

    m_imageCompression = new KIntNumInput(75, groupBox2);
    m_imageCompression->setRange(1, 100, 1, true );
    m_imageCompression->setLabel( i18n("Image compression:") );
    whatsThis = i18n("<p>The compression value of target image for JPEG and PNG formats:<p>");
    whatsThis = whatsThis + i18n("<b>1</b>: very high compression<p>"
                                 "<b>25</b>: high compression<p>"
                                 "<b>50</b>: medium compression<p>"
                                 "<b>75</b>: low compression (default value)<p>"
                                 "<b>100</b>: no compression");

    TQWhatsThis::add( m_imageCompression, whatsThis);
    groupBox2Layout->addWidget( m_imageCompression );

    TQHBoxLayout *hlay12  = new TQHBoxLayout( );
    groupBox2Layout->addLayout( hlay12 );

    m_imagesFormat = new TQComboBox(false, groupBox2);
    m_imagesFormat->insertItem("JPEG");
    m_imagesFormat->insertItem("PNG");
    m_imagesFormat->insertItem("TIFF");
    m_imagesFormat->insertItem("PPM");
    m_imagesFormat->insertItem("BMP");
    m_imagesFormat->setCurrentText ("TIFF");
    whatsThis = i18n("<p>Select here the target image's file format.<p>");
    whatsThis = whatsThis + i18n("<b>JPEG</b>: The Joint Photographic Experts' Group "
                "file format is a good Web file format but it uses lossy data compression.<p>"
                "<b>PNG</b>: the Portable Network Graphics format is an extensible file format for "
                "the lossless, portable, well-compressed storage of raster images. PNG provides a "
                "patent-free replacement for GIF and can also replace many common uses of TIFF. PNG "
                "is designed to work well in online viewing applications, such as the World Wide Web, "
                "so it is fully streamable with a progressive display option. Also, PNG can store gamma "
                "and chromaticity data for improved color matching on heterogeneous platforms.");
    whatsThis = whatsThis + i18n("<p><b>TIFF</b>: the Tagged Image File Format is a rather old standard "
                "that is still very popular today. It is a highly flexible and platform-independent "
                "format which is supported by numerous image processing applications and by virtually all "
                "prepress software on the market.");
    whatsThis = whatsThis + i18n("<p><b>PPM</b>: the Portable Pixel Map file format is used as an "
                "intermediate format for storing color bitmap information. PPM files may be either "
                "binary or ASCII and store pixel values up to 24 bits in size. This format generates "
                "the biggest-sized text files for encoding images without losing quality.");
    whatsThis = whatsThis + i18n("<p><b>BMP</b>: the BitMaP file format is a popular image format from the "
                "Win32 environment. It efficiently stores mapped or unmapped RGB graphics data with pixels "
                "1-, 4-, 8-, or 24-bits in size. Data may be stored raw or compressed using a 4-bit or "
                "8-bit RLE data compression algorithm. BMP is an excellent choice for a simple bitmap "
                "format which supports a wide range of RGB image data.");
    TQWhatsThis::add( m_imagesFormat, whatsThis );

    m_labelImageFormat = new TQLabel( i18n("Image file format:"), groupBox2);
    hlay12->addWidget( m_labelImageFormat );
    m_labelImageFormat->setBuddy( m_imagesFormat );
    hlay12->addStretch( 1 );
    hlay12->addWidget( m_imagesFormat );

    vlay->addWidget( groupBox2 );
    vlay->addStretch(1);

    //---------------------------------------------

    connect(m_imagesFormat, TQT_SIGNAL(activated(const TQString &)),
            this, TQT_SLOT(slotImageFormatChanged(const TQString &)));
}

void AcquireImageDialog::setupAlbumsList(void)
{
    TQString whatsThis;

    page_setupAlbumsList = addPage( i18n("Selection"),
                                    i18n("Album selection"),
                                    BarIcon("folder_image", KIcon::SizeMedium ) );

    TQVBoxLayout *vlay = new TQVBoxLayout( page_setupAlbumsList, 0, spacingHint() );

    //---------------------------------------------

    TQVGroupBox * groupBox1 = new TQVGroupBox( i18n("Select Folder in Which to Save Target Image"),
                                             page_setupAlbumsList );
    
    m_uploadPath = new KIPI::UploadWidget( m_interface, groupBox1, "m_uploadPath" );

    TQWidget* w = new TQWidget( groupBox1 );
    TQHBoxLayout* hlay = new TQHBoxLayout( w, 6 );
    hlay->addStretch( 1 );

    m_addNewAlbumButton = new TQPushButton (i18n( "&Add New Folder"), w, "PushButton_AddNewAlbum");
    hlay->addWidget( m_addNewAlbumButton );
    TQWhatsThis::add( m_addNewAlbumButton, i18n( "<p>Add a new folder."));
    
    vlay->addWidget( groupBox1 );

    //---------------------------------------------

    TQGroupBox * groupBox2 = new TQGroupBox( i18n("Album Description"), page_setupAlbumsList );
    groupBox2->setColumnLayout(0, Qt::Vertical );
    groupBox2->layout()->setSpacing( 6 );
    groupBox2->layout()->setMargin( 11 );
    TQWhatsThis::add( groupBox2, i18n("<p>The description of the current Album in the selection list.") );

    TQVBoxLayout * groupBox2Layout = new TQVBoxLayout( groupBox2->layout() );
    groupBox2Layout->setAlignment( TQt::AlignTop );

    m_AlbumComments = new KSqueezedTextLabel( groupBox2 );
    m_AlbumComments->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter ) );
    groupBox2Layout->addWidget( m_AlbumComments );

    m_AlbumCollection = new KSqueezedTextLabel( groupBox2 );
    m_AlbumCollection->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter ) );
    groupBox2Layout->addWidget( m_AlbumCollection );

    m_AlbumDate = new KSqueezedTextLabel( groupBox2 );
    m_AlbumDate->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter ) );
    groupBox2Layout->addWidget( m_AlbumDate );

    m_AlbumItems = new KSqueezedTextLabel( groupBox2 );
    m_AlbumItems->setAlignment( int( TQLabel::WordBreak | TQLabel::AlignVCenter ) );
    groupBox2Layout->addWidget( m_AlbumItems );

    vlay->addWidget( groupBox2 );
    
    if ( !m_interface->hasFeature( KIPI::AlbumsHaveComments) )
        groupBox2->hide();
    else
        vlay->addStretch(1);
            
    //---------------------------------------------

    connect(m_addNewAlbumButton, TQT_SIGNAL(clicked()),
            m_uploadPath, TQT_SLOT(mkdir()));

    connect(m_uploadPath, TQT_SIGNAL( folderItemSelected( const KURL & ) ),
            this, TQT_SLOT( slotAlbumSelected( const KURL & )));
    
    //---------------------------------------------
                    
    slotAlbumSelected( m_uploadPath->path() );           
 }

void AcquireImageDialog::slotAlbumSelected( const KURL &url )
{
    TQString comments, category, date, items;
    TQValueList<KIPI::ImageCollection> albums = m_interface->allAlbums();
    TQValueList<KIPI::ImageCollection>::Iterator albumIt;
    
    for( albumIt = albums.begin() ; albumIt != albums.end() ; ++albumIt )
    {
       if ( (*albumIt).path() == url ) 
          break;
    }
    
    if (albumIt != albums.end())
    { 
       comments = (*albumIt).comment();
       category = (*albumIt).category();
       date     = (*albumIt).date().toString( Qt::LocalDate );
       items.setNum((*albumIt).images().count());
    }
        
    m_AlbumComments->setText( i18n("Caption: %1").arg(comments) );
    m_AlbumCollection->setText( i18n("Collection: %1").arg(category) );
    m_AlbumDate->setText( i18n("Date: %1").arg(date) );
    m_AlbumItems->setText( i18n("Items: %1").arg( items ) );
}

void AcquireImageDialog::slotOk()
{
    // PENDING( aurelien)
    // It would be nice if m_uploadPath kept its value between multiple snapshots.
    KURL url = m_uploadPath->path();
    url.adjustPath(1);
    kdDebug(51001) << k_funcinfo << "path:" << url.prettyURL() << endl;
    
    if (!url.isValid())
    {
        KMessageBox::error(this, i18n("You must select a target album for this image."));
        return;
    }

    if (m_FileName->text().isEmpty())
    {
        KMessageBox::error(this, i18n("You must provide a file name for this image."));
        return;
    }

    writeSettings();

    // Get all scanned image information.
    TQString imageFormat = m_imagesFormat->currentText();
    int imageCompression = m_imageCompression->value();
    TQString Commentsimg = m_CommentsEdit->text();

    // Find an unique url
    TQString fileName = m_FileName->text();
    TQString ext = extension(imageFormat);
    url.setFileName(fileName + ext);

    if (KIO::NetAccess::exists(url, false NETACCESS_WIDGET)) 
    {
        for (int idx = 1; idx < 100 ; ++idx) 
        {
            url.setFileName(TQString("%1_%2%3").arg(fileName).arg(idx).arg(ext));
            kdDebug(51001) << "File already exist. Try to fixed target Url to: " << url.prettyURL() << endl;
            
            if (!KIO::NetAccess::exists(url, false NETACCESS_WIDGET)) 
               break;
        }
    }

    kdDebug(51001) << k_funcinfo << "Saving image as " << url.prettyURL() << endl;

    // Save file
    KTempFile tmp;
    tmp.setAutoDelete(true);
    TQString imagePath;
    if (url.isLocalFile()) 
    {
        imagePath=url.path();
    } 
    else 
    {
        imagePath=tmp.name();
    }

    bool ok=false;
    if (imageFormat=="JPEG" || imageFormat=="PNG") 
    {
        ok = m_qimageScanned.save(imagePath, imageFormat.latin1(), imageCompression);
    }
    else if (imageFormat=="TIFF") 
    {
        ok = TQImageToTiff(m_qimageScanned, imagePath);
    }
    else 
    {
        ok =  m_qimageScanned.save(imagePath, imageFormat.latin1());
    }

    if ( !ok )
    {
       KMessageBox::error(this, i18n("Cannot write image file \"%1\".").arg(imagePath));
       return;
    }

    // Upload the image if necessary
    if ( !url.isLocalFile()) 
    {
        if (!KIO::NetAccess::upload(imagePath, url NETACCESS_WIDGET)) 
        {
           KMessageBox::error(this, i18n("Could not upload image to \"%1\".").arg(url.prettyURL()));
           return;
        }
    }

    // Save the comments for this image.
    TQString err;
    ok = m_interface->addImage( url, err );
    if ( !ok ) 
    {
        KMessageBox::error(this, i18n("<qt>Error when informing the application about the new image. "
                                  "The error was: %1</qt>" ).arg( err ) );
        return;
    }

    KIPI::ImageInfo info = m_interface->info( url );
    info.setDescription( Commentsimg );

    m_interface->refreshImages( KURL::List(url) );
    
    close();
    delete this;
}

void AcquireImageDialog::slotImageFormatChanged(const TQString &string)
{
    if ( string == "JPEG" || string == "PNG" )
       m_imageCompression->setEnabled(true);
    else
       m_imageCompression->setEnabled(false);
}

TQString AcquireImageDialog::extension(const TQString& imageFormat)
{
    if (imageFormat == "PNG")
        return ".png";

    if (imageFormat == "JPEG")
        return ".jpg";

    if (imageFormat == "TIFF")
        return ".tif";

    if (imageFormat == "BMP")
        return ".bmp";

    if (imageFormat == "PPM")
        return ".ppm";

    Q_ASSERT(false);
    return "";
}

bool AcquireImageDialog::TQImageToTiff(const TQImage& image, const TQString& dst)
{
    TIFF               *tif;
    unsigned char      *data;
    int                 x, y;
    TQRgb                rgb;

    tif = TIFFOpen(TQFile::encodeName(dst).data(), "w");
    if ( tif )
    {
        TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, image.width());
        TIFFSetField(tif, TIFFTAG_IMAGELENGTH, image.height());
        TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
        TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
        TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
        TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
        {
            TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
            TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
            TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));

            data = new unsigned char[image.width()*3];
            unsigned char *dptr = 0;

            for (y = 0 ; y < image.height() ; ++y)
            {
                dptr = data;

                for (x = 0 ; x < image.width() ; ++x)
                {
                    rgb = *((uint *)image.scanLine(y) + x);
                    *(dptr++) = tqRed(rgb);
                    *(dptr++) = tqGreen(rgb);
                    *(dptr++) = tqBlue(rgb);
                }

                TIFFWriteScanline(tif, data, y, 0);
            }

            delete [] data;
        }

        TIFFClose(tif);
        return true;
    }

    return false;
}

}  // NameSpace KIPIAcquireImagesPlugin
