//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Util/UtilXML.cpp
//! @brief     Implements
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/Model/Util/UtilXML.h"
#include "Base/Util/Assert.h"
#include <QColor>
#include <QUuid>

namespace {

void assertCurrentTag(QXmlStreamReader* reader, const QString& expectedTag)
{

    if (reader->name() != expectedTag)
        throw std::runtime_error("Found tag '" + reader->name().toString().toStdString()
                                 + "' instead of expected expected tag '"
                                 + expectedTag.toStdString() + "'");
}

void assertCurrentToken(QXmlStreamReader* reader, QXmlStreamReader::TokenType token)
{
    if (reader->tokenType() != token)
        throw std::runtime_error("Found unexpected token type");
}

} // namespace


void XML::gotoEndElementOfTag(QXmlStreamReader* reader, const QString& tag)
{
    ASSERT(reader);
    if (reader->name() != tag) {
        if (!reader->isEndElement())
            reader->skipCurrentElement();
        reader->skipCurrentElement();
    }
    assertCurrentTag(reader, tag);
    if (!reader->isEndElement())
        reader->skipCurrentElement();

    assertCurrentToken(reader, QXmlStreamReader::EndElement);
    assertCurrentTag(reader, tag);
}

// ------------ write atribute ------------

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, bool b)
{
    writer->writeAttribute(attributeName, b ? "1" : "0");
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, int d)
{
    writer->writeAttribute(attributeName, QString::number(d));
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, size_t d)
{
    writer->writeAttribute(attributeName, QString::number(d));
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, unsigned d)
{
    writer->writeAttribute(attributeName, QString::number(d));
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, double d)
{
    writer->writeAttribute(attributeName, d == 0.0 ? "0" : QString::number(d, 'e', 12));
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, QString s)
{
    writer->writeAttribute(attributeName, s);
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, QColor c)
{
    writer->writeAttribute(attributeName, c.isValid() ? c.name(QColor::HexArgb) : "");
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeBaseName, const R3& vec)
{
    writeAttribute(writer, attributeBaseName + "X", vec.x());
    writeAttribute(writer, attributeBaseName + "Y", vec.y());
    writeAttribute(writer, attributeBaseName + "Z", vec.z());
}

void XML::writeAttribute(QXmlStreamWriter* writer, const QString& attributeBaseName,
                         const complex_t& c)
{
    writeAttribute(writer, attributeBaseName + "Re", c.real());
    writeAttribute(writer, attributeBaseName + "Im", c.imag());
}

// ------------ read atribute ------------

unsigned XML::readUIntAttribute(QXmlStreamReader* reader, const QString& attributeName)
{
    return reader->attributes().value(attributeName).toUInt();
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, bool* b)
{
    *b = reader->attributes().value(attributeName).toUInt() > 0;
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, int* i)
{
    *i = reader->attributes().value(attributeName).toInt();
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, size_t* i)
{
    *i = reader->attributes().value(attributeName).toULong();
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, unsigned* u)
{
    *u = readUIntAttribute(reader, attributeName);
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, double* d)
{
    *d = reader->attributes().value(attributeName).toDouble();
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, QString* s)
{
    *s = reader->attributes().value(attributeName).toString();
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeName, QColor* c)
{
    const QString parameter_value = reader->attributes().value(attributeName).toString();
    *c = QColor(parameter_value);
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeBaseName, R3* vec)
{
    double x, y, z;
    readAttribute(reader, attributeBaseName + "X", &x);
    readAttribute(reader, attributeBaseName + "Y", &y);
    readAttribute(reader, attributeBaseName + "Z", &z);
    *vec = {x, y, z};
}

void XML::readAttribute(QXmlStreamReader* reader, const QString& attributeBaseName, complex_t* c)
{
    double r, i;
    readAttribute(reader, attributeBaseName + "Re", &r);
    readAttribute(reader, attributeBaseName + "Im", &i);
    c->real(r);
    c->imag(i);
}

QString XML::readAttributeString(QXmlStreamReader* reader, const QString& attributeName)
{
    return reader->attributes().value(attributeName).toString();
}
