//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Multilayer/PlainLayer.cpp
//! @brief     Implements base layer class.
//!
//! @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)
//
//  ************************************************************************************************

#include "Sample/Multilayer/PlainLayer.h"
#include "Base/Util/Assert.h"
#include "Sample/Interface/Roughness.h"
#include "Sample/Material/Material.h"
#include <cmath>
#include <limits>
#include <stdexcept>

namespace {

constexpr double inf = std::numeric_limits<double>::infinity();

} // namespace

PlainLayer::PlainLayer(const Material& material, double thickness, const Roughness* roughness)
    : m_material(material.clone())
    , m_thickness(thickness)
    , m_roughness(roughness ? roughness->clone() : nullptr)
{
    if (thickness <= 0.)
        throw std::runtime_error("Layer constructor called with negative or zero thickness");
}

PlainLayer::PlainLayer(const Material& material, const Roughness* roughness)
    : PlainLayer(material, ::inf, roughness)
{
}

PlainLayer::PlainLayer(const Material& material, const Span& span, const Roughness* roughness)
    : PlainLayer(material, span.extension(), roughness)
{
    m_zspan = span;
}

PlainLayer::~PlainLayer() = default;

PlainLayer::PlainLayer(const PlainLayer& other)
    : m_material(other.material() ? other.material()->clone() : nullptr)
    , m_thickness(other.thickness())
    , m_roughness(other.roughness() ? other.roughness()->clone() : nullptr)
    , m_zspan(other.m_zspan)
    , m_rms(other.m_rms)
{
}

void PlainLayer::setZSpan(const Span& zspan) const
{
    ASSERT(!m_zspan);
    double span_thickness = zspan.extension();
    const double tolerance = 1e-12;

    if (std::isfinite(m_thickness))
        ASSERT(std::abs(span_thickness - m_thickness) < tolerance);
    else
        ASSERT(span_thickness == m_thickness); // Both should be infinite
    m_zspan = zspan;
}

const Span& PlainLayer::zSpan() const
{
    ASSERT(m_zspan);
    return m_zspan.value();
}

double PlainLayer::low() const
{
    ASSERT(m_zspan);
    return m_zspan->low();
}

double PlainLayer::hig() const
{
    ASSERT(m_zspan);
    return m_zspan->hig();
}
