//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Material/SLDMaterial.cpp
//! @brief     Implements class SLDMaterial.
//!
//! @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/Material/SLDMaterial.h"
#include "Base/Const/Units.h"
#include "Base/Vector/WavevectorInfo.h"
#include <numbers>

namespace {

// SLD ↔ refractive index conversion:
//   n² = 1 − (λ²/π) × SLD(in nm⁻²)
// where λ is in nm.
// SLD is stored in Å⁻² and converted to nm⁻² by dividing by angstrom_sq.

inline double wlPrefactor(double wavelength)
{
    return wavelength * wavelength / std::numbers::pi;
}

const double angstrom_sq = Units::angstrom * Units::angstrom;

} // namespace

//! Constructs a wavelength-independent material with a given complex-valued
//! scattering length density (SLD). SLD units are Å⁻².
SLDMaterial::SLDMaterial(const std::string& name, const MaterialColor& color, double sld_real,
                         double sld_imag, const R3& magnetization)
    : Material(name, color, magnetization)
    , m_sld_real(sld_real)
    , m_sld_imag(sld_imag < 0.
                     ? throw std::runtime_error(
                           "The imaginary part of the SLD must be nonnegative"
                           " as we follow the convention sld = sld_real - i*sld_imag (Sears 1992)")
                     : sld_imag)
{
}

SLDMaterial* SLDMaterial::clone() const
{
    return new SLDMaterial(materialName(), color(), m_sld_real, m_sld_imag, magnetization());
}

complex_t SLDMaterial::refractiveIndex(double wavelength) const
{
    return std::sqrt(squaredRefractiveIndex(wavelength));
}

complex_t SLDMaterial::squaredRefractiveIndex(double wavelength) const
{
    return 1.0 - wlPrefactor(wavelength) * sld() / angstrom_sq;
}

//! Returns underlying material SLD in Å⁻²
complex_t SLDMaterial::rawData() const
{
    return {m_sld_real, m_sld_imag};
}

complex_t SLDMaterial::sld(double /*wavelength*/) const
{
    return rawData();
}

//! Returns (π/λ² − SLD(in nm⁻²))
complex_t SLDMaterial::plainSubtrSLD(double lambda0) const
{
    if (std::isnan(lambda0))
        throw std::runtime_error("wavelength not set");
    return 1.0 / wlPrefactor(lambda0) - sld() / angstrom_sq;
}

//! Returns the scattering length density in Å⁻²
complex_t SLDMaterial::sld() const
{
    return {m_sld_real, -m_sld_imag};
}
