//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Sample/Sample.h
//! @brief     Defines class Sample.
//!
//! @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_SAMPLE_SAMPLE_H
#define BORNAGAIN_SAMPLE_SAMPLE_SAMPLE_H

#include "Base/Type/Cloneable.h"
#include <heinz/Vectors3D.h>
#include <memory>
#include <vector>

class Layer;
class LayerStack;

//! 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 Sample : public Cloneable {
public:
    Sample();
    ~Sample() override;

    void addLayer(const Layer& layer);
    void addStack(const LayerStack& substack);

    //! Sets the external field applied to the sample (units: A/m)
    void setExternalField(const R3& ext_field);

#ifndef SWIG
    Sample(LayerStack* outer_stack); //!< Takes ownership of given stack.

    Sample* clone() const override;
    virtual void validate() const;

    void checkRefractiveIndex(double wavelength) const;

    bool isMagnetic() const;

    size_t nUnwrapped() const;                         //!< Number of unwrapped layers
    const Layer* unwrappedLayer(size_t i_layer) const; //!< Layer in unwrapped structure
    const std::vector<std::unique_ptr<Layer>>& unwrapped() const;

    R3 externalField() const { return m_ext_field; } //!< (units: A/m)

    const LayerStack& outerStack() const { return *m_outer_stack; }

protected:
    mutable bool m_validated{false};
    mutable std::vector<std::unique_ptr<Layer>> m_unwrapped;

private:
    std::unique_ptr<LayerStack> m_outer_stack; //!< Stack of layers and substacks. Never nullptr
    R3 m_ext_field;                            //!< External magnetic field (in A/m)
#endif                                         // SWIG
};

#ifdef BORNAGAIN_PYTHON

//! Extracts a C++ Sample instance from a Python object which wraps a Sample instance
// NOTE: This function needs the SWIG API and is defined in the corresponding SWIG header
#ifndef SWIG
std::unique_ptr<Sample> BA_SWIG_sampleFromPyObject(void* pSample);
#endif // SWIG

#endif // BORNAGAIN_PYTHON

#endif // BORNAGAIN_SAMPLE_SAMPLE_SAMPLE_H
