//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Device/InstrumentLibrary.cpp
//! @brief     Implements class InstrumentLibrary
//!
//! @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/Device/InstrumentLibrary.h"
#include "GUI/Model/Device/BackgroundItems.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/Support/Util/Path.h"
#include "GUI/Support/XML/DeserializationException.h"
#include <QFile>

namespace {

const QString XML_ROOT_TAG = "BornAgainInstrumentLibrary";
const QString XML_VERSION_TAG = "Version";
const QString XML_INSTRUMENT_COLLECTION_TAG = "InstrumentCollection";

QString instrumentLibraryFilePath()
{
    return GUI::Base::Path::appDataFolder() + "/BornAgainInstrumentLibrary.balib";
}

} // namespace


InstrumentLibrary::InstrumentLibrary()
    : m_ec(&m_instrumentModel)
    , m_modified(false)
{
    QObject::connect(&m_ec, &MultiInstrumentNotifier::instrumentChanged,
                     [&]() { m_modified = true; });
    QObject::connect(&m_ec, &MultiInstrumentNotifier::instrumentAddedOrRemoved,
                     [&]() { m_modified = true; });
}

QString InstrumentLibrary::suggestName(const QString& name) const
{
    return m_instrumentModel.suggestInstrumentName(name);
}

InstrumentItem* InstrumentLibrary::addItemCopy(const QString& name,
                                               const InstrumentItem& itemToCopy)
{
    return m_ec.addInstrumentItemCopy(&itemToCopy, name);
}

bool InstrumentLibrary::isEmpty() const
{
    return m_instrumentModel.instrumentItems().isEmpty();
}

QList<InstrumentItem*> InstrumentLibrary::instrumentItems() const
{
    return m_instrumentModel.instrumentItems().toList();
}

MultiInstrumentNotifier* InstrumentLibrary::editController()
{
    return &m_ec;
}

bool InstrumentLibrary::saveIfModified()
{
    if (!m_modified)
        return true;

    QFile file(instrumentLibraryFilePath());
    if (!file.open(QFile::ReadWrite | QIODevice::Truncate | QFile::Text))
        return false;

    QXmlStreamWriter writer(&file);
    writer.setAutoFormatting(true);
    writer.writeStartDocument();
    writer.writeStartElement(XML_ROOT_TAG);
    writer.writeAttribute(XML_VERSION_TAG, "2");

    writer.writeStartElement(XML_INSTRUMENT_COLLECTION_TAG);
    m_instrumentModel.writeTo(&writer);
    writer.writeEndElement();

    writer.writeEndElement();
    writer.writeEndDocument();

    file.close();
    m_modified = false;
    return true;
}

bool InstrumentLibrary::load()
{
    m_modified = false;
    m_instrumentModel.clear();

    QFile file(instrumentLibraryFilePath());
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return false;

    try {
        QXmlStreamReader reader(&file);

        while (!reader.atEnd()) {
            reader.readNext();
            if (reader.isStartElement()) {
                if (reader.name() == XML_ROOT_TAG) {
                    const int foundVersion =
                        reader.attributes().value(XML_VERSION_TAG).toString().toInt();
                    if (foundVersion != 2)
                        return false;

                    reader.readNextStartElement();
                    if (reader.name().toString() == XML_INSTRUMENT_COLLECTION_TAG) {
                        m_instrumentModel.readFrom(&reader);
                        XML::gotoEndElementOfTag(&reader, XML_INSTRUMENT_COLLECTION_TAG);
                    }
                }
            }
        }

        if (reader.hasError()) {
            m_instrumentModel.clear();
            return false;
        }

        file.close();
        return true;
    } catch (const std::exception&) {
        m_instrumentModel.clear();
        return false;
    } catch (const DeserializationException&) {
        m_instrumentModel.clear();
        return false;
    }
}

InstrumentModel* InstrumentLibrary::instrumentModel()
{
    return &m_instrumentModel;
}
