//  ************************************************************************************************
//
//  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::writeBaseElement<ItemWithParticles>(w, XML::Tag::BaseData, this);

    // core
    if (m_core) {
        XML::writeTaggedElement(w, Tag::Core, *m_core);
    }

    // shell
    if (m_shell) {
        XML::writeTaggedElement(w, Tag::Shell, *m_shell);
    }
    XML::writeTaggedValue(w, Tag::ExpandMainGroupbox, expandMain);
    XML::writeTaggedValue(w, Tag::ExpandCoreGroupbox, expandCore);
    XML::writeTaggedValue(w, Tag::ExpandShellGroupbox, expandShell);
}

void CoreAndShellItem::readFrom(QXmlStreamReader* r)
{
    while (r->readNextStartElement()) {
        QString tag = r->name().toString();
        if (tag == Tag::BaseData)
            XML::readBaseElement<ItemWithParticles>(r, tag, this);
        else if (tag == Tag::Core) {
            createCoreItem(m_materials)->readFrom(r);
            XML::gotoEndElementOfTag(r, tag);
        } else if (tag == Tag::Shell) {
            createShellItem(m_materials)->readFrom(r);
            XML::gotoEndElementOfTag(r, tag);
        } else if (tag == Tag::ExpandMainGroupbox)
            expandMain = XML::readTaggedBool(r, tag);
        else if (tag == Tag::ExpandCoreGroupbox)
            expandCore = XML::readTaggedBool(r, tag);
        else if (tag == Tag::ExpandShellGroupbox)
            expandShell = XML::readTaggedBool(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().dVal());
    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;
}
