//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Particle/Compound.cpp
//! @brief     Implements class Compound.
//!
//! @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 "Sample/Particle/Compound.h"
#include "Base/Type/Span.h"
#include "Base/Util/Assert.h"
#include "Sample/Scattering/Rotations.h"

Compound::Compound() = default;

Compound::~Compound() = default;

Compound* Compound::clone() const
{
    auto* result = new Compound;
    result->setAbundance(m_abundance);
    for (auto* m_particle : m_particles)
        result->addComponent(*m_particle);
    if (rotation())
        result->rotate(*rotation());
    result->translate(particlePosition());
    return result;
}

std::vector<const INode*> Compound::nodeChildren() const
{
    std::vector<const INode*> result = IParticle::nodeChildren();
    for (const IParticle* p : m_particles)
        result.push_back(p);
    return result;
}

OwningVector<IParticle> Compound::decompose() const
{
    OwningVector<IParticle> result;
    const auto* rot = rotation();
    const auto& translation = particlePosition();
    for (const auto& particle : m_particles) {
        const auto sublist = particle->decompose();
        for (const auto& subparticle : sublist) {
            IParticle* new_subparticle = subparticle->clone();
            if (rot)
                new_subparticle->rotate(*rot);
            new_subparticle->translate(translation);
            result.push_back(new_subparticle);
        }
    }
    return result;
}

void Compound::addComponent(const IParticle& particle)
{
    m_particles.push_back(particle.clone());
}

void Compound::addComponent(const IParticle& particle, const R3& position)
{
    m_particles.push_back(particle.clone()->translate(position));
}

// Please note, that positions is not const reference here. This is intentional, to
// enable python lists to std::vector conversion
void Compound::addComponents(const IParticle& particle, std::vector<R3> positions)
{
    for (const auto& position : positions)
        addComponent(particle, position);
}

std::vector<const IParticle*> Compound::particles() const
{
    std::vector<const IParticle*> result;
    for (const IParticle* p : m_particles)
        result.push_back(p);
    return result;
}

Span Compound::zSpan() const
{
    Span result = m_particles[0]->zSpan();
    for (size_t i = 1; i < m_particles.size(); ++i)
        result = Span::unite(result, m_particles[i]->zSpan());
    return result + particlePosition().z();
}
