/***************************************************************************
 *   Copyright 2007-2008 Wouter Haffmans <cheetah@simply-life.net>         *
 *                                                                         *
 *   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) version 3 or any later version        *
 *   accepted by the membership of KDE e.V. (or its successor approved     *
 *   by the membership of KDE e.V.), which shall act as a proxy            *
 *   defined in Section 14 of version 3 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, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#include "palettemanagerxml.h"

#include <QDomNode>
#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QTextStream>

#include <QtDebug>

PaletteManagerXML::PaletteManagerXML(QObject *parent)
 : PaletteManager(parent)
{
    m_document = new QDomDocument();

    // Generate necessary KidDraw element to be sure it exists
    kidDrawElement();
}
//------------------------------------------------------------------------------

PaletteManagerXML::PaletteManagerXML(QString fileName, QObject *parent)
 : PaletteManager(parent)
{
    m_document = new QDomDocument();
    loadFromFile(fileName);
}
//------------------------------------------------------------------------------

PaletteManagerXML::~PaletteManagerXML()
{
    delete m_document;
}
//------------------------------------------------------------------------------

bool PaletteManagerXML::loadFromFile(QString file)
{
    QFile f(file);
    if (!f.open(QIODevice::ReadOnly)) {
        return false;
    }
    QString error;
    int errorLine, errorColumn;
    if (!m_document->setContent(&f, false, &error, &errorLine, &errorColumn)) {
        qWarning() << "Error \"" << error << "\" in XML file " << file <<
                ", line " << errorLine << ", column " << errorColumn << ".";
        f.close();
        return false;
    }
    f.close();

    if (readDom()) {
        return true;
    }
    else {
        // Reset dom document
        delete m_document;
        m_document = new QDomDocument();
        return false;
    }
}
//------------------------------------------------------------------------------

bool PaletteManagerXML::saveToFile(QString file) const
{
    if (checkKidDrawElement()) {
        return false;
    }

    QFile f(file);
    if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        return false;
    }

    QTextStream ts(&f);
    ts << m_document->toString(2);

    f.close();

    return true;
}
//------------------------------------------------------------------------------

void PaletteManagerXML::addColor(QString name, Color color)
{
    PaletteManager::addColor(name, color);

    QDomElement colorsNode = colorsElement();
    QDomElement oldElement, newElement;

    // Check if color already exists in DOM tree
    oldElement = colorsNode.firstChildElement("color");
    while (!oldElement.isNull() && oldElement.attribute("name") != name) {
        oldElement = oldElement.nextSiblingElement("color");
    }

    newElement = createColorElement(color, name);

    if (oldElement.isNull()) {
        colorsNode.appendChild(newElement);
    }
    else {
        colorsNode.replaceChild(newElement, oldElement);
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::addPalette(QString name, Palette* palette)
{
    PaletteManager::addPalette(name, palette);

    QDomElement palettesNode = palettesElement();
    QDomElement oldElement, newElement;

    // Check if palette already exists in DOM tree
    oldElement = palettesNode.firstChildElement("palette");
    while (!oldElement.isNull() && oldElement.attribute("name") != name) {
        oldElement = oldElement.nextSiblingElement("palette");
    }

    newElement = createPaletteElement(palette, name);

    // Insert/replace palette
    if (oldElement.isNull()) {
        palettesNode.appendChild(newElement);
    }
    else {
        palettesNode.replaceChild(newElement, oldElement);
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::clearColors()
{
    PaletteManager::clearColors();
    domClearColors();
}
//------------------------------------------------------------------------------

void PaletteManagerXML::domClearColors()
{
    QDomElement c = colorsElement();
    QDomElement child = c.firstChildElement("color");
    QDomElement nextChild;
    while (!child.isNull()) {
        nextChild = child.nextSiblingElement("color");
        c.removeChild(child);
        child = nextChild;
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::clearPalettes()
{
    PaletteManager::clearPalettes();
    domClearPalettes();
}
//------------------------------------------------------------------------------

void PaletteManagerXML::domClearPalettes()
{
    QDomElement p = palettesElement();
    QDomElement child = p.firstChildElement("palette");
    QDomElement nextChild;
    while (!child.isNull()) {
        nextChild = child.nextSiblingElement("palette");
        p.removeChild(child);
        child = nextChild;
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::removeColor(Color color)
{
    PaletteManager::removeColor(color);

    QDomElement c = colorsElement();
    QDomElement child = c.firstChildElement("color");
    QDomElement nextChild;
    while (!child.isNull()) {
        QColor value = elementToQColor(child.firstChildElement("value"));
        QColor border = elementToQColor(child.firstChildElement("border"));
        QColor background = elementToQColor(
                child.firstChildElement("background"));
        if (color.color == value && color.border == border &&
                color.background == background) {
            nextChild = child.nextSiblingElement("color");
            c.removeChild(child);
            child = nextChild;
        }
        else {
            child = child.nextSiblingElement("color");
        }
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::removeColor(QString name)
{
    PaletteManager::removeColor(name);

    QDomElement c = colorsElement();
    QDomElement child = c.firstChildElement("color");
    QDomElement nextChild;
    while (!child.isNull()) {
        if (child.attribute("name") == name) {
            nextChild = child.nextSiblingElement("color");
            c.removeChild(child);
            child = nextChild;
        }
        else {
            child = child.nextSiblingElement("color");
        }
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::removePalette(QString name)
{
    PaletteManager::removePalette(name);

    QDomElement p = palettesElement();
    QDomElement child = p.firstChildElement("palette");
    QDomElement nextChild;
    while (!child.isNull()) {
        if (child.attribute("name") == name) {
            nextChild = child.nextSiblingElement("palette");
            p.removeChild(child);
            child = nextChild;
        }
        else {
            child = child.nextSiblingElement("palette");
        }
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::setColors(QMap<QString, Color> colors)
{
    PaletteManager::setColors(colors);

    domSetColors(colors);
}
//------------------------------------------------------------------------------

void PaletteManagerXML::domSetColors(QMap<QString, Color> colors)
{
    domClearColors();
    QDomElement c = colorsElement();
    for (QMap<QString, Color>::const_iterator i = colors.constBegin();
         i != colors.constEnd(); ++i) {
        c.appendChild(createColorElement(i.value(), i.key()));
    }
}

//------------------------------------------------------------------------------
void PaletteManagerXML::setPalettes(QMap<QString, Palette *> palettes)
{
    PaletteManager::setPalettes(palettes);

    domSetPalettes(palettes);
}
//------------------------------------------------------------------------------

void PaletteManagerXML::domSetPalettes(QMap<QString, Palette *> palettes)
{
    domClearPalettes();
    QDomElement p = palettesElement();
    for (QMap<QString, Palette *>::const_iterator i = palettes.constBegin();
         i != palettes.constEnd(); ++i) {
        p.appendChild(createPaletteElement(i.value(), i.key()));
    }
}
//------------------------------------------------------------------------------

bool PaletteManagerXML::checkKidDrawElement() const
{
    QDomElement e = m_document->documentElement();
    if (!e.isNull()) {
        // Invalid document if root element isn't "kidDraw" tag - return invalid
        // element
        return (e.tagName() == "kidDraw");
    }
    else {
        return false;
    }
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::kidDrawElement()
{
    QDomElement e = m_document->documentElement();
    if (e.tagName() != "kidDraw") {
        // Invalid document if root element isn't "kidDraw" tag - return invalid
        // element
        return QDomElement();
    }

    if (e.isNull()) {
        // Create node
        QDomElement e = m_document->createElement("kidDraw");
        e.setAttribute("version", "2.0");
        e = m_document->appendChild(e).toElement();
    }

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::colorsElement()
{
    QDomElement e = kidDrawElement().firstChildElement("colors");

    if (e.isNull()) {
        // Create node
        QDomElement e = m_document->createElement("colors");
        e = kidDrawElement().appendChild(e).toElement();
    }

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::palettesElement()
{
    if (kidDrawElement().isNull()) {
        return QDomElement();
    }

    QDomElement e = kidDrawElement().firstChildElement("palettes");

    if (e.isNull()) {
        // Create node
        QDomElement e = m_document->createElement("palettes");
        e = kidDrawElement().appendChild(e).toElement();
    }

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::createColorDomElement
        (QDomDocument document, Color color, QString name)
{
    QDomElement e = document.createElement("color");
    if (!name.isEmpty()) {
        e.setAttribute("name", name);
    }

    e.appendChild(colorToDomElement(document, color.color, "value"));
    e.appendChild(colorToDomElement(document, color.border, "border"));
    e.appendChild(colorToDomElement(document, color.background, "background"));

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::createColorElement(Color color, QString name)
{
    return createColorDomElement(*m_document, color, name);
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::createPaletteElement(Palette *palette,
        QString name)
{
    QDomElement e = m_document->createElement("palette");
    if (!name.isEmpty()) {
        e.setAttribute("name", name);
    }

    // Add colors
    for (Palette::const_iterator i = palette->constBegin();
         i != palette->constEnd(); ++i) {
        // Find color in color list - if so, use name, otherwise add color
        QString name;
        for (QMap<QString, Color>::const_iterator j = colors().constBegin();
             j != colors().constEnd(); ++j) {
            if ((j.value().color == (*i).color) &&
                (j.value().border == (*i).border) &&
                (j.value().background == (*i).background)) {
                name = j.key();
            }
        }

        if (name.isEmpty()) {
            e.appendChild(createColorElement(*i));
        }
        else {
            QDomElement c = m_document->createElement("useColor");
            c.setAttribute("value", name);
            e.appendChild(c);
        }
    }

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::colorToDomElement
        (QDomDocument document, QColor color, QString name, QColor::Spec spec)
{
    if (!color.isValid()) {
        return QDomElement();
    }

    if (spec != QColor::Invalid && spec != color.spec()) {
        color = color.convertTo(spec);
    }

    QDomElement e;

    switch(color.spec()) {
    default:
        break;

    case QColor::Rgb:
        e = document.createElement(name);
        e.setAttribute("spec", "rgb");

        e.appendChild(createTextElement(document, "red", QString::number(color.red())));
        e.appendChild(createTextElement(document, "green",
                QString::number(color.green())));
        e.appendChild(createTextElement(document, "blue", QString::number(color.blue())));
        e.appendChild(createTextElement(document, "alpha",
                QString::number(color.alpha())));
        break;

    case QColor::Hsv:
        e = document.createElement(name);
        e.setAttribute("spec", "hsv");

        e.appendChild(createTextElement(document, "hue", QString::number(color.hue())));
        e.appendChild(createTextElement(document, "saturation",
                QString::number(color.saturation())));
        e.appendChild(createTextElement(document, "value",
                QString::number(color.value())));
        e.appendChild(createTextElement(document, "alpha",
                QString::number(color.alpha())));
        break;

    case QColor::Cmyk:
        e = document.createElement(name);
        e.setAttribute("spec", "cmyk");

        e.appendChild(createTextElement(document, "cyan", QString::number(color.cyan())));
        e.appendChild(createTextElement(document, "magenta",
                QString::number(color.magenta())));
        e.appendChild(createTextElement(document, "yellow",
                QString::number(color.yellow())));
        e.appendChild(createTextElement(document, "black",
                QString::number(color.black())));
        e.appendChild(createTextElement(document, "alpha",
                QString::number(color.alpha())));
        break;
    }

    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::colorToElement(QColor color, QString name,
        QColor::Spec spec)
{
    return colorToDomElement(*m_document, color, name, spec);
}
//------------------------------------------------------------------------------

QColor PaletteManagerXML::elementToQColor(QDomElement element)
{
    QColor c;
    QDomElement e;

    if (element.attribute("spec") == "rgb") {
        int r, g, b, a;

        r = readElementInt(element, "red", 0);
        g = readElementInt(element, "green", 0);
        b = readElementInt(element, "blue", 0);
        a = readElementInt(element, "alpha", 255);

        c.setRgb(r, g, b, a);
    }
    else if (element.attribute("spec") == "hsv") {
        int h, s, v, a;

        h = readElementInt(element, "hue", 0);
        s = readElementInt(element, "saturation", 0);
        v = readElementInt(element, "value", 0);
        a = readElementInt(element, "alpha", 255);

        c.setHsv(h, s, v, a);
    }
    else if (element.attribute("spec") == "cmyk") {
        int cc, m, y, k, a;

        cc = readElementInt(element, "cyan", 0);
        m = readElementInt(element, "magenta", 0);
        y = readElementInt(element, "yellow", 0);
        k = readElementInt(element, "black", 0);
        a = readElementInt(element, "alpha", 255);

        c.setCmyk(cc, m, y, k, a);
    }
    else if (element.attribute("spec") == "name") {
        c.setNamedColor(element.text());
    }

    return c;
}
//------------------------------------------------------------------------------

Color PaletteManagerXML::elementToColor(QDomElement element)
{
    Color c;
    QDomElement e;
    c.color = elementToQColor(element.firstChildElement("value"));
    c.border = elementToQColor(element.firstChildElement("border"));
    c.background = elementToQColor(element.firstChildElement("background"));

    return c;
}
//------------------------------------------------------------------------------

int PaletteManagerXML::readElementInt(QDomElement p, QString name, int defValue)
{
    QDomElement e = p.firstChildElement(name);
    int r = defValue;
    bool ok;
    if (!e.isNull()) {
        r = e.text().toInt(&ok);
    }
    else {
        r = defValue;
    }

    if (!ok) {
        r = defValue;
    }
    return r;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::createTextElement
        (QDomDocument document, QString name, QString contents)
{
    QDomElement e = document.createElement(name);
    QDomText et = document.createTextNode(contents);
    e.appendChild(et);
    return e;
}
//------------------------------------------------------------------------------

QDomElement PaletteManagerXML::createTextElement(QString name, QString contents)
{
    return createTextElement(*m_document, name, contents);
}
//------------------------------------------------------------------------------

bool PaletteManagerXML::readDom()
{
    if (checkKidDrawElement()) {
        readDomColors();
        readDomPalettes();
        return true;
    }
    else {
        return false;
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::readDomColors()
{
    QDomElement c = colorsElement();
    QDomElement child = c.firstChildElement("color");
    while (!child.isNull()) {
        PaletteManager::addColor(child.attribute("name"),
                elementToColor(child));
        child = child.nextSiblingElement("color");
    }
}
//------------------------------------------------------------------------------

void PaletteManagerXML::readDomPalettes()
{
    QDomElement p = palettesElement();
    QDomElement child = p.firstChildElement("palette");
    while (!child.isNull()) {
        QString name = child.attribute("name");
        Palette *palette = new Palette();
        QDomNode c = child.firstChild();
        while (!c.isNull()) {
            if (c.isElement()) {
                if (c.toElement().tagName() == "color") {
                    palette->append(elementToColor(c.toElement()));
                }
                else if (c.toElement().tagName() == "useColor") {
                    palette->append(colors().value(
                            c.toElement().attribute("value")));
                }
            }
            c = c.nextSibling();
        }
        PaletteManager::addPalette(name, palette);
        child = child.nextSiblingElement("palette");
    }
}
//------------------------------------------------------------------------------

#include "palettemanagerxml.moc"
