//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sim/Simulation/ParticleSimulation.cpp
//! @brief     Implements class ParticleSimulation.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2026
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Sim/Simulation/ParticleSimulation.h"
#include "Base/Axis/Pixel.h"
#include "Base/Vector/GisasDirection.h"
#include "Base/Vector/WavevectorInfo.h"
#include "Device/Beam/Beam.h"
#include "Device/Data/Datafield.h"
#include "Device/Detector/Detector.h"
#include "Sample/Particle/DwbaChannels.h"
#include "Sample/Particle/IParticle.h"

ParticleSimulation::ParticleSimulation(const Beam& beam, const IParticle& particle,
                                       const Detector& detector)
    : m_beam(beam.clone())
    , m_particle(particle.clone())
    , m_detector(detector.clone())
{
}

ParticleSimulation::~ParticleSimulation() = default;

Datafield ParticleSimulation::simulate()
{
    const double wavelength = m_beam->wavelength();
    const double alpha_i = m_beam->alpha_i();
    const double phi_i = m_beam->phi_i();

    // Incoming wavevector
    const R3 k_i = vecOfLambdaAlphaPhi(wavelength, -alpha_i, -phi_i);

    // Get active detector indices
    const std::vector<size_t> active_indices = m_detector->activeIndices();

    // Prepare result
    Datafield result(m_detector->createDetectorMap());

    // Loop over all active pixels
    for (size_t i : active_indices) {
        const size_t full_index = m_detector->roiToFullIndex(i);
        std::unique_ptr<const Pixel> pixel(m_detector->createPixel(full_index));

        // Outgoing wavevector at pixel center
        const R3 k_f = pixel->getK(0.5, 0.5, wavelength);

        // Scattering vector
        const WavevectorInfo wv(k_i, k_f, wavelength);

        // In pure Born geometry only the TT channel is active, but mixtures and other
        // stochastic particles still need their incoherent channel variance.
        const DwbaChannels born{{wv, wv, wv, wv}, {1.0, 0.0, 0.0, 0.0}, 1};
        const ChannelMoments moments = m_particle->channelMoments(born);
        const double intensity = std::norm(moments.mu[0]) + std::real(moments.C[0][0]);

        result.setAt(i, intensity);
    }

    return result;
}
