//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Material/MaterialItem.cpp
//! @brief     Implements class MaterialItem.
//!
//! @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/Material/MaterialItem.h"
#include "Base/Util/Assert.h"
#include "GUI/Model/Util/UtilXML.h"
#include "Sample/Material/MaterialFactoryFuncs.h"
#include <QUuid>

namespace {
namespace Tag {

const QString Color("Color");
const QString Id("Id");
const QString Name("Name");
const QString Delta("Delta");
const QString Beta("Beta");
const QString SldRe("SldRe");
const QString SldIm("SldIm");
const QString UseRefractiveIndex("UseRefractiveIndex");
const QString Magnetization("Magnetization");

} // namespace Tag

} // namespace


MaterialItem::MaterialItem()
    : m_color(Qt::red)
{
    m_id = QUuid::createUuid().toString();

    m_delta.init("Delta", "Delta of refractive index (n = 1 - delta + i*beta)", 0.0, 3,
                 RealLimits::limitless(), "delta");
    m_beta.init("Beta", "Beta of refractive index (n = 1 - delta + i*beta)", 0.0, 3,
                RealLimits::nonnegative(), "beta");

    m_sld_re.init("SLD, real", "Real part SLD (SLD = real - i*imag) (Å^-2)", 0.0, 3,
                  RealLimits::limitless(), "sldRe");
    m_sld_im.init("SLD, imaginary", "Imaginary part of SLD (SLD = real - i*imag) (Å^-2), ", 0.0, 3,
                  RealLimits::nonnegative(), "sldIm");

    m_magnetization.init("Magnetization", "Magnetization (A/m)", "magnetization");
}

void MaterialItem::setRefractiveIndex(const double delta, const double beta)
{
    if (hasRefractiveIndex() && m_delta.dVal() == delta && m_beta.dVal() == beta)
        return;

    m_use_refractive_index = true;

    m_delta.setDVal(delta);
    m_beta.setDVal(beta);
    emit dataChanged();
}

void MaterialItem::setScatteringLengthDensity(const complex_t sld)
{
    if (!hasRefractiveIndex() && m_sld_re.dVal() == sld.real() && m_sld_im.dVal() == sld.imag())
        return;

    m_use_refractive_index = false;

    m_sld_re.setDVal(sld.real());
    m_sld_im.setDVal(sld.imag());
    emit dataChanged();
}

void MaterialItem::setMatItemName(const QString& name)
{
    if (m_name != name) {
        m_name = name;
        emit dataChanged();
    }
}

void MaterialItem::createNewIdentifier()
{
    m_id = QUuid::createUuid().toString();
}

void MaterialItem::setColor(const QColor& color)
{
    if (m_color != color) {
        m_color = color;
        emit dataChanged();
    }
}

void MaterialItem::setMagnetization(const R3& magnetization)
{
    if (m_magnetization.r3() != magnetization) {
        m_magnetization.setR3(magnetization);
        emit dataChanged();
    }
}

std::unique_ptr<Material> MaterialItem::createMaterial() const
{
    if (hasRefractiveIndex())
        return std::make_unique<Material>(RefractiveMaterial(
            matItemName().toStdString(), m_delta.dVal(), m_beta.dVal(), m_magnetization));

    return std::make_unique<Material>(MaterialBySLD(matItemName().toStdString(), m_sld_re.dVal(),
                                                    m_sld_im.dVal(), m_magnetization));
}

void MaterialItem::writeTo(QXmlStreamWriter* w) const
{
    XML::writeTaggedValue(w, Tag::Name, m_name);
    XML::writeTaggedValue(w, Tag::Id, m_id);
    XML::writeTaggedValue(w, Tag::Color, m_color);
    XML::writeTaggedElement(w, Tag::Magnetization, m_magnetization);
    XML::writeTaggedValue(w, Tag::UseRefractiveIndex, m_use_refractive_index);

    if (m_use_refractive_index) {
        // delta
        m_delta.writeTo2(w, Tag::Delta);

        // beta
        m_beta.writeTo2(w, Tag::Beta);
    } else {
        // sld real
        m_sld_re.writeTo2(w, Tag::SldRe);

        // sld imaginary
        m_sld_im.writeTo2(w, Tag::SldIm);
    }
}

void MaterialItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::Name)
            m_name = XML::readTaggedString(r, tag);
        else if (tag == Tag::Id)
            m_id = XML::readTaggedString(r, tag);
        else if (tag == Tag::Color)
            m_color = XML::readTaggedColor(r, tag);
        else if (tag == Tag::Magnetization)
            XML::readTaggedElement(r, tag, m_magnetization);
        else if (tag == Tag::UseRefractiveIndex)
            m_use_refractive_index = XML::readTaggedBool(r, tag);
        else if (tag == Tag::Delta && m_use_refractive_index) {
            m_delta.readFrom2(r, tag);
        } else if (tag == Tag::Beta && m_use_refractive_index) {
            m_beta.readFrom2(r, tag);
        } else if (tag == Tag::SldRe && !m_use_refractive_index) {
            m_sld_re.readFrom2(r, tag);
        } else if (tag == Tag::SldIm && !m_use_refractive_index) {
            m_sld_im.readFrom2(r, tag);

        } else
            r->skipCurrentElement();
    }
}

void MaterialItem::updateFrom(const MaterialItem& other)
{
    if (*this == other)
        return;
    m_name = other.m_name;
    m_color = other.m_color;
    m_magnetization.setR3(other.m_magnetization.r3());

    m_use_refractive_index = other.m_use_refractive_index;

    m_delta.setDVal(other.m_delta.dVal());
    m_beta.setDVal(other.m_beta.dVal());
    m_sld_re.setDVal(other.m_sld_re.dVal());
    m_sld_im.setDVal(other.m_sld_im.dVal());

    emit dataChanged();
}

bool MaterialItem::operator==(const MaterialItem& other) const
{
    if (m_use_refractive_index != other.m_use_refractive_index)
        return false;

    if (hasRefractiveIndex()) {
        if ((m_delta.dVal() != other.m_delta.dVal()) || (m_beta.dVal() != other.m_beta.dVal()))
            return false;
    } else if ((m_sld_re.dVal() != other.m_sld_re.dVal())
               || (m_sld_im.dVal() != other.m_sld_im.dVal()))
        return false;


    return (m_id == other.m_id) && (m_name == other.m_name) && (m_color == other.m_color)
           && (m_magnetization == other.m_magnetization);
}
