//  ************************************************************************************************
//
//  libformfactor: efficient and accurate computation of scattering form factors
//
//! @file      ff/Face.h
//! @brief     Defines class Face
//!
//! @homepage  https://jugit.fz-juelich.de/mlz/libformfactor
//! @license   GNU General Public License v3 or higher (see LICENSE)
//! @copyright Forschungszentrum Jülich GmbH 2022
//! @author    Joachim Wuttke, Scientific Computing Group at MLZ (see CITATION)
//
//  ************************************************************************************************

#ifndef FF_FACE_H
#define FF_FACE_H

#include <heinz/Complex.h>
#include <heinz/Vectors3D.h>
#include <vector>

namespace ff {

class Edge;

#ifdef ALGORITHM_DIAGNOSTIC
#include <string>

struct PolyhedralDiagnosis {
    int algo;
    int order;
    std::string msg;
    void reset();
    std::string message() const;
    bool operator==(const PolyhedralDiagnosis&) const;
    bool operator!=(const PolyhedralDiagnosis&) const;
};
inline PolyhedralDiagnosis polyhedralDiagnosis;
#endif
//! A polygon, for form factor computation.

class Face {
public:
    Face(const std::vector<R3>& _V, bool _sym_S2 = false);
    ~Face();
    Face(const Face&) = delete;
    Face(Face&&) = default;

    double area() const { return m_area; }
    double radius() const { return m_radius_2d; }
    double pyramidalVolume() const { return m_rperp * m_area / 3; }
    //! Returns center of mass of the plane figure
    R3 center_of_polygon() const;
    //! Returns conj(q)*normal [BasicVector3D::dot is antilinear in 'this' argument]
    complex_t normalProjectionConj(C3 q) const { return q.dot(m_normal); }
    complex_t ff_n(int n, C3 q) const;
    complex_t ff(C3 q, bool sym_Ci) const;
    complex_t ff_2D(C3 qpa) const;
    complex_t ff_2D_direct(C3 qpa) const;   // for TestTriangle
    complex_t ff_2D_expanded(C3 qpa) const; // for TestTriangle
    void assert_Ci(const Face& other) const;
    const std::vector<Edge>& edges() const;
    const R3& normal() const { return m_normal; }
    bool is_symmetric() const { return m_sym_S2; }
    bool is_inside(const R3& v) const;

private:
    const bool m_sym_S2;      //!< if true, then edges obtainable by inversion are not provided
    const double m_radius_2d; //!< radius of enclosing cylinder
    const R3 m_normal;        //!< normal vector of this polygon's plane
    const double m_rperp;     //!< distance of polygon's plane from the origin, along 'm_normal'
    const double m_area;
    const std::vector<Edge> m_edges;

    void decompose_q(C3 q, complex_t& qperp, C3& qpa) const;
    complex_t ff_n_core(int m, C3 qpa, complex_t qperp) const;
    complex_t edge_sum_ff(C3 q, C3 qpa, bool sym_Ci) const;
    complex_t expansion(complex_t fac_even, complex_t fac_odd, C3 qpa, double abslevel) const;
};

} // namespace ff

#endif // FF_FACE_H
