/***************************************************************************
 *   Copyright (C) 2010-2018 by Daniel Nicoletti                           *
 *   dantti12@gmail.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.                                   *
 *                                                                         *
 *   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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; see the file COPYING. If not, write to       *
 *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,  *
 *   Boston, MA 02110-1301, USA.                                           *
 *                                                                         *
 *   The save PPD snipet is from CUPS                                      *
 *   Copyright 2007-2009 by Apple Inc.                                     *
 *   Copyright 1997-2007 by Easy Software Products.                        *
 *                                                                         *
 *   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.                                   *
 *   This is converted from LGPL 2 in accordance with section 3            *
 *   See http://www.cups.org/documentation.php/license.html                *
 ***************************************************************************/

#include "PrinterOptions.h"

#include "ui_PrinterOptions.h"

#include "Debug.h"

#include <KCupsRequest.h>
#include <NoSelectionRectDelegate.h>

#include <QFormLayout>
#include <QComboBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QStandardItemModel>
#include <QListView>
#include <QGroupBox>
#include <QPointer>

#include <ctype.h>

#define DEFAULT_CHOICE "defaultChoice"

PrinterOptions::PrinterOptions(const QString &destName, bool isClass, bool isRemote, QWidget *parent) :
    PrinterPage(parent),
    ui(new Ui::PrinterOptions),
    m_destName(destName),
    m_isClass(isClass),
    m_isRemote(isRemote)
{
    ui->setupUi(this);

    reloadPPD();
}

void PrinterOptions::on_autoConfigurePB_clicked()
{
    QPointer<KCupsRequest> request = new KCupsRequest;
    request->printCommand(m_destName, QLatin1String("AutoConfigure"), i18n("Set Default Options"));
    request->waitTillFinished();
    if (request) {
        request->deleteLater();
    }
}

void PrinterOptions::reloadPPD()
{
    //     The caller "owns" the file that is created and must unlink the returned filename.
    if (!m_filename.isEmpty()) {
        unlink(qUtf8Printable(m_filename));
    }

    // remove all the options
    while (ui->verticalLayout->count()) {
        qCDebug(PM_CONFIGURE_PRINTER) << "removing" << ui->verticalLayout->count();
        QLayoutItem *item = ui->verticalLayout->itemAt(0);
        ui->verticalLayout->removeItem(item);
        if (item->widget()) {
            item->widget()->deleteLater();
            delete item;
        } else if (item->layout()) {
            qCDebug(PM_CONFIGURE_PRINTER) << "removing layout" << ui->verticalLayout->count();

//            item->layout()->deleteLater();
        } else if (item->spacerItem()) {
            delete item->spacerItem();
        }
    }
    m_changes = 0;
    m_customValues.clear();
    emit changed(false);

    QPointer<KCupsRequest> request = new KCupsRequest;
    request->getPrinterPPD(m_destName);
    request->waitTillFinished();
    if (!request) {
        return;
    }
    m_filename = request->printerPPD();
    m_ppd = ppdOpenFile(qUtf8Printable(m_filename));
    request->deleteLater();
    if (m_ppd == NULL) {
        qCWarning(PM_CONFIGURE_PRINTER) << "Could not open ppd file:" << m_filename << request->errorMsg();
        m_filename.clear();
        return;
    }

    ppdLocalize(m_ppd);
    // select the default options on the ppd file
    ppdMarkDefaults(m_ppd);

    // TODO try to use QTextCodec aliases
    const char *lang_encoding;
    lang_encoding = m_ppd->lang_encoding;
    if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin1")) {
        m_codec = QTextCodec::codecForName("ISO-8859-1");
    } else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin2")) {
        m_codec = QTextCodec::codecForName("ISO-8859-2");
    } else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin5")) {
        m_codec = QTextCodec::codecForName("ISO-8859-5");
    } else if (lang_encoding && !strcasecmp (lang_encoding, "JIS83-RKSJ")) {
        m_codec = QTextCodec::codecForName("SHIFT-JIS");
    } else if (lang_encoding && !strcasecmp (lang_encoding, "MacStandard")) {
        m_codec = QTextCodec::codecForName("MACINTOSH");
    } else if (lang_encoding && !strcasecmp (lang_encoding, "WindowsANSI")) {
        m_codec = QTextCodec::codecForName("WINDOWS-1252");
    } else {
        // Guess
        m_codec = QTextCodec::codecForName(lang_encoding);
    }
    if (m_codec == 0) {
        m_codec = QTextCodec::codecForName("UTF-8");
    }

    if (m_ppd->manufacturer) {
        m_make = m_codec->toUnicode(m_ppd->manufacturer);
    }

    if (m_ppd->nickname) {
        m_makeAndModel = m_codec->toUnicode(m_ppd->nickname);
    }

    ui->autoConfigurePB->hide();
    ppd_attr_t  *ppdattr;
    if (m_ppd->num_filters == 0 ||
        ((ppdattr = ppdFindAttr(m_ppd, "cupsCommands", NULL)) != NULL &&
           ppdattr->value && strstr(ppdattr->value, "AutoConfigure"))) {
        ui->autoConfigurePB->show();
    } else {
        for (int i = 0; i < m_ppd->num_filters; i ++) {
            if (!strncmp(m_ppd->filters[i], "application/vnd.cups-postscript", 31)) {
              ui->autoConfigurePB->show();
              break;
            }
        }
    }

    createGroups();
}

void PrinterOptions::createGroups()
{
    int i;
    ppd_group_t *group;
    // Iterate over the groups
    for (i = 0, group = m_ppd->groups;
         i < m_ppd->num_groups;
         i++, group++) {
        // The name of the group
        QString name = m_codec->toUnicode(group->name);

        // The humman name of the group
        QString text = m_codec->toUnicode(group->text);

        // The group box were the options will be laid out
        auto groupBox = new QGroupBox(text, ui->scrollArea);

        // Create the form layout to put options in
        auto gFormLayout = new QFormLayout(groupBox);
        gFormLayout->setFormAlignment(Qt::AlignCenter);
        groupBox->setLayout(gFormLayout);
        ui->verticalLayout->addWidget(groupBox);

        int j;
        ppd_option_t *option;
        // Iterate over the options in the group
        for (j = 0, option = group->options;
             j < group->num_options;
             j++, option++) {
            QString oKeyword = m_codec->toUnicode(option->keyword);
            QString oText = m_codec->toUnicode(option->text);
            QString oDefChoice = m_codec->toUnicode(option->defchoice);
            // The python system-config-printer skips this one
            // which has the same data as "PageSize", let's hope
            // they did the right thing
            if (oKeyword == QLatin1String("PageRegion")) {
                continue;
            }

            QWidget *optionW = 0;
            switch (option->ui) {
            case PPD_UI_BOOLEAN:
                optionW = pickBoolean(option, oKeyword, ui->scrollAreaWidgetContents);
                break;
            case PPD_UI_PICKMANY:
                optionW = pickMany(option, oKeyword, ui->scrollAreaWidgetContents);
                break;
            case PPD_UI_PICKONE:
                optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents);
                break;
            default:
                qCWarning(PM_CONFIGURE_PRINTER) << "Option type not recognized: " << option->ui;
                // let's use the most common
                optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents);
                break;
            }

            if (optionW) {
                // insert the option widget
                gFormLayout->addRow(oText, optionW);
            }
        }
    }
    ui->verticalLayout->addStretch();
}

QWidget* PrinterOptions::pickBoolean(ppd_option_t *option, const QString &keyword, QWidget *parent) const
{
    Q_UNUSED(keyword)
    auto widget = new QWidget(parent);
    auto layout = new QHBoxLayout(widget);
    auto radioGroup = new QButtonGroup(widget);
    widget->setLayout(layout);

    int i;
    ppd_choice_t *choice;
    QString defChoice = m_codec->toUnicode(option->defchoice);
    // Iterate over the choices in the option
    for (i = 0, choice = option->choices;
         i < option->num_choices;
         ++i, ++choice) {
        const QString choiceName = m_codec->toUnicode(choice->choice);
        const QString cText = m_codec->toUnicode(choice->text);

        auto button = new QRadioButton(cText, widget);
        button->setChecked(defChoice == choiceName);
        button->setProperty("choice", choiceName);
        // if we are in looking at a remote printer we can't save it
        button->setEnabled(!m_isRemote);
        layout->addWidget(button);
        radioGroup->addButton(button);
    }

    // store the default choice
    radioGroup->setProperty(DEFAULT_CHOICE, defChoice);
    radioGroup->setProperty("Keyword", keyword);
    connect(radioGroup, static_cast<void(QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked), this, &PrinterOptions::radioBtClicked);
    return widget;
}

void PrinterOptions::radioBtClicked(QAbstractButton *button)
{
    QObject *radioGroup = sender();
    bool isDifferent = radioGroup->property(DEFAULT_CHOICE).toString() != button->property("choice");

    if (isDifferent != radioGroup->property("different").toBool()) {
        // it's different from the last time so add or remove changes
        isDifferent ? m_changes++ : m_changes--;

        radioGroup->setProperty("different", isDifferent);
        emit changed(m_changes);
    }

    QString keyword = radioGroup->property("Keyword").toString();
    QString choice = button->property("choice").toString();
    radioGroup->setProperty("currentChoice", choice);

    // TODO warning about conflicts
//     ppdMarkOption(m_ppd,
//                   m_codec->fromUnicode(keyword),
//                   m_codec->fromUnicode(choice));
    // store the new value
    if (isDifferent) {
        m_customValues[keyword] = radioGroup;
    } else {
        m_customValues.remove(keyword);
    }
}

QWidget* PrinterOptions::pickMany(ppd_option_t *option, const QString &keyword, QWidget *parent) const
{
    Q_UNUSED(keyword)
    auto listView = new QListView(parent);
    auto model = new QStandardItemModel(listView);
    listView->setModel(model);
    listView->setItemDelegate(new NoSelectionRectDelegate(listView));

    int i;
    ppd_choice_t *choice;
    const QString oDefChoice = m_codec->toUnicode(option->defchoice);
    // Iterate over the choices in the option
    for (i = 0, choice = option->choices;
         i < option->num_choices;
         ++i, ++choice) {
        const QString cName = m_codec->toUnicode(choice->choice);
        const QString cText = m_codec->toUnicode(choice->text);

        auto item = new QStandardItem(cText);
        item->setData(cName);
        item->setCheckable(true);
        item->setEditable(false);
        // TODO there is only ONE default choice, what about the other
        // Items selected?!
        item->setCheckState(oDefChoice == cName ? Qt::Checked : Qt::Unchecked);
        model->appendRow(item);
    }
    // if we are in looking at a remote printer we can't save it
    listView->setEnabled(!m_isRemote);
    return qobject_cast<QWidget*>(listView);
}

QWidget* PrinterOptions::pickOne(ppd_option_t *option, const QString &keyword, QWidget *parent) const
{
    int i;
    ppd_choice_t *choice;
    const QString defChoice = m_codec->toUnicode(option->defchoice);
    auto comboBox = new QComboBox(parent);
    // Iterate over the choices in the option
    for (i = 0, choice = option->choices;
         i < option->num_choices;
         ++i, ++choice) {
        const QString cName = m_codec->toUnicode(choice->choice);
        const QString cText = m_codec->toUnicode(choice->text);

        comboBox->addItem(cText, cName);
    }
    // store the default choice
    comboBox->setProperty(DEFAULT_CHOICE, defChoice);
    comboBox->setProperty("Keyword", keyword);
    comboBox->setCurrentIndex(comboBox->findData(defChoice));
    // connect the signal AFTER setCurrentIndex is called
    connect(comboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &PrinterOptions::currentIndexChangedCB);
    // if we are in looking at a remote printer we can't save it
    comboBox->setEnabled(!m_isRemote);
    return qobject_cast<QWidget*>(comboBox);
}

void PrinterOptions::currentIndexChangedCB(int index)
{
    auto comboBox = qobject_cast<QComboBox*>(sender());
    bool isDifferent = comboBox->property(DEFAULT_CHOICE).toString() != comboBox->itemData(index);

    if (isDifferent != comboBox->property("different").toBool()) {
        // it's different from the last time so add or remove changes
        isDifferent ? m_changes++ : m_changes--;

        comboBox->setProperty("different", isDifferent);
        emit changed(m_changes);
    }

    QString keyword = comboBox->property("Keyword").toString();
    QString value = comboBox->itemData(index).toString();
    comboBox->setProperty("currentChoice", value);

    // TODO warning about conflicts
//     ppdMarkOption(m_ppd,
//                   m_codec->fromUnicode(keyword),
//                   m_codec->fromUnicode(value));
    // store the new value
    if (isDifferent) {
        m_customValues[keyword] = qobject_cast<QObject*>(comboBox);
    } else {
        m_customValues.remove(keyword);
    }
}

PrinterOptions::~PrinterOptions()
{
    if (m_ppd != NULL) {
        ppdClose(m_ppd);
    }

    if (!m_filename.isEmpty()) {
        unlink(qUtf8Printable(m_filename));
    }

    delete ui;
}

const char *                            /* O - Value of variable */
PrinterOptions::getVariable(const char *name)    const    /* I - Name of variable */
{
    const QString keyword = m_codec->toUnicode(name);
    auto it = m_customValues.constFind(keyword);
    if (it != m_customValues.constEnd()) {
        const QString value = it.value()->property("currentChoice").toString();
        return m_codec->fromUnicode(value).constData();
    } else {
        return NULL;
    }
}

/*
 * 'get_points()' - Get a value in points.
 */
double                           /* O - Number in points */
PrinterOptions::get_points(double     number,           /* I - Original number */
           const char *uval)            /* I - Units */
{
    if (!strcmp(uval, "mm"))              /* Millimeters */
        return (number * 72.0 / 25.4);
    else if (!strcmp(uval, "cm"))         /* Centimeters */
        return (number * 72.0 / 2.54);
    else if (!strcmp(uval, "in"))         /* Inches */
        return (number * 72.0);
    else if (!strcmp(uval, "ft"))         /* Feet */
        return (number * 72.0 * 12.0);
    else if (!strcmp(uval, "m"))          /* Meters */
        return (number * 72.0 / 0.0254);
    else                                  /* Points */
        return (number);
}

/*
 * 'get_option_value()' - Return the value of an option.
 *
 * This function also handles generation of custom option values.
 */

char *                           /* O - Value string or NULL on error */
PrinterOptions::get_option_value(
    ppd_file_t    *ppd,                 /* I - PPD file */
    const char    *name,                /* I - Option name */
    char          *buffer,              /* I - String buffer */
    size_t        bufsize) const             /* I - Size of buffer */
{
    char          *bufptr,                /* Pointer into buffer */
            *bufend;                /* End of buffer */
    ppd_coption_t *coption;               /* Custom option */
    ppd_cparam_t  *cparam;                /* Current custom parameter */
    char          keyword[256];           /* Parameter name */
    const char    *val,                   /* Parameter value */
            *uval;                  /* Units value */
    long          integer;                /* Integer value */
    double        number,                 /* Number value */
            number_points;          /* Number in points */


    /*
     * See if we have a custom option choice...
     */

    if ((val = getVariable(name)) == NULL) {
        /*
         * Option not found!
         */

        return (NULL);
    } else if (strcasecmp(val, "Custom") ||
               (coption = ppdFindCustomOption(ppd, name)) == NULL) {
        /*
         * Not a custom choice...
         */

        qstrncpy(buffer, val, bufsize);
        return (buffer);
    }

    /*
     * OK, we have a custom option choice, format it...
     */

    *buffer = '\0';

    if (!strcmp(coption->keyword, "PageSize")) {
        const char  *lval;                  /* Length string value */
        double      width,                  /* Width value */
                    width_points,           /* Width in points */
                    length,                 /* Length value */
                    length_points;          /* Length in points */


        val  = getVariable("PageSize.Width");
        lval = getVariable("PageSize.Height");
        uval = getVariable("PageSize.Units");

        if (!val || !lval || !uval ||
                (width = strtod(val, NULL)) == 0.0 ||
                (length = strtod(lval, NULL)) == 0.0 ||
                (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
                 strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) {
            return (NULL);
        }

        width_points  = get_points(width, uval);
        length_points = get_points(length, uval);

        if (width_points < ppd->custom_min[0] ||
                width_points > ppd->custom_max[0] ||
                length_points < ppd->custom_min[1] ||
                length_points > ppd->custom_max[1]) {
            return (NULL);
        }

        snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval);
    } else if (cupsArrayCount(coption->params) == 1) {
        cparam = ppdFirstCustomParam(coption);
        snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name);

        if ((val = getVariable(keyword)) == NULL)
            return (NULL);

        switch (cparam->type) {
        case PPD_CUSTOM_CURVE :
        case PPD_CUSTOM_INVCURVE :
        case PPD_CUSTOM_REAL :
            if ((number = strtod(val, NULL)) == 0.0 ||
                    number < cparam->minimum.custom_real ||
                    number > cparam->maximum.custom_real)
                return (NULL);

            snprintf(buffer, bufsize, "Custom.%g", number);
            break;
        case PPD_CUSTOM_INT :
            if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
                    integer == LONG_MAX ||
                    integer < cparam->minimum.custom_int ||
                    integer > cparam->maximum.custom_int)
                return (NULL);

            snprintf(buffer, bufsize, "Custom.%ld", integer);
            break;
        case PPD_CUSTOM_POINTS :
            snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);

            if ((number = strtod(val, NULL)) == 0.0 ||
                    (uval = getVariable(keyword)) == NULL ||
                    (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") &&
                     strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m")))
                return (NULL);

            number_points = get_points(number, uval);
            if (number_points < cparam->minimum.custom_points ||
                    number_points > cparam->maximum.custom_points)
                return (NULL);

            snprintf(buffer, bufsize, "Custom.%g%s", number, uval);
            break;
        case PPD_CUSTOM_PASSCODE :
            for (uval = val; *uval; ++uval) {
                if (!isdigit(*uval & 255)) {
                    return (NULL);
                }
            }
        case PPD_CUSTOM_PASSWORD :
        case PPD_CUSTOM_STRING :
            integer = (long)strlen(val);
            if (integer < cparam->minimum.custom_string ||
                    integer > cparam->maximum.custom_string) {
                return (NULL);
            }

            snprintf(buffer, bufsize, "Custom.%s", val);
            break;
        }
    } else {
        const char *prefix = "{";           /* Prefix string */


        bufptr = buffer;
        bufend = buffer + bufsize;

        for (cparam = ppdFirstCustomParam(coption);
             cparam;
             cparam = ppdNextCustomParam(coption)) {
            snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword,
                     cparam->name);

            if ((val = getVariable(keyword)) == NULL) {
                return (NULL);
            }

            snprintf(bufptr, bufend - bufptr, "%s%s=", prefix, cparam->name);
            bufptr += strlen(bufptr);
            prefix = " ";

            switch (cparam->type) {
            case PPD_CUSTOM_CURVE :
            case PPD_CUSTOM_INVCURVE :
            case PPD_CUSTOM_REAL :
                if ((number = strtod(val, NULL)) == 0.0 ||
                        number < cparam->minimum.custom_real ||
                        number > cparam->maximum.custom_real)
                    return (NULL);

                snprintf(bufptr, bufend - bufptr, "%g", number);
                break;
            case PPD_CUSTOM_INT :
                if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN ||
                        integer == LONG_MAX ||
                        integer < cparam->minimum.custom_int ||
                        integer > cparam->maximum.custom_int) {
                    return (NULL);
                }

                snprintf(bufptr, bufend - bufptr, "%ld", integer);
                break;
            case PPD_CUSTOM_POINTS :
                snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword);

                if ((number = strtod(val, NULL)) == 0.0 ||
                        (uval = getVariable(keyword)) == NULL ||
                        (strcmp(uval, "pt") && strcmp(uval, "in") &&
                         strcmp(uval, "ft") && strcmp(uval, "cm") &&
                         strcmp(uval, "mm") && strcmp(uval, "m"))) {
                    return (NULL);
                }

                number_points = get_points(number, uval);
                if (number_points < cparam->minimum.custom_points ||
                        number_points > cparam->maximum.custom_points) {
                    return (NULL);
                }

                snprintf(bufptr, bufend - bufptr, "%g%s", number, uval);
                break;

            case PPD_CUSTOM_PASSCODE :
                for (uval = val; *uval; uval ++) {
                    if (!isdigit(*uval & 255)) {
                        return (NULL);
                    }
                }
            case PPD_CUSTOM_PASSWORD :
            case PPD_CUSTOM_STRING :
                integer = (long)strlen(val);
                if (integer < cparam->minimum.custom_string ||
                        integer > cparam->maximum.custom_string) {
                    return (NULL);
                }

                if ((bufptr + 2) > bufend) {
                    return (NULL);
                }

                bufend --;
                *bufptr++ = '\"';

                while (*val && bufptr < bufend) {
                    if (*val == '\\' || *val == '\"') {
                        if ((bufptr + 1) >= bufend) {
                            return (NULL);
                        }

                        *bufptr++ = '\\';
                    }

                    *bufptr++ = *val++;
                }

                if (bufptr >= bufend) {
                    return (NULL);
                }

                *bufptr++ = '\"';
                *bufptr   = '\0';
                bufend ++;
                break;
            }

            bufptr += strlen(bufptr);
        }

        if (bufptr == buffer || (bufend - bufptr) < 2) {
            return (NULL);
        }

        strcpy(bufptr, "}");
    }

    return (buffer);
}


void PrinterOptions::save()
{
    char tempfile[1024];
    const char  *var;
    cups_file_t *in,                    /* Input file */
                *out;                   /* Output file */
    char        line[1024],             /* Line from PPD file */
                value[1024],            /* Option value */
                keyword[1024],          /* Keyword from Default line */
                *keyptr;                /* Pointer into keyword... */

    // copy cups-1.4.2/cgi-bin line 3779
    if (!m_filename.isEmpty()) {
        out = cupsTempFile2(tempfile, sizeof(tempfile));
        in  = cupsFileOpen(qUtf8Printable(m_filename), "r");

        if (!in || !out)
        {
            if (in) {
                cupsFileClose(in);
            }

            if (out) {
                cupsFileClose(out);
                unlink(tempfile);
            }

            // TODO add a KMessageBox::error

            return;
        }

        while (cupsFileGets(in, line, sizeof(line))) {
            if (!strncmp(line, "*cupsProtocol:", 14)) {
                continue;
            } else if (strncmp(line, "*Default", 8)) {
                cupsFilePrintf(out, "%s\n", line);
            } else {
                /*
                * Get default option name...
                */
                qstrncpy(keyword, line + 8, sizeof(keyword));

                for (keyptr = keyword; *keyptr; keyptr ++) {
                    if (*keyptr == ':' || isspace(*keyptr & 255)) {
                        break;
                    }
                }

                *keyptr = '\0';

                if (!strcmp(keyword, "PageRegion") ||
                    !strcmp(keyword, "PaperDimension") ||
                    !strcmp(keyword, "ImageableArea")) {
                    var = get_option_value(m_ppd, "PageSize", value, sizeof(value));
                } else {
                    var = get_option_value(m_ppd, keyword, value, sizeof(value));
                }

                if (!var) {
                    cupsFilePrintf(out, "%s\n", line);
                } else {
                    cupsFilePrintf(out, "*Default%s: %s\n", keyword, var);
                }
            }
        }

        cupsFileClose(in);
        cupsFileClose(out);
    }
    else {
        // TODO add a KMessageBox::error
        qCWarning(PM_CONFIGURE_PRINTER) << "No printer PPD file set, can't save options.";

        return;
    }

    QVariantHash values; // we need null values
    QPointer<KCupsRequest> request = new KCupsRequest;
    if (m_isClass) {
        request->addOrModifyClass(m_destName, values);
    } else {
        request->addOrModifyPrinter(m_destName, values, QString::fromUtf8(tempfile));
    }

    // Disable the widget till the request is processed
    // Otherwise the user might change something in the ui
    // which won't be saved but the apply but when the request
    // finishes we will set the current options as default
    setEnabled(false);
    request->waitTillFinished();

    // unlink the file
    unlink(tempfile);

    if (request) {
        setEnabled(true);
        if (!request->hasError()) {
            // if we succefully save the new ppd we need now to
            // clear our changes
            auto i = m_customValues.constBegin();
            while (i != m_customValues.constEnd()) {
                QObject *obj = i.value();
                const QString currentChoice = obj->property("currentChoice").toString();
                // Store the current choice as the default one
                obj->setProperty(DEFAULT_CHOICE, currentChoice);
                obj->setProperty("currentChoice", QVariant());
                obj->setProperty("different", false);
                ++i;
            }
            m_changes = 0;
            m_customValues.clear();
            emit changed(false);
        }
        request->deleteLater();
    }
}

bool PrinterOptions::hasChanges()
{
    return m_changes;
}

QString PrinterOptions::currentMake() const
{
    return m_make;
}

QString PrinterOptions::currentMakeAndModel() const
{
    return m_makeAndModel;
}

#include "moc_PrinterOptions.cpp"
