//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Sample/ProfileItems.cpp
//! @brief     Implements classes Profile1DItem, Profile2DItem, and subclasses.
//!
//! @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/Sample/ProfileItems.h"
#include "Base/Const/Units.h"
#include "GUI/Model/Util/UtilXML.h"

namespace {
namespace Tag {

const QString Omega("Omega");
const QString OmegaX("OmegaX");
const QString OmegaY("OmegaY");
const QString Eta("Eta");
const QString Gamma("Gamma");
const QString BaseData("BaseData");

} // namespace Tag
} // namespace

Profile1DItem::Profile1DItem()
{
    m_omega.init("Omega (nm)", "Half-width of the distribution", 1.0, "omega");
}

void Profile1DItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));
    m_omega.writeTo(w, Tag::Omega);
}

void Profile1DItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        if (tag == Tag::Omega)
            m_omega.readFrom(r, tag);
        else
            r->skipCurrentElement();
    }
}

DoubleProperties Profile1DItem::profileProperties()
{
    return {&m_omega};
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile1D> Profile1DCauchyItem::createProfile() const
{
    return std::make_unique<Profile1DCauchy>(m_omega.value());
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile1D> Profile1DGaussItem::createProfile() const
{
    return std::make_unique<Profile1DGauss>(m_omega.value());
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile1D> Profile1DGateItem::createProfile() const
{
    return std::make_unique<Profile1DGate>(m_omega.value());
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile1D> Profile1DTriangleItem::createProfile() const
{
    return std::make_unique<Profile1DTriangle>(m_omega.value());
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile1D> Profile1DCosineItem::createProfile() const
{
    return std::make_unique<Profile1DCosine>(m_omega.value());
}

// --------------------------------------------------------------------------------------------- //

Profile1DVoigtItem::Profile1DVoigtItem()
{
    m_eta.init("Eta", "Parameter [0,1] to balance between Cauchy (eta=0.0) and Gauss (eta=1.0)",
               0.5, 3, RealLimits::limited(0.0, 1.0), "eta");
}

std::unique_ptr<IProfile1D> Profile1DVoigtItem::createProfile() const
{
    return std::make_unique<Profile1DVoigt>(m_omega.value(), m_eta.value());
}

void Profile1DVoigtItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    Profile1DItem::writeTo(w);
    w->writeEndElement();

    // eta
    w->writeStartElement(Tag::Eta);
    m_eta.writeTo(w);
    w->writeEndElement();
}

void Profile1DVoigtItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            Profile1DItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // eta
        } else if (tag == Tag::Eta) {
            m_eta.readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

DoubleProperties Profile1DVoigtItem::profileProperties()
{
    return Profile1DItem::profileProperties() + DoubleProperties{&m_eta};
}

// --------------------------------------------------------------------------------------------- //

Profile2DItem::Profile2DItem()
{
    m_omegaX.init("OmegaX (nm)", "Half-width of the distribution along its x-axis", 1.0, "omegaX");
    m_omegaY.init("OmegaY (nm)", "Half-width of the distribution along its y-axis", 1.0, "omegaY");
    m_gamma.init(
        "Gamma (deg)",
        "Angle in direct space between first lattice vector and x-axis of the distribution", 0.0, 2,
        1.0, RealLimits::limited(0.0, 360.0), "gamma");
}

void Profile2DItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    m_omegaX.writeTo(w, Tag::OmegaX);
    m_omegaY.writeTo(w, Tag::OmegaY);
    m_gamma.writeTo(w, Tag::Gamma);
}

void Profile2DItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        if (tag == Tag::OmegaX)
            m_omegaX.readFrom(r, tag);
        else if (tag == Tag::OmegaY)
            m_omegaY.readFrom(r, tag);
        else if (tag == Tag::Gamma)
            m_gamma.readFrom(r, tag);
        else
            r->skipCurrentElement();
    }
}

DoubleProperties Profile2DItem::profileProperties()
{
    return {&m_omegaX, &m_omegaY, &m_gamma};
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile2D> Profile2DCauchyItem::createProfile() const
{
    return std::make_unique<Profile2DCauchy>(m_omegaX.value(), m_omegaY.value(),
                                             Units::deg2rad(m_gamma.value()));
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile2D> Profile2DGaussItem::createProfile() const
{
    return std::make_unique<Profile2DGauss>(m_omegaX.value(), m_omegaY.value(),
                                            Units::deg2rad(m_gamma.value()));
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile2D> Profile2DGateItem::createProfile() const
{
    return std::make_unique<Profile2DGate>(m_omegaX.value(), m_omegaY.value(),
                                           Units::deg2rad(m_gamma.value()));
}

// --------------------------------------------------------------------------------------------- //

std::unique_ptr<IProfile2D> Profile2DConeItem::createProfile() const
{
    return std::make_unique<Profile2DCone>(m_omegaX.value(), m_omegaY.value(),
                                           Units::deg2rad(m_gamma.value()));
}

// --------------------------------------------------------------------------------------------- //

Profile2DVoigtItem::Profile2DVoigtItem()
{
    m_eta.init("Eta", "Parameter [0,1] to balance between Cauchy (eta=0.0) and Gauss (eta=1.0)",
               0.5, 3, RealLimits::limited(0.0, 1.0), "eta");
}

std::unique_ptr<IProfile2D> Profile2DVoigtItem::createProfile() const
{
    return std::make_unique<Profile2DVoigt>(m_omegaX.value(), m_omegaY.value(),
                                            Units::deg2rad(m_gamma.value()), m_eta.value());
}

void Profile2DVoigtItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeAttribute(w, XML::Attrib::version, uint(1));

    // parameters from base class
    w->writeStartElement(Tag::BaseData);
    Profile2DItem::writeTo(w);
    w->writeEndElement();

    // eta
    w->writeStartElement(Tag::Eta);
    m_eta.writeTo(w);
    w->writeEndElement();
}

void Profile2DVoigtItem::readFrom(QXmlStreamReader* r)
{
    const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
    Q_UNUSED(version)

    while (r->readNextStartElement()) {
        QString tag = r->name().toString();

        // parameters from base class
        if (tag == Tag::BaseData) {
            Profile2DItem::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // eta
        } else if (tag == Tag::Eta) {
            m_eta.readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

DoubleProperties Profile2DVoigtItem::profileProperties()
{
    return {&m_omegaX, &m_omegaY, &m_eta, &m_gamma};
}
