//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Particle/Mixture.h
//! @brief     Defines class Mixture.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2025
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_SAMPLE_PARTICLE_MIXTURE_H
#define BORNAGAIN_SAMPLE_PARTICLE_MIXTURE_H

#include "Sample/Particle/IParticle.h"
#include <memory>
#include <vector>

//! An uncorrelated random mixture of different particle types.
//!
//! Each particle type has a positive relative abundance, normalized internally.
//! Unlike Compound, which coherently sums form factors of particles at fixed positions,
//! Mixture represents an incoherent mixture where each particle type is handled
//! separately in the scattering calculation.
//!
//! In showSample3D, each layout position displays one realization of this
//! compositional disorder: one variant sampled by abundance. This is a
//! visualization choice; the scattering calculation still uses the full ensemble.

class Mixture : public IParticle {
public:
    Mixture();
    ~Mixture() override;

    //! Adds a particle type with given abundance.
    //! @param particle The particle to add
    //! @param abundance Relative weight of this particle type (must be positive)
    void addParticle(const IParticle& particle, double abundance);

#ifndef SWIG
    //! Takes ownership of particle without cloning (preserves calculation context).
    void addParticle(std::unique_ptr<IParticle> particle, double abundance);
#endif

    //! Returns number of different particle types
    size_t nbrParticles() const { return m_particles.size(); }

#ifndef SWIG
    Mixture* clone() const override;
    void validate() const override;

    //! Returns the constituent particles
    std::vector<const IParticle*> particles() const override;

    //! Returns non-const pointers to constituent particles (for pre-deposit translation).
    std::vector<IParticle*> mutableParticles();

    //! Returns the abundances of each particle type
    const std::vector<double>& abundances() const { return m_abundances; }

    //! Returns abundances normalized to probabilities.
    //! Used by consumers that need the same Mixture weights as scattering.
    const std::vector<double>& normalizedWeights() const
    {
        validate();
        return m_weights;
    }

    void setSliceNumber(size_t n) override;

    double bottomZ() const override;
    double topZ() const override;
    double volumeToAdmix() const override;
    bool hasRadius() const override;
    double radius() const override;
    double radialExtension() const override;
    bool containsSoftParticle() const override;

    //! Returns material for averaging, computed from subparticles weighted by abundance × volume.
    std::unique_ptr<const Material> materialToAdmix() const override;

    complex_t theFF(const WavevectorInfo& wavevectors) const override;
    SpinMatrix thePolFF(const WavevectorInfo& wavevectors) const override;

    ScatteringMoments scatteringMoments(const DiffuseElement& ele) const override;
    PolScatteringMoments polScatteringMoments(const DiffuseElement& ele) const override;
    ChannelMoments channelMoments(const DwbaChannels& channels) const override;
    PolChannelMoments polChannelMoments(const PolDwbaChannels& channels) const override;

    SscaMoments sscaMoments(const DiffuseElement& ele, double kappa_qp,
                            double mean_extension) const override;
    PolSscaMoments polSscaMoments(const DiffuseElement& ele, double kappa_qp,
                                  double mean_extension) const override;

private:
    std::vector<std::unique_ptr<IParticle>> m_particles;
    std::vector<double> m_abundances;      //!< user provided weights, with arbitrary total
    mutable std::vector<double> m_weights; //!< normalized weights, computed in validate()
#endif                                     // SWIG
};

#endif // BORNAGAIN_SAMPLE_PARTICLE_MIXTURE_H
