/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2005-04-07
 * Description : a tool to resize an image
 *
 * SPDX-FileCopyrightText: 2005-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#include "resizetool.h"

// C++ includes

#include <cmath>

// Qt includes

#include <QBrush>
#include <QCheckBox>
#include <QCloseEvent>
#include <QRadioButton>
#include <QComboBox>
#include <QEvent>
#include <QFile>
#include <QFrame>
#include <QGridLayout>
#include <QGroupBox>
#include <QImage>
#include <QLabel>
#include <QPixmap>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
#include <QVector>
#include <QIcon>
#include <QApplication>
#include <QStandardPaths>
#include <QMessageBox>

// KDE includes

#include <klocalizedstring.h>
#include <ksharedconfig.h>
#include <kconfiggroup.h>

// Local includes

#include "dlayoutbox.h"
#include "dnuminput.h"
#include "dimg.h"
#include "dimgbuiltinfilter.h"
#include "imageiface.h"
#include "imageguidewidget.h"
#include "editortoolsettings.h"
#include "editortooliface.h"
#include "dimgthreadedfilter.h"
#include "greycstorationfilter.h"
#include "greycstorationsettings.h"
#include "dactivelabel.h"
#include "dexpanderbox.h"
#include "dfiledialog.h"

namespace DigikamEditorResizeToolPlugin
{

class Q_DECL_HIDDEN ResizeTool::Private
{
public:

    enum WidthPreset
    {
        Original = 0,
        Tiny,
        Small,
        Medium,
        Big,
        Large,
        Huge,
        UHD4K,
        A3,
        A4,
        A6,
        Letter,
        Print1,
        Print2,
        Print3,
        Print4
    };

    enum Units
    {
        Pixels,
        Inches,
        Centimeters,
        Millimeters
    };

public:

    Private() = default;

    QSize presetLengthValue(WidthPreset preset);

public:

    const QString configGroupName                   = QLatin1String("resize Tool");
    const QString configFastApproxEntry             = QLatin1String("FastApprox");
    const QString configInterpolationEntry          = QLatin1String("Interpolation");
    const QString configAmplitudeEntry              = QLatin1String("Amplitude");
    const QString configSharpnessEntry              = QLatin1String("Sharpness");
    const QString configAnisotropyEntry             = QLatin1String("Anisotropy");
    const QString configAlphaEntry                  = QLatin1String("Alpha");
    const QString configSigmaEntry                  = QLatin1String("Sigma");
    const QString configGaussPrecEntry              = QLatin1String("GaussPrec");
    const QString configDlEntry                     = QLatin1String("Dl");
    const QString configDaEntry                     = QLatin1String("Da");
    const QString configIterationEntry              = QLatin1String("Iteration");
    const QString configTileEntry                   = QLatin1String("Tile");
    const QString configBTileEntry                  = QLatin1String("BTile");

    int                     orgWidth                = 0;
    int                     orgHeight               = 0;
    int                     prevW                   = 0;
    int                     prevH                   = 0;

    double                  prevWP                  = 0.0;
    double                  prevHP                  = 0.0;

    QLabel*                 restorationTips         = nullptr;

    QComboBox*              presetCBox              = nullptr;
    QComboBox*              units                   = nullptr;

    QCheckBox*              preserveRatioBox        = nullptr;
    QCheckBox*              useGreycstorationBox    = nullptr;

    QTabWidget*             mainTab                 = nullptr;

    DActiveLabel*           cimgLogoLabel           = nullptr;

    ImageGuideWidget*       previewWidget           = nullptr;

    DDoubleNumInput*        wInput                  = nullptr;
    DDoubleNumInput*        hInput                  = nullptr;
    DIntNumInput*           resolution              = nullptr;

    DDoubleNumInput*        wpInput                 = nullptr;
    DDoubleNumInput*        hpInput                 = nullptr;

    EditorToolSettings*     gboxSettings            = nullptr;
    GreycstorationSettings* settingsWidget          = nullptr;
};

QSize ResizeTool::Private::presetLengthValue(WidthPreset preset)
{
    QSize size;

    switch (preset)
    {
        case Private::Original:
        {
            size = QSize(orgWidth, orgHeight);
            break;
        }

        case Private::Tiny:
        {
            size = QSize(320, 200);
            break;
        }

        case Private::Small:
        {
            size = QSize(640, 480);
            break;
        }

        case Private::Medium:
        {
            size = QSize(960, 640);
            break;
        }

        case Private::Big:
        {
            size = QSize(1024, 768);
            break;
        }

        case Private::Large:
        {
            size = QSize(1280, 720);
            break;
        }

        case Private::Huge:
        {
            size = QSize(1920, 1080);
            break;
        }

        case Private::UHD4K:
        {
            size = QSize(3840, 2160);
            break;
        }

        case Private::A3:
        {
            size = QSize(3508, 4961);
            break;
        }

        case Private::A4:
        {
            size = QSize(2480, 3508);
            break;
        }

        case Private::A6:
        {
            size = QSize(1240, 1748);
            break;
        }

        case Private::Letter:
        {
            size = QSize(2550, 3300);
            break;
        }

        case Private::Print1:
        {
            size = QSize(1200, 1800);
            break;
        }

        case Private::Print2:
        {
            size = QSize(1500, 2100);
            break;
        }

        case Private::Print3:
        {
            size = QSize(2400, 3000);
            break;
        }

        default:
        {
            size = QSize(3300, 4200);
            break;
        }
    }

    return size;
}


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

ResizeTool::ResizeTool(QObject* const parent)
    : EditorToolThreaded(parent),
      d                 (new Private)
{
    setObjectName(QLatin1String("resizeimage"));

    d->previewWidget = new ImageGuideWidget(nullptr, false, ImageGuideWidget::HVGuideMode, Qt::red, 1, false);
    setToolView(d->previewWidget);
    setPreviewModeMask(PreviewToolBar::UnSplitPreviewModes);

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

    d->gboxSettings = new EditorToolSettings(nullptr);
    d->gboxSettings->setButtons(EditorToolSettings::Default |
                                EditorToolSettings::Try     |
                                EditorToolSettings::Ok      |
                                EditorToolSettings::Load    |
                                EditorToolSettings::SaveAs  |
                                EditorToolSettings::Cancel);

    ImageIface iface;
    d->orgWidth  = iface.originalSize().width();
    d->orgHeight = iface.originalSize().height();
    d->prevW     = d->orgWidth;
    d->prevH     = d->orgHeight;
    d->prevWP    = 100.0;
    d->prevHP    = 100.0;

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

    d->mainTab               = new QTabWidget();
    QWidget* const firstPage = new QWidget(d->mainTab);
    QGridLayout* const grid  = new QGridLayout(firstPage);

    d->mainTab->addTab(firstPage, i18n("New Size"));

    QLabel* const label = new QLabel(i18n("Preset Resolutions:"), firstPage);
    d->presetCBox       = new QComboBox(firstPage);
    d->presetCBox->addItem(i18nc("Original (size)", "Original (%1 x %2 pixels)", d->presetLengthValue(Private::Original).width(),
                                                                                 d->presetLengthValue(Private::Original).height()),
                                                                                 Private::Original);
    d->presetCBox->addItem(i18nc("Tiny (size)",     "Tiny (%1 x %2 pixels)",     d->presetLengthValue(Private::Tiny).width(),
                                                                                 d->presetLengthValue(Private::Tiny).height()),
                                                                                 Private::Tiny);
    d->presetCBox->addItem(i18nc("Small (size)",    "Small (%1 x %2 pixels)",    d->presetLengthValue(Private::Small).width(),
                                                                                 d->presetLengthValue(Private::Small).height()),
                                                                                 Private::Small);
    d->presetCBox->addItem(i18nc("Medium (size)",   "Medium (%1 x %2 pixels)",   d->presetLengthValue(Private::Medium).width(),
                                                                                 d->presetLengthValue(Private::Medium).height()),
                                                                                 Private::Medium);
    d->presetCBox->addItem(i18nc("Big (size)",      "Big (%1 x %2 pixels)",      d->presetLengthValue(Private::Big).width(),
                                                                                 d->presetLengthValue(Private::Big).height()),
                                                                                 Private::Big);
    d->presetCBox->addItem(i18nc("Large (size)",    "Large (%1 x %2 pixels)",    d->presetLengthValue(Private::Large).width(),
                                                                                 d->presetLengthValue(Private::Large).height()),
                                                                                 Private::Large);
    d->presetCBox->addItem(i18nc("Huge (size)",     "Huge (%1 x %2 pixels)",     d->presetLengthValue(Private::Huge).width(),
                                                                                 d->presetLengthValue(Private::Huge).height()),
                                                                                 Private::Huge);
    d->presetCBox->addItem(i18nc("UHD4K (size)",    "UHD-4K (%1 x %2 pixels)",   d->presetLengthValue(Private::UHD4K).width(),
                                                                                 d->presetLengthValue(Private::UHD4K).height()),
                                                                                 Private::UHD4K);
    d->presetCBox->addItem(i18nc("A3 (size)",       "A3 (297 x 420 mm)"),        Private::A3);
    d->presetCBox->addItem(i18nc("A4 (size)",       "A4 (210 x 297 mm)"),        Private::A4);
    d->presetCBox->addItem(i18nc("A6 (size)",       "A6 (105 x 148 mm)"),        Private::A6);
    d->presetCBox->addItem(i18nc("Letter (size)",   "Letter (8.5 x 11 in)"),     Private::Letter);
    d->presetCBox->addItem(i18nc("Print (size)",    "4 x 6 in"),                 Private::Print1);
    d->presetCBox->addItem(i18nc("Print (size)",    "5 x 7 in"),                 Private::Print2);
    d->presetCBox->addItem(i18nc("Print (size)",    "8 x 10 in"),                Private::Print3);
    d->presetCBox->addItem(i18nc("Print (size)",    "11 x 14 in"),               Private::Print4);
    d->presetCBox->insertSeparator((int)Private::A3);
    d->presetCBox->insertSeparator((int)Private::Tiny);

    QLabel* const label1    = new QLabel(i18n("Units:"), firstPage);
    d->units                = new QComboBox(firstPage);
    d->units->addItem(i18n("Pixels (px)"),      Private::Pixels);
    d->units->addItem(i18n("Inches (in)"),      Private::Inches);
    d->units->addItem(i18n("Centimeters (cm)"), Private::Centimeters);
    d->units->addItem(i18n("Millimeters (mm)"), Private::Millimeters);

    d->preserveRatioBox = new QCheckBox(i18n("Maintain aspect ratio"), firstPage);
    d->preserveRatioBox->setWhatsThis( i18n("Enable this option to maintain aspect "
                                            "ratio with new image sizes."));

    QLabel* const label2 = new QLabel(i18n("Width:"), firstPage);
    d->wInput            = new DDoubleNumInput(firstPage);
    d->wInput->setRange(1.0, (double)qMax(d->orgWidth * 10, 15000), 1.0);
    d->wInput->setSuffix(QLatin1Char(' ') + i18n("px"));
    d->wInput->setObjectName(QLatin1String("wInput"));
    d->wInput->setDefaultValue((double)d->orgWidth);
    d->wInput->setDecimals(0);
    d->wInput->setWhatsThis( i18n("Set here the new image width in pixels."));

    QLabel* const label3 = new QLabel(i18n("Height:"), firstPage);
    d->hInput            = new DDoubleNumInput(firstPage);
    d->hInput->setRange(1.0, (double)qMax(d->orgHeight * 10, 15000), 1.0);
    d->hInput->setSuffix(QLatin1Char(' ') + i18n("px"));
    d->hInput->setObjectName(QLatin1String("hInput"));
    d->hInput->setDefaultValue((double)d->orgHeight);
    d->hInput->setDecimals(0);
    d->hInput->setWhatsThis( i18n("New image height in pixels (px)."));

    QLabel* const label4 = new QLabel(i18n("Width (%):"), firstPage);
    d->wpInput           = new DDoubleNumInput(firstPage);
    d->wpInput->setObjectName(QLatin1String("wpInput"));
    d->wpInput->setRange(1.0, 999.0, 1.0);
    d->wpInput->setDefaultValue(100.0);
    d->wInput->setDecimals(0);
    d->wpInput->setWhatsThis( i18n("New image width in percent (%)."));

    QLabel* const label5 = new QLabel(i18n("Height (%):"), firstPage);
    d->hpInput           = new DDoubleNumInput(firstPage);
    d->hpInput->setObjectName(QLatin1String("hpInput"));
    d->hpInput->setRange(1.0, 999.0, 1.0);
    d->hpInput->setDefaultValue(100.0);
    d->wInput->setDecimals(0);
    d->hpInput->setWhatsThis( i18n("New image height in percent (%)."));

    QLabel* const label6 = new QLabel(i18n("Resolution:"), firstPage);
    d->resolution        = new DIntNumInput(firstPage);
    d->resolution->setRange(1, 500, 1);
    d->resolution->setDefaultValue(300);
    d->resolution->setSuffix(i18n(" Pixel/Inch"));
    d->resolution->setObjectName(QLatin1String("resolution"));
    d->resolution->setWhatsThis( i18n("New image resolution in pixels/inch."));

    d->cimgLogoLabel        = new DActiveLabel(QUrl(QLatin1String("http://cimg.sourceforge.net")),      // krazy:exclude=insecurenet
                                               QStandardPaths::locate(QStandardPaths::GenericDataLocation,
                                                                      QLatin1String("digikam/data/logo-cimg.png")), firstPage);
    d->cimgLogoLabel->setToolTip(i18n("Visit CImg library website"));

    d->useGreycstorationBox = new QCheckBox(i18n("Restore photograph (slow)"), firstPage);
    d->useGreycstorationBox->setWhatsThis( i18n("Enable this option to scale-up an image to a huge size. "
                                           "<b>Warning</b>: This process can take some time."));

    d->restorationTips      = new QLabel(i18n("<b>Note:</b> use Restoration Mode to scale-up an image to a huge size. "
                                              "This process can take some time."), firstPage);
    d->restorationTips->setWordWrap(true);

    const int spacing = d->gboxSettings->spacingHint();

    grid->addWidget(label,                                       0, 0, 1, 3);
    grid->addWidget(d->presetCBox,                               1, 0, 1, 3);
    grid->addWidget(d->preserveRatioBox,                         2, 0, 1, 3);
    grid->addWidget(label1,                                      3, 0, 1, 1);
    grid->addWidget(d->units,                                    3, 1, 1, 1);
    grid->addWidget(label2,                                      4, 0, 1, 1);
    grid->addWidget(d->wInput,                                   4, 1, 1, 2);
    grid->addWidget(label3,                                      5, 0, 1, 1);
    grid->addWidget(d->hInput,                                   5, 1, 1, 2);
    grid->addWidget(label4,                                      6, 0, 1, 1);
    grid->addWidget(d->wpInput,                                  6, 1, 1, 2);
    grid->addWidget(label5,                                      7, 0, 1, 1);
    grid->addWidget(d->hpInput,                                  7, 1, 1, 2);
    grid->addWidget(label6,                                      8, 0, 1, 1);
    grid->addWidget(d->resolution,                               8, 1, 1, 2);
    grid->addWidget(new DLineWidget(Qt::Horizontal, firstPage),  9, 0, 1, 3);
    grid->addWidget(d->cimgLogoLabel,                           10, 0, 3, 1);
    grid->addWidget(d->useGreycstorationBox,                    10, 1, 1, 2);
    grid->addWidget(d->restorationTips,                         11, 1, 1, 2);
    grid->setRowStretch(12, 10);
    grid->setContentsMargins(spacing, spacing, spacing, spacing);
    grid->setSpacing(spacing);

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

    d->settingsWidget = new GreycstorationSettings(d->mainTab);

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

    QGridLayout* grid2 = new QGridLayout();
    grid2->addWidget(d->mainTab, 0, 1, 1, 1);
    grid2->setContentsMargins(spacing, spacing, spacing, spacing);
    grid2->setSpacing(spacing);
    grid2->setRowStretch(1, 10);
    d->gboxSettings->plainPage()->setLayout(grid2);

    setToolSettings(d->gboxSettings);

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

    connect(d->presetCBox, SIGNAL(activated(int)),
            this, SLOT(slotPresetsChanged()));

    connect(d->units, SIGNAL(activated(int)),
            this, SLOT(slotUnitsChanged()));

    connect(d->wInput, SIGNAL(valueChanged(double)),
            this, SLOT(slotValuesChanged()));

    connect(d->hInput, SIGNAL(valueChanged(double)),
            this, SLOT(slotValuesChanged()));

    connect(d->wpInput, SIGNAL(valueChanged(double)),
            this, SLOT(slotValuesChanged()));

    connect(d->hpInput, SIGNAL(valueChanged(double)),
            this, SLOT(slotValuesChanged()));

    connect(d->resolution, SIGNAL(valueChanged(int)),
            this, SLOT(slotValuesChanged()));

    connect(d->useGreycstorationBox, SIGNAL(toggled(bool)),
            this, SLOT(slotRestorationToggled(bool)) );

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

    GreycstorationContainer defaults;
    defaults.setResizeDefaultSettings();
    d->settingsWidget->setDefaultSettings(defaults);

    QTimer::singleShot(0, this, SLOT(slotResetSettings()));
}

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

void ResizeTool::readSettings()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup group        = config->group(d->configGroupName);

    GreycstorationContainer prm;
    GreycstorationContainer defaults;
    defaults.setResizeDefaultSettings();

    prm.fastApprox = group.readEntry(d->configFastApproxEntry,    defaults.fastApprox);
    prm.interp     = group.readEntry(d->configInterpolationEntry, defaults.interp);
    prm.amplitude  = group.readEntry(d->configAmplitudeEntry,     (double)defaults.amplitude);
    prm.sharpness  = group.readEntry(d->configSharpnessEntry,     (double)defaults.sharpness);
    prm.anisotropy = group.readEntry(d->configAnisotropyEntry,    (double)defaults.anisotropy);
    prm.alpha      = group.readEntry(d->configAlphaEntry,         (double)defaults.alpha);
    prm.sigma      = group.readEntry(d->configSigmaEntry,         (double)defaults.sigma);
    prm.gaussPrec  = group.readEntry(d->configGaussPrecEntry,     (double)defaults.gaussPrec);
    prm.dl         = group.readEntry(d->configDlEntry,            (double)defaults.dl);
    prm.da         = group.readEntry(d->configDaEntry,            (double)defaults.da);
    prm.nbIter     = group.readEntry(d->configIterationEntry,     defaults.nbIter);
    prm.tile       = group.readEntry(d->configTileEntry,          defaults.tile);
    prm.btile      = group.readEntry(d->configBTileEntry,         defaults.btile);
    d->settingsWidget->setSettings(prm);
}

void ResizeTool::writeSettings()
{
    GreycstorationContainer prm = d->settingsWidget->settings();
    KConfigGroup group          = KSharedConfig::openConfig()->group(d->configGroupName);

    group.writeEntry(d->configFastApproxEntry,    prm.fastApprox);
    group.writeEntry(d->configInterpolationEntry, prm.interp);
    group.writeEntry(d->configAmplitudeEntry,     (double)prm.amplitude);
    group.writeEntry(d->configSharpnessEntry,     (double)prm.sharpness);
    group.writeEntry(d->configAnisotropyEntry,    (double)prm.anisotropy);
    group.writeEntry(d->configAlphaEntry,         (double)prm.alpha);
    group.writeEntry(d->configSigmaEntry,         (double)prm.sigma);
    group.writeEntry(d->configGaussPrecEntry,     (double)prm.gaussPrec);
    group.writeEntry(d->configDlEntry,            (double)prm.dl);
    group.writeEntry(d->configDaEntry,            (double)prm.da);
    group.writeEntry(d->configIterationEntry,     prm.nbIter);
    group.writeEntry(d->configTileEntry,          prm.tile);
    group.writeEntry(d->configBTileEntry,         prm.btile);
    group.writeEntry("RestorePhotograph",         d->useGreycstorationBox->isChecked());
    group.sync();
}

void ResizeTool::slotResetSettings()
{
    GreycstorationContainer prm;
    prm.setResizeDefaultSettings();

    d->settingsWidget->setSettings(prm);
    d->useGreycstorationBox->setChecked(false);
    slotRestorationToggled(d->useGreycstorationBox->isChecked());

    d->units->setCurrentIndex(0);
    d->presetCBox->setCurrentIndex(0);
    d->preserveRatioBox->setChecked(true);

    slotUnitsChanged();

    blockWidgetSignals(true);

    d->wInput->slotReset();
    d->hInput->slotReset();
    d->wpInput->slotReset();
    d->hpInput->slotReset();
    d->resolution->slotReset();

    blockWidgetSignals(false);
}

void ResizeTool::slotPresetsChanged()
{
    Private::WidthPreset preset = (Private::WidthPreset)d->presetCBox->currentData().toInt();
    QSize size                  = d->presetLengthValue(preset);

    switch (preset)
    {
        case Private::A3:
        case Private::A4:
        case Private::A6:
        {
            d->units->setCurrentIndex(d->units->findData(Private::Millimeters));

            if (d->orgWidth > d->orgHeight)
            {
                size.transpose();
            }

            break;
        }

        case Private::Letter:
        case Private::Print1:
        case Private::Print2:
        case Private::Print3:
        case Private::Print4:
        {
            d->units->setCurrentIndex(d->units->findData(Private::Inches));

            if (d->orgWidth > d->orgHeight)
            {
                size.transpose();
            }

            break;
        }

        default:
        {
            d->units->setCurrentIndex(d->units->findData(Private::Pixels));
            break;
        }
    }

    slotUnitsChanged();
    d->resolution->slotReset();

    if (!d->preserveRatioBox->isChecked())
    {
        d->wInput->setValue(pixelsToUnits(size.width()));
        d->hInput->setValue(pixelsToUnits(size.height()));
    }
    else
    {
        d->wInput->setValue(pixelsToUnits(size.width()));

        if (d->prevH > size.height())
        {
            d->hInput->setValue(pixelsToUnits(size.height()));
        }
    }
}

void ResizeTool::slotUnitsChanged()
{
    blockWidgetSignals(true);

    int decimals = 0;
    QString suffix;

    switch ((Private::Units)d->units->currentData().toInt())
    {
        case Private::Pixels:
        {
            decimals = 0;
            suffix   = i18nc("Pixels", "px");
            break;
        }

        case Private::Inches:
        {
            decimals = 1;
            suffix   = i18nc("Inches", "in");
            break;
        }

        case Private::Millimeters:
        {
            decimals = 1;
            suffix   = i18nc("Millimeters", "mm");
            break;
        }

        case Private::Centimeters:
        {
            decimals = 2;
            suffix   = i18nc("Centimeters", "cm");
            break;
        }

        default:
        {
            break;
        }
    }

    d->wInput->setRange(1.0, pixelsToUnits(qMax(d->orgWidth * 10, 15000)), 1.0);
    d->wInput->setDefaultValue(pixelsToUnits(d->orgWidth));
    d->wInput->setSuffix(QLatin1Char(' ') + suffix);
    d->wInput->setDecimals(decimals);
    d->wInput->setValue(pixelsToUnits(d->prevW));

    d->hInput->setRange(1.0, pixelsToUnits(qMax(d->orgHeight * 10, 15000)), 1.0);
    d->hInput->setDefaultValue(pixelsToUnits(d->orgHeight));
    d->hInput->setSuffix(QLatin1Char(' ') + suffix);
    d->hInput->setDecimals(decimals);
    d->hInput->setValue(pixelsToUnits(d->prevH));

    blockWidgetSignals(false);
}

int ResizeTool::unitsToPixels(double val)
{
    Private::Units units = (Private::Units)d->units->currentData().toInt();
    int res              = d->resolution->value();
    int pixel            = qRound(val);

    switch (units)
    {
        case Private::Inches:
        {
            pixel = pixel * res;
            break;
        }

        case Private::Millimeters:
        {
            pixel = qRound(pixel * res / 25.4);
            break;
        }

        case Private::Centimeters:
        {
            pixel = qRound(pixel * res / 2.54);
            break;
        }

        default:
        {
            break;
        }
    }

    return pixel;
}

double ResizeTool::pixelsToUnits(int pix)
{
    Private::Units units = (Private::Units)d->units->currentData().toInt();
    int    res           = d->resolution->value();
    double val           = (double)pix;

    switch (units)
    {
        case Private::Inches:
        {
            val = val / (double)res;
            break;
        }

        case Private::Millimeters:
        {
            val = (val * 25.4) / (double)res;
            break;
        }

        case Private::Centimeters:
        {
            val = (val * 2.54) / (double)res;
            break;
        }

        default:
        {
            break;
        }
    }

    return val;
}

void ResizeTool::slotValuesChanged()
{
    blockWidgetSignals(true);

    QString s(sender()->objectName());

    if      (s == QLatin1String("wInput"))
    {
        double val  = unitsToPixels(d->wInput->value());
        double pval = val / (double)(d->orgWidth) * 100.0;

        d->wpInput->setValue(pval);

        if (d->preserveRatioBox->isChecked())
        {
            double h = pixelsToUnits(qRound(pval * d->orgHeight / 100));
            d->hpInput->setValue(pval);
            d->hInput->setValue(h);
        }
    }
    else if (s == QLatin1String("hInput"))
    {
        double val  = unitsToPixels(d->hInput->value());
        double pval = val / (double)(d->orgHeight) * 100.0;

        d->hpInput->setValue(pval);

        if (d->preserveRatioBox->isChecked())
        {
            double w = pixelsToUnits(qRound(pval * d->orgWidth / 100));
            d->wpInput->setValue(pval);
            d->wInput->setValue(w);
        }
    }
    else if (s == QLatin1String("wpInput"))
    {
        double val = d->wpInput->value();
        double w   = pixelsToUnits(qRound(val * d->orgWidth / 100));
        d->wInput->setValue(w);

        if (d->preserveRatioBox->isChecked())
        {
            double h = pixelsToUnits(qRound(val * d->orgHeight / 100));
            d->hpInput->setValue(val);
            d->hInput->setValue(h);
        }
    }
    else if (s == QLatin1String("hpInput"))
    {
        double val = d->hpInput->value();
        double h   = pixelsToUnits(qRound(val * d->orgHeight / 100));
        d->hInput->setValue(h);

        if (d->preserveRatioBox->isChecked())
        {
            double w = pixelsToUnits(qRound(val * d->orgWidth / 100));
            d->wpInput->setValue(val);
            d->wInput->setValue(w);
        }
    }
    else if (s == QLatin1String("resolution"))
    {
        double hval  = unitsToPixels(d->hInput->value());
        double pHval = hval / (double)(d->orgHeight) * 100.0;
        double wval  = unitsToPixels(d->wInput->value());
        double pWval = wval / (double)(d->orgWidth) * 100.0;

        d->wpInput->setValue(pWval);
        d->hpInput->setValue(pHval);
    }

    d->prevW  = unitsToPixels(d->wInput->value());
    d->prevH  = unitsToPixels(d->hInput->value());
    d->prevWP = d->wpInput->value();
    d->prevHP = d->hpInput->value();

    blockWidgetSignals(false);
}

void ResizeTool::preparePreview()
{
    int h = unitsToPixels(d->hInput->value());
    int w = unitsToPixels(d->wInput->value());

    if (
        (d->prevW  != d->wInput->value())  || (d->prevH  != d->hInput->value()) ||
        (d->prevWP != d->wpInput->value()) || (d->prevHP != d->hpInput->value())
       )
    {
        slotValuesChanged();
    }

    ImageIface* const iface = d->previewWidget->imageIface();
    DImg* imTemp            = iface->original();

    if (d->useGreycstorationBox->isChecked())
    {
        setFilter(new GreycstorationFilter(imTemp,
                                           d->settingsWidget->settings(),
                                           GreycstorationFilter::Resize,
                                           w, h,
                                           QImage(),
                                           this));
    }
    else
    {
        // See bug #152192: CImg resize() sound like defective or unadapted
        // to resize image without good quality.

        DImgBuiltinFilter resize(DImgBuiltinFilter::Resize, QSize(w, h));
        setFilter(resize.createThreadedFilter(imTemp, this));
    }
}

void ResizeTool::prepareFinal()
{
    int h = unitsToPixels(d->hInput->value());
    int w = unitsToPixels(d->wInput->value());

    if (
        (d->prevW  != d->wInput->value())  || (d->prevH  != d->hInput->value()) ||
        (d->prevWP != d->wpInput->value()) || (d->prevHP != d->hpInput->value())
       )
    {
        slotValuesChanged();
    }

    d->mainTab->setCurrentIndex(0);

    ImageIface iface;

    if (d->useGreycstorationBox->isChecked())
    {
        setFilter(new GreycstorationFilter(iface.original(),
                                           d->settingsWidget->settings(),
                                           GreycstorationFilter::Resize,
                                           w,
                                           h,
                                           QImage(),
                                           this));
    }
    else
    {
        // See bug #152192: CImg resize() sound like defective or unadapted
        // to resize image without good quality.

        DImgBuiltinFilter resize(DImgBuiltinFilter::Resize, QSize(w, h));
        setFilter(resize.createThreadedFilter(iface.original(), this));
    }
}

void ResizeTool::setPreviewImage()
{
    ImageIface* const iface = d->previewWidget->imageIface();
    int w                   = iface->previewSize().width();
    int h                   = iface->previewSize().height();
    DImg imTemp             = filter()->getTargetImage().smoothScale(w, h, Qt::KeepAspectRatio);
    DImg imDest(w, h, filter()->getTargetImage().sixteenBit(), filter()->getTargetImage().hasAlpha());

    QColor background = toolView()->backgroundRole();
    imDest.fill(DColor(background, filter()->getTargetImage().sixteenBit()));
    imDest.bitBltImage(&imTemp, (w-imTemp.width())/2, (h-imTemp.height())/2);

    iface->setPreview(imDest.smoothScale(iface->previewSize()));
    d->previewWidget->updatePreview();
}

void ResizeTool::renderingFinished()
{
    d->settingsWidget->setEnabled(d->useGreycstorationBox->isChecked());
}

void ResizeTool::setFinalImage()
{
    ImageIface iface;
    DImg targetImage = filter()->getTargetImage();
    iface.setOriginal(i18n("Resize"),
                           filter()->filterAction(),
                           targetImage);
}

void ResizeTool::blockWidgetSignals(bool b)
{
    d->preserveRatioBox->blockSignals(b);
    d->wInput->blockSignals(b);
    d->hInput->blockSignals(b);
    d->wpInput->blockSignals(b);
    d->hpInput->blockSignals(b);
}

void ResizeTool::slotRestorationToggled(bool b)
{
    d->settingsWidget->setEnabled(b);
    d->cimgLogoLabel->setEnabled(b);
    toolSettings()->enableButton(EditorToolSettings::Load, b);
    toolSettings()->enableButton(EditorToolSettings::SaveAs, b);
}

void ResizeTool::slotLoadSettings()
{
    QUrl loadBlowupFile = DFileDialog::getOpenFileUrl(qApp->activeWindow(), i18nc("@title:window", "Photograph Resizing Settings File to Load"),
                                                      QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)),
                                                      QLatin1String("*"));

    if ( loadBlowupFile.isEmpty() )
    {
        return;
    }

    QFile file(loadBlowupFile.toLocalFile());

    if ( file.open(QIODevice::ReadOnly) )
    {
        if (!d->settingsWidget->loadSettings(file, QLatin1String("# Photograph Resizing Configuration File")))
        {
            QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
                                  i18n("\"%1\" is not a Photograph Resizing settings text file.",
                                       loadBlowupFile.fileName()));
            file.close();
            return;
        }
    }
    else
    {
        QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
                              i18n("Cannot load settings from the Photograph Resizing text file."));
    }

    file.close();
}

void ResizeTool::slotSaveAsSettings()
{
    QUrl saveBlowupFile = DFileDialog::getSaveFileUrl(qApp->activeWindow(), i18nc("@title:window", "Photograph Resizing Settings File to Save"),
                                                      QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)),
                                                      QLatin1String("*"));

    if ( saveBlowupFile.isEmpty() )
    {
        return;
    }

    QFile file(saveBlowupFile.toLocalFile());

    if ( file.open(QIODevice::WriteOnly) )
    {
        d->settingsWidget->saveSettings(file, QLatin1String("# Photograph Resizing Configuration File"));
    }
    else
    {
        QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(),
                              i18n("Cannot save settings to the Photograph Resizing text file."));
    }

    file.close();
}

} // namespace DigikamEditorResizeToolPlugin

#include "moc_resizetool.cpp"
