//  ************************************************************************************************
//
//  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>
#include <stdexcept>

using std::numbers::pi;

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

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

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)
{
    Lattice2D::validate();
}

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

void BasicLattice2D::validate() const
{
    if (m_length1 <= 0 || m_length2 <= 0)
        throw std::runtime_error("a length parameter <= 0");
    if (fabs(std::sin(m_angle)) < 1e-15)
        throw std::runtime_error("lattice angle too close to 0 or 180deg");
    m_validated = true;
}

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

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

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

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

void SquareLattice2D::validate() const
{
    if (m_length <= 0)
        throw std::runtime_error("length <= 0");
    m_validated = true;
}

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

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

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

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

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

void HexagonalLattice2D::validate() const
{
    if (m_length <= 0)
        throw std::runtime_error("length <= 0");
    m_validated = true;
}

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

double HexagonalLattice2D::unitCellArea() const
{
    static const double sinval = std::sin((2 * pi) / 3); // can be made constexpr in C++26
    return std::abs(m_length * m_length * sinval);
}
