//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Sample/CoreAndShellItem.cpp
//! @brief     Implements class CoreAndShellItem.
//!
//! @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/CoreAndShellItem.h"
#include "Base/Util/Assert.h"
#include "Base/Util/Vec.h"
#include "GUI/Model/Material/MaterialsSet.h"
#include "GUI/Model/Sample/ParticleItem.h"
#include "Sample/Particle/CoreAndShell.h"
#include "Sample/Particle/Particle.h"
#include "Sample/Scattering/Rotations.h"

namespace {
namespace Tag {

const QString Core("Core");
const QString Shell("Shell");
const QString BaseData("BaseData");
const QString ExpandMainGroupbox("ExpandMainGroupbox");
const QString ExpandCoreGroupbox("ExpandCoreGroupbox");
const QString ExpandShellGroupbox("ExpandShellGroupbox");

} // namespace Tag

const QString abundance_tooltip = "Proportion of this type of particles normalized to the \n"
                                  "total number of particles in the layout";

const QString position_tooltip = "Relative position of the particle's reference point \n"
                                 "in the coordinate system of the parent (nm)";

} // namespace

CoreAndShellItem::CoreAndShellItem(const MaterialsSet* materials)
    : ItemWithParticles(abundance_tooltip, position_tooltip)
    , m_materials(materials)
{
}

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

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

    // core
    if (m_core) {
        w->writeStartElement(Tag::Core);
        m_core->writeTo(w);
        w->writeEndElement();
    }

    // shell
    if (m_shell) {
        w->writeStartElement(Tag::Shell);
        m_shell->writeTo(w);
        w->writeEndElement();
    }

    // main groupbox: is expanded?
    w->writeStartElement(Tag::ExpandMainGroupbox);
    XML::writeAttribute(w, XML::Attrib::value, expandMain);
    w->writeEndElement();

    // core groupbox: is expanded?
    w->writeStartElement(Tag::ExpandCoreGroupbox);
    XML::writeAttribute(w, XML::Attrib::value, expandCore);
    w->writeEndElement();

    // shell groupbox: is expanded?
    w->writeStartElement(Tag::ExpandShellGroupbox);
    XML::writeAttribute(w, XML::Attrib::value, expandShell);
    w->writeEndElement();
}

void CoreAndShellItem::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) {
            ItemWithParticles::readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // core
        } else if (tag == Tag::Core) {
            createCoreItem(m_materials)->readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // shell
        } else if (tag == Tag::Shell) {
            createShellItem(m_materials)->readFrom(r);
            XML::gotoEndElementOfTag(r, tag);

            // main groupbox: is expanded?
        } else if (tag == Tag::ExpandMainGroupbox) {
            XML::readAttribute(r, XML::Attrib::value, &expandMain);
            XML::gotoEndElementOfTag(r, tag);

            // core groupbox: is expanded?
        } else if (tag == Tag::ExpandCoreGroupbox) {
            XML::readAttribute(r, XML::Attrib::value, &expandCore);
            XML::gotoEndElementOfTag(r, tag);

            // shell groupbox: is expanded?
        } else if (tag == Tag::ExpandShellGroupbox) {
            XML::readAttribute(r, XML::Attrib::value, &expandShell);
            XML::gotoEndElementOfTag(r, tag);

        } else
            r->skipCurrentElement();
    }
}

std::unique_ptr<CoreAndShell> CoreAndShellItem::createCoreAndShell() const
{
    ASSERT(m_core);
    std::unique_ptr<Particle> core = m_core->createParticle();
    ASSERT(core);

    ASSERT(m_shell);
    std::unique_ptr<Particle> shell = m_shell->createParticle();
    ASSERT(shell);

    auto coreshell = std::make_unique<CoreAndShell>(*core, *shell);
    coreshell->setAbundance(abundance());
    if (auto r = createRotation(); r && !r->isIdentity())
        coreshell->rotate(*r);
    coreshell->translate(position());

    return coreshell;
}

ParticleItem* CoreAndShellItem::coreItem() const
{
    return m_core.get();
}

ParticleItem* CoreAndShellItem::createCoreItem(const MaterialsSet* materials)
{
    m_core = std::make_unique<ParticleItem>(materials);
    m_core->setMaterial(materials->defaultCoreMaterialItem());
    return m_core.get();
}

ParticleItem* CoreAndShellItem::shellItem() const
{
    return m_shell.get();
}

ParticleItem* CoreAndShellItem::createShellItem(const MaterialsSet* materials)
{
    m_shell = std::make_unique<ParticleItem>(materials);
    m_shell->setMaterial(materials->defaultParticleMaterialItem());

    // position is not used for shell item
    m_shell->setPosition({});
    return m_shell.get();
}

std::vector<ItemWithParticles*> CoreAndShellItem::containedItemsWithParticles() const
{
    std::vector<ItemWithParticles*> result;
    if (coreItem()) {
        result.push_back(coreItem());
        Vec::concat(result, coreItem()->containedItemsWithParticles());
    }
    if (shellItem()) {
        result.push_back(shellItem());
        Vec::concat(result, shellItem()->containedItemsWithParticles());
    }

    return result;
}
