/*
 * 	    ppdfile.cpp               (C) 2006-2008, Aurélien Croc (AP²C)
 *
 *  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; version 2 of the License.
 * 
 *  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; if not, write to the
 *  Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 *  $Id: ppdfile.cpp 301 2012-02-29 14:11:09Z tillkamppeter $
 * 
 */
#include "ppdfile.h"
#include <cups/cups.h>
#include <string.h>
#include <ctype.h>
#include "errlog.h"



/*
 * Constructeur - Destructeur
 * Init - Uninit
 */
PPDFile::PPDFile()
{
    _ppd = NULL;
}

PPDFile::~PPDFile()
{
    close();
}



/*
 * Ouverture et fermeture du fichier PPD
 * Opening or closing PPD file
 */
bool PPDFile::open(const char *file, const char *version, const char *useropts)
{
    const char *fileVersion;
    cups_option_t *options;
    int nr;

    // Check if a PPD file was previously opened
    if (_ppd) {
        ERRORMSG(_("Internal warning: a PPD file was previously opened. Please "
            "close it before opening a new one."));
        close();
    }

    // Open the PPD file
    _ppd = ppdOpenFile(file);
    if (!_ppd) {
        ERRORMSG(_("Cannot open PPD file %s"), file);
        return false;
    }

    // Mark the default values and the user options
    ppdMarkDefaults(_ppd);
    nr = cupsParseOptions(useropts, 0, &options);
    cupsMarkOptions(_ppd, nr, options);
    cupsFreeOptions(nr, options);

    // Check if the PPD version is compatible with this filter
    fileVersion = get("FileVersion");
    if (!fileVersion) { 
        ERRORMSG(_("No FileVersion found in the PPD file: invalid "
            "PPD file"));
        ppdClose(_ppd);
        _ppd = NULL;
        return false;
    }
    if (strcmp(fileVersion, version)) {
        ERRORMSG(_("Invalid PPD file version: %s but the PPD file "
            "is designed for SpliX V. %s"), version, fileVersion);
        ppdClose(_ppd);
        _ppd = NULL;
        return false;
    }

    return true;
}

void PPDFile::close()
{
    if (!_ppd)
        return;
    ppdClose(_ppd);
    _ppd = NULL;
}




/*
 * Obtention d'une donnée du fichier PPD
 * Get a string from the PPD file
 */
PPDValue PPDFile::get(const char *name, const char *opt)
{
    ppd_attr_t *attr;
    PPDValue val;

    if (!_ppd) {
        ERRORMSG(_("Trying to read a PPD file which wasn't opened"));
        return val;
    }

    if (!opt)
        attr = ppdFindAttr(_ppd, name, NULL);
    else
        attr = ppdFindAttr(_ppd, opt, name);
    if (!attr) {
        ppd_choice_t *choice;
        choice = ppdFindMarkedChoice(_ppd, name);
        if (!choice)
            return val;
        val.set(choice->choice);
    } else
        val.set(attr->value);
    return val;
}

PPDValue PPDFile::getPageSize(const char *name)
{
    ppd_size_t *s;
    PPDValue val;

    if (!(s = ppdPageSize(_ppd, name)))
        return val;
    val.set(s->width, s->length, s->left, s->bottom);
    return val;
}



/*
 * Gestion des valeurs du PPD
 * PPD values management
 */
PPDFile::Value::Value()
{
    _value = NULL;
    _out = NULL;
    _preformatted = NULL;
    _width = 0.;
    _height = 0.;
    _marginX = 0.;
    _marginY = 0.;
}

PPDFile::Value::Value(const char *value)
{
    _value = value;
    _out = value;
    _preformatted = NULL;
    _width = 0.;
    _height = 0.;
    _marginX = 0.;
    _marginY = 0.;
}

PPDFile::Value::~Value()
{
    if (_preformatted)
        delete[] _preformatted;
}

PPDFile::Value& PPDFile::Value::set(const char *value)
{
    if (_preformatted) {
        delete[] _preformatted;
        _preformatted = NULL;
    }
    _value = value;
    _out = _value;

    return *this;
}

PPDFile::Value& PPDFile::Value::set(float width, float height, float marginX,
    float marginY)
{
    _width = width;
    _height = height;
    _marginX = marginX;
    _marginY = marginY;

    return *this;
}

PPDFile::Value& PPDFile::Value::setPreformatted()
{
    const char *str = _value;
    unsigned int i;

    if (!str)
        return *this;

    _preformatted = new char[strlen(str)];
    for (i=0; *str; str++) {
        if (*str == '<' && strlen(str) >= 3 && isxdigit(*(str+1))) {
            char temp[3] = {0, 0, 0};

            str++;
            temp[0] = *str;
            str++;
            if (*str != '>' && (!isxdigit(*str) || *(str + 1) != '>')) {
                _preformatted[i] = '<';
                _preformatted[i+1] = temp[0];
                i += 2;
                continue;
            }
            if (*str != '>') {
                temp[1] = *str;
                str++;
            }
            _preformatted[i] = (char)strtol((char *)&temp, (char **)NULL,16);
            i++;
            continue;
        }
        _preformatted[i] = *str;
        i++;
    }
    _preformatted[i] = 0;
    _out = _preformatted;

    return *this;
}

char* PPDFile::Value::deepCopy() const
{
    char *tmp;

    if (!_out)
        return NULL;
    tmp = new char[strlen(_out)+1];
    strcpy(tmp, _out);

    return tmp;
}

bool PPDFile::Value::isTrue() const
{
    if (!_out)
        return false;
    if (!strcmp(_out, "1") || !strcasecmp(_out, "true") || 
        !strcasecmp(_out, "enable") || !strcasecmp(_out, "enabled") || 
        !strcasecmp(_out, "yes") || !strcasecmp(_out, "on"))
        return true;
    return false;
}

bool PPDFile::Value::operator == (const char* val) const
{
    if (!_out)
        return false;
    return !strcasecmp(_out, val);
}

bool PPDFile::Value::operator != (const char* val) const
{
    if (!_out)
        return true;
    return strcasecmp(_out, val);
}



/*
 * Opérateur d'assignation
 * Assignment operator
 */
void PPDFile::Value::operator = (const PPDFile::Value &val)
{
    if (_preformatted)
        delete[] _preformatted;
    _value = val._value;
    _out = val._out;
    _preformatted = val._preformatted;
    _width = val._width;
    _height = val._height;
    _marginX = val._marginX;
    _marginY = val._marginY;
}


/* vim: set expandtab tabstop=4 shiftwidth=4 smarttab tw=80 cin enc=utf8: */

