//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Multilayer/MultiLayer.h
//! @brief     Defines class MultiLayer.
//!
//! @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)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_SAMPLE_MULTILAYER_MULTILAYER_H
#define BORNAGAIN_SAMPLE_MULTILAYER_MULTILAYER_H

#include "Base/Type/OwningVector.h"
#include "Sample/Interface/RoughnessModels.h"
#include "Sample/Scattering/ISampleNode.h"
#include <functional>
#include <heinz/Vectors3D.h>

class Layer;
class LayerInterface;
class LayerRoughness;
class ParticleLayout;

//! Our sample model: a stack of layers one below the other.

//! Example of system of 4 layers (3 interfaces):
//! @verbatim
//! vacuum      layer 0
//! ---------   interface 0    z=0.0
//! Fe, 20A     layer 1
//! ---------   interface 1    z=-20.0
//! Cr, 40A     layer 2
//! ---------   interface 2    z=-60.0
//! substrate   layer 3
//! @endverbatim

class MultiLayer : public ISampleNode {
public:
    MultiLayer();
    ~MultiLayer() override;

    MultiLayer* clone() const override;
    std::string className() const final { return "MultiLayer"; }

    void addLayer(const Layer& layer);
    void addLayerWithTopRoughness(const Layer& layer, const LayerRoughness& roughness);
    //! Sets cross correlation length of roughnesses between interfaces
    void setCrossCorrLength(double crossCorrLength);
    //! Sets the external field applied to the sample (units: A/m)
    void setExternalField(R3 ext_field);

    void setName(const std::string& name) { m_sample_name = name; }

#ifndef SWIG
    std::vector<const INode*> nodeChildren() const override;

    std::string validate() const override;

    const std::string& name() const
    {
        return m_sample_name;
    }

    double hig(size_t i) const; //!< Returns top z coordinate of layer i
    double low(size_t i) const; //!< Returns bottom z coordinate of layer i

    size_t numberOfLayers() const
    {
        return m_layers.size();
    }
    const Layer* layer(size_t i_layer) const;
    const LayerInterface* layerInterface(size_t i_interface) const;
    double crossCorrLength() const
    {
        return m_cross_corr_length;
    }
    R3 externalField() const
    {
        return m_ext_field;
    } //!< (units: A/m)

    void checkMaterials(double wavelength) const;

private:
    void addLayerExec(const Layer& layer, const LayerRoughness* roughness);

    //! stack of layers [nlayers]
    OwningVector<const Layer> m_layers;
    //! stack of layer interfaces [nlayers-1]
    OwningVector<const LayerInterface> m_interfaces;
    //! cross correlation length (in z direction) between different layers
    double m_cross_corr_length;
    //! external magnetic field (in A/m)
    R3 m_ext_field;

    std::string m_sample_name{"Unnamed"};

    mutable std::vector<double> ZInterfaces;
#endif // SWIG
};

#endif // BORNAGAIN_SAMPLE_MULTILAYER_MULTILAYER_H
