//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Lattice/Lattice2D.cpp
//! @brief     Implements 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)
//
//  ************************************************************************************************

#include "Sample/Lattice/Lattice2D.h"
#include <cmath>
#include <numbers>

using std::numbers::pi;

//  ************************************************************************************************
//  class Lattice2D
//  ************************************************************************************************

Lattice2D::Lattice2D(const std::vector<double>& PValues)
    : INode(PValues)
{
}

Lattice2D::Lattice2D(double xi)
    : m_xi(xi)
{
}

Lattice2D::ReciprocalBases Lattice2D::reciprocalBases() const
{
    const double sinalpha = std::sin(latticeAngle());
    const double ainv = (2 * pi) / length1() / sinalpha;
    const double binv = (2 * pi) / length2() / sinalpha;
    const double xi = rotationAngle();
    const double xialpha = xi + latticeAngle();

    return {+ainv * std::sin(xialpha), -ainv * std::cos(xialpha), -binv * std::sin(xi),
            +binv * std::cos(xi)};
}

void Lattice2D::setRotationEnabled(bool /*enabled*/) // TODO ASAP replace by generic mechanism
{
    // #bapool enabling/disabling was done by register/unregister m_xi. Any equivalent
    // implementation necessary?
}

//  ************************************************************************************************
//  class BasicLattice2D
//  ************************************************************************************************

BasicLattice2D::BasicLattice2D(double length1, double length2, double angle, double xi)
    : Lattice2D(xi)
    , m_length1(length1)
    , m_length2(length2)
    , m_angle(angle)
{
    validateOrThrow();
}

BasicLattice2D* BasicLattice2D::clone() const
{
    return new BasicLattice2D(m_length1, m_length2, m_angle, m_xi);
}

double BasicLattice2D::unitCellArea() const
{
    return std::abs(m_length1 * m_length2 * std::sin(m_angle));
}

std::string BasicLattice2D::validate() const
{
    std::vector<std::string> errs;
    requestGt0(errs, m_length1, "length1");
    requestGt0(errs, m_length2, "length2");
    if (!errs.empty())
        return jointError(errs);
    m_validated = true;
    return "";
}

//  ************************************************************************************************
//  class SquareLattice2D
//  ************************************************************************************************

SquareLattice2D::SquareLattice2D(double length, double xi)
    : Lattice2D(xi)
    , m_length(length)
{
    validateOrThrow();
}

SquareLattice2D* SquareLattice2D::clone() const
{
    return new SquareLattice2D(m_length, m_xi);
}

double SquareLattice2D::latticeAngle() const
{
    return pi / 2.0;
}

double SquareLattice2D::unitCellArea() const
{
    return std::abs(m_length * m_length);
}

std::string SquareLattice2D::validate() const
{
    std::vector<std::string> errs;
    requestGt0(errs, m_length, "length");
    if (!errs.empty())
        return jointError(errs);
    m_validated = true;
    return "";
}

//  ************************************************************************************************
//  class HexagonalLattice2D
//  ************************************************************************************************

HexagonalLattice2D::HexagonalLattice2D(double length, double xi)
    : Lattice2D(xi)
    , m_length(length)
{
    validateOrThrow();
}

HexagonalLattice2D* HexagonalLattice2D::clone() const
{
    return new HexagonalLattice2D(m_length, m_xi);
}

double HexagonalLattice2D::latticeAngle() const
{
    return (2 * pi) / 3.0;
}

double HexagonalLattice2D::unitCellArea() const
{
    static const double sinval = std::sin(latticeAngle());
    return std::abs(m_length * m_length * sinval);
}

std::string HexagonalLattice2D::validate() const
{
    std::vector<std::string> errs;
    requestGt0(errs, m_length, "length");
    if (!errs.empty())
        return jointError(errs);
    m_validated = true;
    return "";
}
