//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Lattice/Lattice2D.h
//! @brief     Defines classes of Lattice2D family.
//!
//! @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_LATTICE_LATTICE2D_H
#define BORNAGAIN_SAMPLE_LATTICE_LATTICE2D_H

#include "Base/Type/ICloneable.h"
#include "Param/Node/INode.h"

//! A two-dimensional Bravais lattice.
struct ReciprocalBases {
    double m_asx, m_asy; //!< x,y coordinates of a*
    double m_bsx, m_bsy; //!< x,y coordinates of b*
};

class Lattice2D : public ICloneable, public INode {
public:
    Lattice2D(const std::vector<double>& PValues);
    explicit Lattice2D(double xi);

#ifndef SWIG
    Lattice2D* clone() const override = 0;
#endif // SWIG

    virtual double length1() const = 0;
    virtual double length2() const = 0;
    virtual double latticeAngle() const = 0;
    virtual double unitCellArea() const = 0;

    double rotationAngle() const { return m_xi; }

    ReciprocalBases reciprocalBases() const;

    void setRotationEnabled(bool enabled);

protected:
    double m_xi;
};

//! A two-dimensional Bravais lattice with no special symmetry.

class BasicLattice2D : public Lattice2D {
public:
    BasicLattice2D(double length1, double length2, double angle, double xi);

#ifndef SWIG
    BasicLattice2D* clone() const override;
#endif // SWIG

    std::string className() const final { return "BasicLattice2D"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"Length1", "nm"}, {"Length2", "nm"}, {"Alpha", "rad"}};
    }

    double length1() const override { return m_length1; }
    double length2() const override { return m_length2; }
    double latticeAngle() const override { return m_angle; }
    double unitCellArea() const override;

    std::string validate() const override;

private:
    double m_length1, m_length2;
    double m_angle;
};

//! A two-dimensional Bravais lattice with square unit cell.

class SquareLattice2D : public Lattice2D {
public:
    SquareLattice2D(double length, double xi = 0.0);

#ifndef SWIG
    SquareLattice2D* clone() const override;
#endif // SWIG

    std::string className() const final { return "SquareLattice2D"; }
    std::vector<ParaMeta> parDefs() const final { return {{"Length", "nm"}}; }

    double length1() const override { return m_length; }
    double length2() const override { return m_length; }
    double latticeAngle() const override;
    double unitCellArea() const override;

    std::string validate() const override;

private:
    double m_length;
};

//! A two-dimensional Bravais lattice with hexagonal symmetry.

class HexagonalLattice2D : public Lattice2D {
public:
    HexagonalLattice2D(double length, double xi);

#ifndef SWIG
    HexagonalLattice2D* clone() const override;
#endif // SWIG

    std::string className() const final { return "HexagonalLattice2D"; }
    std::vector<ParaMeta> parDefs() const final { return {{"Length", "nm"}, {"Xi", "rad"}}; }

    double length1() const override { return m_length; }
    double length2() const override { return m_length; }
    double latticeAngle() const override;
    double unitCellArea() const override;

    std::string validate() const override;

private:
    double m_length;
};

#endif // BORNAGAIN_SAMPLE_LATTICE_LATTICE2D_H
