//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Util/UtilXML.h
//! @brief     Defines
//!
//! @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)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_GUI_MODEL_UTIL_UTILXML_H
#define BORNAGAIN_GUI_MODEL_UTIL_UTILXML_H

#include <QColor>
#include <QXmlStreamReader> // used in every including file, also provides QXmlStreamWriter

namespace XML {

constexpr const char* LinkMimeType{"application/org.bornagainproject.fittinglink"};

const QString minimal_supported_version = "22.0";

namespace Tag {

const QString BaseData("BaseData");

} // namespace Tag

namespace Attrib {

const QString BA_Version("BA_Version");
const QString collapsed("collapsed");
const QString id("id");
const QString index("index");
const QString kind("kind");
const QString name("name");
const QString projectName("projectName");
const QString type("type");
const QString value("value");
const QString units("units");

} // namespace Attrib

void gotoEndElementOfTag(QXmlStreamReader* reader, const QString& tag);

void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, bool b);
void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, double d);
void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, QString s);
void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, QColor c);

template <typename T>
void writeAttribute(QXmlStreamWriter* writer, const QString& attributeName, const T d)
{
    writer->writeAttribute(attributeName, QString::number(d));
};

template <typename T> void writeTaggedValue(QXmlStreamWriter* w, const QString& tag, const T value)
{
    w->writeStartElement(tag);
    writeAttribute(w, XML::Attrib::value, value);
    w->writeEndElement();
};

template <typename T>
void writeTaggedElement(QXmlStreamWriter* w, const QString& tag, const T& element)
{
    w->writeStartElement(tag);
    element.writeTo(w);
    w->writeEndElement();
}

template <typename T>
void writeBaseElement(QXmlStreamWriter* w, const QString& tag, const T* element)
{
    w->writeStartElement(tag);
    element->T::writeTo(w);
    w->writeEndElement();
}

bool readBool(QXmlStreamReader* reader, const QString& attributeName);
int readInt(QXmlStreamReader* reader, const QString& attributeName);
unsigned readUInt(QXmlStreamReader* reader, const QString& attributeName);
double readDouble(QXmlStreamReader* reader, const QString& attributeName);
QString readString(QXmlStreamReader* reader, const QString& attributeName);

bool readTaggedBool(QXmlStreamReader* reader, const QString& tag);
int readTaggedInt(QXmlStreamReader* reader, const QString& tag);
unsigned readTaggedUInt(QXmlStreamReader* reader, const QString& tag);
double readTaggedDouble(QXmlStreamReader* reader, const QString& tag);
QString readTaggedString(QXmlStreamReader* reader, const QString& tag);
QColor readTaggedColor(QXmlStreamReader* reader, const QString& tag);

template <typename T, typename... Args>
void readTaggedElement(QXmlStreamReader* r, const QString& tag, T& element)
{
    element.readFrom(r);
    gotoEndElementOfTag(r, tag);
}

template <typename T> void readBaseElement(QXmlStreamReader* r, const QString& tag, T* element)
{
    element->T::readFrom(r);
    gotoEndElementOfTag(r, tag);
}

template <typename B, typename C, typename... Args>
B* readItemFrom(QXmlStreamReader* r, Args... args)
{
    const uint typeIndex = readUInt(r, XML::Attrib::type);
    const QString kind = readString(r, XML::Attrib::kind);
    const auto type = static_cast<typename C::Type>(typeIndex);
    B* t = C::create(type, args...);
    if (t)
        t->readFrom(r);
    return t;
}

template <typename B, typename C, typename... Args>
B* readChosen(QXmlStreamReader* r, const QString& tag, Args... args)
{
    B* t = readItemFrom<B, C>(r, args...);
    gotoEndElementOfTag(r, tag);
    return t;
}

template <typename B, typename C> void writeItemTo(const B* t, QXmlStreamWriter* w)
{
    const uint typeIndex = static_cast<uint>(C::type(t));
    writeAttribute(w, Attrib::type, typeIndex);
    // The next line allows to see the name of item type in XML. May be skipped while reading.
    writeAttribute(w, Attrib::kind, C::uiInfo(C::type(t)).menuEntry);
    if (t)
        t->writeTo(w);
}

template <typename B, typename C>
void writeChosen(const B* t, QXmlStreamWriter* w, const QString& tag)
{
    w->writeStartElement(tag);
    writeItemTo<B, C>(t, w);
    w->writeEndElement();
}

} // namespace XML

#endif // BORNAGAIN_GUI_MODEL_UTIL_UTILXML_H
