/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2004-07-13
 * Description : image editor printing interface.
 *
 * Copyright (C) 2006 by F.J. Cruz <fj.cruz@supercable.es>
 * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com> 
 *
 * KeepRatio and Alignment options imported from Gwenview program.
 * Copyright (c) 2003 Angelo Naselli <anaselli at linux dot it>
 * 
 * Original printing code from Kuickshow program.
 * Copyright (C) 2002 Carsten Pfeiffer <pfeiffer at 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, 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.
 *
 * ============================================================ */

// Qt includes.

#include <qobject.h>
#include <qpixmap.h>
#include <qlayout.h>
#include <qgroupbox.h>
#include <qbuttongroup.h>
#include <qstring.h>
#include <qsize.h>
#include <qcursor.h>
#include <qlabel.h>
#include <qhbox.h>
#include <qcheckbox.h>
#include <qfont.h>
#include <qgrid.h>
#include <qimage.h>
#include <qpaintdevicemetrics.h>
#include <qpainter.h>
#include <qradiobutton.h>
#include <qvbuttongroup.h>
#include <qcolor.h>
#include <qcombobox.h>
#include <qstyle.h>
#include <qpushbutton.h>

// KDE includes.

#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kimageio.h>
#include <kcombobox.h>
#include <kglobalsettings.h>
#include <knuminput.h>
#include <kprinter.h>
#include <ktempfile.h>
#include <kpropertiesdialog.h>

// Local includes.

#include "ddebug.h"
#include "dimg.h"
#include "editorwindow.h"
#include "icctransform.h"
#include "imageprint.h"
#include "imageprint.moc"

namespace Digikam
{

class ImagePrintPrivate
{

public:

    ImagePrintPrivate(){}

    QString filename;
    QString inProfilePath;
    QString outputProfilePath;

    DImg    image;
};

ImagePrint::ImagePrint(DImg& image, KPrinter& printer, const QString& filename)
          : m_printer(printer)
{
    d = new ImagePrintPrivate();
    d->image    = image;
    d->filename = filename;
}

ImagePrint::~ImagePrint()
{
    delete d;
}

bool ImagePrint::printImageWithQt()
{
    if ( d->image.isNull() )
    {
        DWarning() << "Supplied Image for printing is null" << endl;
        return false;
    }

    QString t = "true";
    QString f = "false";

    if (m_printer.option( "app-imageeditor-color-managed") != f)
    {
        IccTransform *transform = new IccTransform();
        readSettings();

        if (d->image.getICCProfil().isNull())
        {
            transform->setProfiles( d->inProfilePath, d->outputProfilePath );
        }
        else
        {
            transform->setProfiles(d->outputProfilePath);
        }
        
        transform->apply( d->image );
    }

    QImage image2Print = d->image.copyQImage();

    // Black & white print ?
    if ( m_printer.option( "app-imageeditor-blackwhite" ) != f)
    {
        image2Print = image2Print.convertDepth( 1, Qt::MonoOnly |
                                                Qt::ThresholdDither |
                                                Qt::AvoidDither );
    }

    QPainter p;
    p.begin( &m_printer );

    QPaintDeviceMetrics metrics( &m_printer );
    p.setFont( KGlobalSettings::generalFont() );
    QFontMetrics fm = p.fontMetrics();

    int w, h; // will be set to the width and height of the printer
              // when the orientation is decided.
    w = metrics.width();
    h = metrics.height();
    int filenameOffset = 0;

    QSize size = image2Print.size();

    bool printFilename = m_printer.option( "app-imageeditor-printFilename" ) != f;
    if ( printFilename )
    {
        // filename goes into one line!
        filenameOffset = fm.lineSpacing() + 14;
        h -= filenameOffset;
    }

    if ( m_printer.option( "app-imageeditor-scaleToFit" ) != f )
    {
        if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t )
            m_printer.setOrientation( size.width() <= size.height() ? KPrinter::Portrait 
                                       : KPrinter::Landscape );

        // Scale image to fit pagesize
        size.scale( w, h, QSize::ScaleMin );
    }
    else
    {
        int unit = (m_printer.option("app-imageeditor-scale-unit").isEmpty() ?
            ImageEditorPrintDialogPage::DK_INCHES : m_printer.option("app-imageeditor-scale-unit").toInt());
        double inches = 1;
    
        if (unit == ImageEditorPrintDialogPage::DK_MILLIMETERS) 
        {
            inches = 1/25.4;
        } 
        else if (unit == ImageEditorPrintDialogPage::DK_CENTIMETERS) 
        {
            inches = 1/2.54;
        }
    
        double wImg = (m_printer.option("app-imageeditor-scale-width").isEmpty() ?
                1 : m_printer.option("app-imageeditor-scale-width").toDouble()) * inches;
        double hImg = (m_printer.option("app-imageeditor-scale-height").isEmpty() ?
                1 : m_printer.option("app-imageeditor-scale-height").toDouble()) * inches;
        size.setWidth( int(wImg * m_printer.resolution()) );
        size.setHeight( int(hImg * m_printer.resolution()) );
    
        if ( m_printer.option( "app-imageeditor-auto-rotate" ) == t )
            m_printer.setOrientation( wImg <= hImg ? KPrinter::Portrait : KPrinter::Landscape );
    
        if (size.width() > w || size.height() > h) 
        {
            int resp = KMessageBox::warningYesNoCancel(KApplication::kApplication()->mainWidget(),
                i18n("The image will not fit on the page, what do you want to do?"),
                QString(),KStdGuiItem::cont(),
                i18n("Shrink") );
    
            if (resp==KMessageBox::Cancel) 
            {
                m_printer.abort();
                // no need to return false, user decided to abort
                return true;
            } 
            else if (resp == KMessageBox::No) 
            { // Shrink
                size.scale(w, h, QSize::ScaleMin);
            }
        }
    }

    // Align image.
    int alignment = (m_printer.option("app-imageeditor-alignment").isEmpty() ?
        Qt::AlignCenter : m_printer.option("app-imageeditor-alignment").toInt());

    int x = 0;
    int y = 0;

    // x - alignment
    if ( alignment & Qt::AlignHCenter )
        x = (w - size.width())/2;
    else if ( alignment & Qt::AlignLeft )
        x = 0;
    else if ( alignment & Qt::AlignRight )
        x = w - size.width();

    // y - alignment
    if ( alignment & Qt::AlignVCenter )
        y = (h - size.height())/2;
    else if ( alignment & Qt::AlignTop )
        y = 0;
    else if ( alignment & Qt::AlignBottom )
        y = h - size.height();

    // Perform the actual drawing.
    p.drawImage( QRect( x, y, size.width(), size.height()), image2Print );

    if ( printFilename )
    {
        QString fname = minimizeString( d->filename, fm, w );

        if ( !fname.isEmpty() )
        {
            int fw = fm.width( fname );
            int x = (w - fw)/2;
            int y = metrics.height() - filenameOffset/2;
            p.drawText( x, y, fname );
        }
    }

    p.end();

    return true;
}

QString ImagePrint::minimizeString( QString text, const QFontMetrics& metrics,
                                    int maxWidth )
{
    // no sense to cut that tiny little string
    if ( text.length() <= 5 )
        return QString();

    bool changed = false;

    while ( metrics.width( text ) > maxWidth )
    {
        int mid = text.length() / 2;
        // remove 2 characters in the middle
        text.remove( mid, 2 );
        changed = true;
    }

    // add "..." in the middle
    if ( changed )
    {
        int mid = text.length() / 2;

        // sanity check
        if ( mid <= 5 )
            return QString();

        text.replace( mid - 1, 3, "..." );
    }

    return text;
}

void ImagePrint::readSettings()
{
    KConfig* config = kapp->config();

    config->setGroup("Color Management");

    d->inProfilePath     = config->readPathEntry("WorkSpaceProfile");
    d->outputProfilePath = config->readPathEntry("ProofProfileFile");
}

// Image print dialog class -------------------------------------------------------------

class ImageEditorPrintDialogPagePrivate
{

public:

    ImageEditorPrintDialogPagePrivate()
    {
        cmEnabled     = false;
        position      = 0;
        keepRatio     = 0;
        scaleToFit    = 0;
        scale         = 0;
        addFileName   = 0;
        blackwhite    = 0;
        autoRotate    = 0;
        colorManaged  = 0;
        cmPreferences = 0;
        parent        = 0;
        width         = 0;
        height        = 0;
        units         = 0;
    }

    bool                              cmEnabled;

    QRadioButton                     *scaleToFit;
    QRadioButton                     *scale;

    QCheckBox                        *keepRatio;
    QCheckBox                        *addFileName;
    QCheckBox                        *blackwhite;
    QCheckBox                        *autoRotate;
    QCheckBox                        *colorManaged;

    QPushButton                      *cmPreferences;

    QWidget                          *parent;

    KDoubleNumInput                  *width;
    KDoubleNumInput                  *height;

    KComboBox                        *position;
    KComboBox                        *units;

    DImg                              image;

    ImageEditorPrintDialogPage::Unit  previousUnit;
};

ImageEditorPrintDialogPage::ImageEditorPrintDialogPage(DImg& image, QWidget *parent, const char *name )
                          : KPrintDialogPage( parent, name )
{
    d = new ImageEditorPrintDialogPagePrivate;
    d->image  = image;
    d->parent = parent;
    setTitle( i18n("Image Settings") );

    readSettings();

    QVBoxLayout *layout = new QVBoxLayout( this );
    layout->setMargin( KDialog::marginHint() );
    layout->setSpacing( KDialog::spacingHint() );

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

    QHBoxLayout *layout2 = new QHBoxLayout( layout ); 
    layout2->setSpacing(3);

    QLabel* textLabel = new QLabel( this, "Image position:" );
    textLabel->setText( i18n( "Image position:" ) );
    layout2->addWidget( textLabel );
    d->position = new KComboBox( false, this, "Print position" );
    d->position->clear();
    d->position->insertItem( i18n( "Top-Left" ) );
    d->position->insertItem( i18n( "Top-Central" ) );
    d->position->insertItem( i18n( "Top-Right" ) );
    d->position->insertItem( i18n( "Central-Left" ) );
    d->position->insertItem( i18n( "Central" ) );
    d->position->insertItem( i18n( "Central-Right" ) );
    d->position->insertItem( i18n( "Bottom-Left" ) );
    d->position->insertItem( i18n( "Bottom-Central" ) );
    d->position->insertItem( i18n( "Bottom-Right" ) );
    layout2->addWidget( d->position );
    QSpacerItem *spacer1 = new QSpacerItem( 101, 21, QSizePolicy::Expanding, QSizePolicy::Minimum );
    layout2->addItem( spacer1 );

    d->addFileName = new QCheckBox( i18n("Print fi&lename below image"), this);
    d->addFileName->setChecked( false );
    layout->addWidget( d->addFileName );

    d->blackwhite = new QCheckBox ( i18n("Print image in &black and white"), this);
    d->blackwhite->setChecked( false );
    layout->addWidget (d->blackwhite );

    d->autoRotate = new QCheckBox( i18n("&Auto-rotate page"), this );
    d->autoRotate->setChecked( false );
    layout->addWidget( d->autoRotate );

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

    QHBox *cmbox = new QHBox(this);

    d->colorManaged = new QCheckBox(i18n("Use Color Management for Printing"), cmbox);
    d->colorManaged->setChecked( false );

    d->cmPreferences = new QPushButton(i18n("Settings..."), cmbox);

    QWidget *space = new QWidget(cmbox);
    cmbox->setStretchFactor(space, 10);
    cmbox->setSpacing(KDialog::spacingHint());

    layout->addWidget(cmbox);
   
    // ------------------------------------------------------------------------

    QVButtonGroup *group = new QVButtonGroup( i18n("Scaling"), this );
    group->setRadioButtonExclusive( true );
    layout->addWidget( group );

    d->scaleToFit = new QRadioButton( i18n("Scale image to &fit"), group );
    d->scaleToFit->setChecked( true );

    d->scale = new QRadioButton( i18n("Print e&xact size: "), group );

    QHBox *hb = new QHBox( group );
    hb->setSpacing( KDialog::spacingHint() );
    QWidget *w = new QWidget(hb);
    w->setFixedWidth(d->scale->style().subRect( QStyle::SR_RadioButtonIndicator, d->scale ).width());

    d->width = new KDoubleNumInput( hb, "exact width" );
    d->width->setMinValue( 1 );

    new QLabel( "x", hb );

    d->height = new KDoubleNumInput( hb, "exact height" );
    d->height->setMinValue( 1 );

    d->units = new KComboBox( false, hb, "unit combobox" );
    d->units->insertItem( i18n("Millimeters") );
    d->units->insertItem( i18n("Centimeters") );
    d->units->insertItem( i18n("Inches") );

    d->keepRatio = new QCheckBox( i18n("Keep ratio"), hb);

    w = new QWidget(hb);
    hb->setStretchFactor( w, 1 );
    d->previousUnit = DK_MILLIMETERS;

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

    connect( d->colorManaged, SIGNAL(toggled(bool)),
             this, SLOT(slotAlertSettings( bool )) );

    connect( d->cmPreferences, SIGNAL(clicked()),
             this, SLOT(slotSetupDlg()) );

    connect( d->scale, SIGNAL( toggled( bool )),
             this, SLOT( toggleScaling( bool )));

    connect(d->width, SIGNAL( valueChanged( double )), 
            this, SLOT( slotWidthChanged( double )));

    connect(d->height, SIGNAL( valueChanged( double )), 
            this, SLOT( slotHeightChanged( double )));

    connect(d->keepRatio, SIGNAL( toggled( bool )), 
            this, SLOT( toggleRatio( bool )));

    connect(d->units, SIGNAL(activated(const QString &)), 
            this, SLOT(slotUnitChanged(const QString& )));
}

ImageEditorPrintDialogPage::~ImageEditorPrintDialogPage()
{
    delete d;
}

void ImageEditorPrintDialogPage::getOptions( QMap<QString,QString>& opts, bool /*incldef*/ )
{
    QString t = "true";
    QString f = "false";

    opts["app-imageeditor-alignment"]       = QString::number(getPosition(d->position->currentText()));
    opts["app-imageeditor-printFilename"]   = d->addFileName->isChecked() ? t : f;
    opts["app-imageeditor-blackwhite"]      = d->blackwhite->isChecked() ? t : f;
    opts["app-imageeditor-scaleToFit"]      = d->scaleToFit->isChecked() ? t : f;
    opts["app-imageeditor-scale"]           = d->scale->isChecked() ? t : f;
    opts["app-imageeditor-scale-unit"]      = QString::number(stringToUnit(d->units->currentText()));
    opts["app-imageeditor-scale-width"]     = QString::number( d->width->value() );
    opts["app-imageeditor-scale-height"]    = QString::number( d->height->value() );
    opts["app-imageeditor-scale-KeepRatio"] = d->keepRatio->isChecked() ? t : f;
    opts["app-imageeditor-auto-rotate"]     = d->autoRotate->isChecked() ? t : f;
    opts["app-imageeditor-color-managed"]   = d->colorManaged->isChecked() ? t : f;
}

void ImageEditorPrintDialogPage::setOptions( const QMap<QString,QString>& opts )
{
    QString t = "true";
    QString f = "false";
    QString stVal;
    bool    ok;
    double  dVal;
    int     iVal;

    iVal = opts["app-imageeditor-alignment"].toInt( &ok );
    if (ok) 
    {
        stVal = setPosition(iVal);
        d->position->setCurrentItem(stVal);
    }

    d->addFileName->setChecked( opts["app-imageeditor-printFilename"] != f );
    // This sound strange, but if I copy the code on the line above, the checkbox
    // was always checked. And this is not the wanted behavior. So, with this works.
    // KPrint magic ;-)
    d->blackwhite->setChecked ( false );
    d->scaleToFit->setChecked( opts["app-imageeditor-scaleToFit"] != f );
    d->scale->setChecked( opts["app-imageeditor-scale"] == t );
    d->autoRotate->setChecked( opts["app-imageeditor-auto-rotate"] == t );

    d->colorManaged->setChecked( false );

    Unit unit = static_cast<Unit>( opts["app-imageeditor-scale-unit"].toInt( &ok ) );
    if (ok) 
    {
        stVal = unitToString(unit);
        d->units->setCurrentItem(stVal);
        d->previousUnit = unit;
    }
    else
    {
        //for back compatibility
        d->units->setCurrentItem(i18n("Millimeters"));
    }

    dVal = opts["app-imageeditor-scale-width"].toDouble( &ok );

    if ( ok )
      d->width->setValue( dVal );

    dVal = opts["app-imageeditor-scale-height"].toDouble( &ok );

    if ( ok )
      d->height->setValue( dVal );

    if ( d->scale->isChecked() == d->scaleToFit->isChecked() )
        d->scaleToFit->setChecked( !d->scale->isChecked() );

    d->keepRatio->setChecked( opts["app-imageeditor-scale-KeepRatio"] == t );

}
int ImageEditorPrintDialogPage::getPosition(const QString& align) 
{
    int alignment;
    
    if (align == i18n("Central-Left")) 
    {
        alignment = Qt::AlignLeft | Qt::AlignVCenter;
    } 
    else if (align == i18n("Central-Right")) 
    {
        alignment = Qt::AlignRight | Qt::AlignVCenter;
    } 
    else if (align == i18n("Top-Left")) 
    {
        alignment = Qt::AlignTop | Qt::AlignLeft;
    } 
    else if (align == i18n("Top-Right")) 
    {
        alignment = Qt::AlignTop | Qt::AlignRight;
    } 
    else if (align == i18n("Bottom-Left")) 
    {
        alignment = Qt::AlignBottom | Qt::AlignLeft;
    }  
    else if (align == i18n("Bottom-Right")) 
    {
        alignment = Qt::AlignBottom | Qt::AlignRight;
    } 
    else if (align == i18n("Top-Central")) 
    {
        alignment = Qt::AlignTop | Qt::AlignHCenter;
    } 
    else if (align == i18n("Bottom-Central")) 
    {
        alignment = Qt::AlignBottom | Qt::AlignHCenter;
    }  
    else  
    {
        // Central
        alignment = Qt::AlignCenter; // Qt::AlignHCenter || Qt::AlignVCenter
    }
    
    return alignment;
}

QString ImageEditorPrintDialogPage::setPosition(int align) 
{
    QString alignment;
    
    if (align == (Qt::AlignLeft | Qt::AlignVCenter)) 
    {
        alignment = i18n("Central-Left");
    } 
    else if (align == (Qt::AlignRight | Qt::AlignVCenter)) 
    {
        alignment = i18n("Central-Right");
    } 
    else if (align == (Qt::AlignTop | Qt::AlignLeft)) 
    {
        alignment = i18n("Top-Left");
    } 
    else if (align == (Qt::AlignTop | Qt::AlignRight)) 
    {
        alignment = i18n("Top-Right");
    } 
    else if (align == (Qt::AlignBottom | Qt::AlignLeft)) 
    {
        alignment = i18n("Bottom-Left");
    } 
    else if (align == (Qt::AlignBottom | Qt::AlignRight)) 
    {
        alignment = i18n("Bottom-Right");
    } 
    else if (align == (Qt::AlignTop | Qt::AlignHCenter)) 
    {
        alignment = i18n("Top-Central");
    } 
    else if (align == (Qt::AlignBottom | Qt::AlignHCenter)) 
    {
        alignment = i18n("Bottom-Central");
    } 
    else  
    {
        // Central: Qt::AlignCenter or (Qt::AlignHCenter || Qt::AlignVCenter)
        alignment = i18n("Central");
    }
    
    return alignment;
}

void ImageEditorPrintDialogPage::toggleScaling( bool enable )
{
    d->width->setEnabled( enable );
    d->height->setEnabled( enable );
    d->units->setEnabled( enable );
    d->keepRatio->setEnabled( enable );
}

void ImageEditorPrintDialogPage::slotHeightChanged (double value) 
{
    d->width->blockSignals(true);
    d->height->blockSignals(true);
    
    if (d->keepRatio->isChecked()) 
    {
        double width = (d->image.width() * value) / d->image.height();
        d->width->setValue( width ? width : 1.);
    }
    d->height->setValue(value);
    
    d->width->blockSignals(false);
    d->height->blockSignals(false);
}

void ImageEditorPrintDialogPage::slotWidthChanged (double value) 
{
    d->width->blockSignals(true);
    d->height->blockSignals(true);
    
    if (d->keepRatio->isChecked()) 
    {
        double height = (d->image.height() * value) / d->image.width();
        d->height->setValue( height ? height : 1);
    }
    d->width->setValue(value);
    
    d->width->blockSignals(false);
    d->height->blockSignals(false);
}

void ImageEditorPrintDialogPage::toggleRatio( bool enable )
{
    if (!enable) return;
    // choosing a startup value of 15x10 cm (common photo dimention)
    // mContent->mHeight->value() or mContent->mWidth->value()
    // are usually empty at startup and hxw (0x0) is not good IMO keeping ratio
    double hValue, wValue;
    if (d->image.height() > d->image.width()) 
    {
        hValue = d->height->value();
        if (!hValue) hValue = 150*unitToMM(d->previousUnit);
        wValue = (d->image.width() * hValue)/ d->image.height();
    } 
    else 
    {
        wValue = d->width->value();
        if (!wValue) wValue = 150*unitToMM(d->previousUnit);
        hValue = (d->image.height() * wValue)/ d->image.width();
    }
    
    d->width->blockSignals(true);
    d->height->blockSignals(true);
    
    d->width->setValue(wValue);
    d->height->setValue(hValue);
    
    d->width->blockSignals(false);
    d->height->blockSignals(false);
}

void ImageEditorPrintDialogPage::slotUnitChanged(const QString& string) 
{
    Unit newUnit = stringToUnit(string);
    double ratio = unitToMM(d->previousUnit) / unitToMM(newUnit);
    
    d->width->blockSignals(true);
    d->height->blockSignals(true);
    
    d->width->setValue( d->width->value() * ratio);
    d->height->setValue( d->height->value() * ratio);
    
    d->width->blockSignals(false);
    d->height->blockSignals(false);
    
    d->previousUnit = newUnit;
}

void ImageEditorPrintDialogPage::readSettings()
{
    KConfig* config = kapp->config();
    config->setGroup("Color Management");
    d->cmEnabled = config->readBoolEntry("EnableCM", false);
}

void ImageEditorPrintDialogPage::slotSetupDlg()
{
    EditorWindow* editor = dynamic_cast<EditorWindow*>(d->parent);
    editor->setup(true);
}

void ImageEditorPrintDialogPage::slotAlertSettings( bool t)
{
    if (t && !d->cmEnabled)
    {
        QString message = i18n("<p>Color Management is disabled.</p> \
                                <p>You can enable it now by clicking on the \"Settings\" button.</p>");
        KMessageBox::information(this, message);
        d->colorManaged->setChecked(!t);
    }
}

double ImageEditorPrintDialogPage::unitToMM(Unit unit) 
{
    if (unit == DK_MILLIMETERS) 
    {
      return 1.;
    } 
    else if (unit == DK_CENTIMETERS) 
    {
      return 10.;
    } 
    else 
    { //DK_INCHES
      return 25.4;
    }
}

ImageEditorPrintDialogPage::Unit ImageEditorPrintDialogPage::stringToUnit(const QString& unit) 
{
    if (unit == i18n("Millimeters")) 
    {
      return DK_MILLIMETERS;
    }
    else if (unit == i18n("Centimeters")) 
    {
      return DK_CENTIMETERS;
    }
    else 
    {//Inches
      return DK_INCHES;
    }
}
  
QString ImageEditorPrintDialogPage::unitToString(Unit unit) 
{
    if (unit == DK_MILLIMETERS) 
    {
      return i18n("Millimeters");
    }
    else if (unit == DK_CENTIMETERS) 
    {
      return i18n("Centimeters");
    } 
    else 
    { //DK_INCHES
      return i18n("Inches");
    }
}

}  // namespace Digikam

