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

using std::numbers::pi;

RefractiveMaterial::RefractiveMaterial(const std::string& name, const MaterialColor& color,
                                       double delta, double beta, const R3& magnetization)
    : Material(name, color, magnetization)
    , m_delta(delta)
    , m_beta(beta < 0.
                 ? throw std::runtime_error(
                       "The imaginary part of the refractive index must be greater or equal zero")
                 : beta)
{
}

RefractiveMaterial::RefractiveMaterial(const std::string& name, const MaterialColor& color,
                                       complex_t refr_idx, const R3& magnetization)
    : RefractiveMaterial(name, color, 1 - std::real(refr_idx), std::imag(refr_idx), magnetization)
{
}

RefractiveMaterial* RefractiveMaterial::clone() const
{
    return new RefractiveMaterial(materialName(), color(), m_delta, m_beta, magnetization());
}

complex_t RefractiveMaterial::refractiveIndex(double) const
{
    return {1.0 - m_delta, m_beta};
}

complex_t RefractiveMaterial::squaredRefractiveIndex(double) const
{
    complex_t result(1.0 - m_delta, m_beta);
    return result * result;
}

complex_t RefractiveMaterial::rawData() const
{
    return {m_delta, m_beta};
}

complex_t RefractiveMaterial::averageableData() const
{
    const complex_t mdc = std::conj(rawData());
    return mdc * mdc - 2.0 * mdc;
}

complex_t RefractiveMaterial::sld(double wavelength) const
{
    // Convert from refractive index to SLD:
    // n² = 1 - λ²*SLD/(π*angstrom²)  =>  SLD = π*angstrom²*(1-n²)/λ²
    const double angstrom_sq = Units::angstrom * Units::angstrom; // angstrom² in nm²
    const complex_t n_sq = squaredRefractiveIndex(wavelength);
    // Return SLD with sign convention: sld = sld_real - i*sld_imag
    // So we return {real, -imag} to match SLDMaterial convention
    const complex_t sld = pi * angstrom_sq * (1.0 - n_sq) / (wavelength * wavelength);
    return {sld.real(), -sld.imag()};
}

complex_t RefractiveMaterial::plainSubtrSLD(double lambda0) const
{
    if (std::isnan(lambda0))
        throw std::runtime_error("wavelength not set");
    return pi / lambda0 / lambda0 * squaredRefractiveIndex(lambda0);
}
