//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Img3D/Model/Geometry.h
//! @brief     Defines class Geometry.
//!
//! @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_IMG3D_MODEL_GEOMETRY_H
#define BORNAGAIN_IMG3D_MODEL_GEOMETRY_H

#include "Base/Type/Field2D.h"
#include "Img3D/Model/Geometry_inc.h"
#include <QVector>

namespace Img3D {

class Geometry {
public:
    // vertex + normal pair
    struct VertexAndNormal {
        F3 v, n;
        VertexAndNormal() = default;
        VertexAndNormal(const F3& v_, const F3& n_)
            : v(v_)
            , n(n_)
        {
        }
    };

    // vertex indices (for GL)
    using Indices = std::vector<unsigned>;

    // vertices (for GL)
    struct Vertices : private QVector<F3> {
        using QVector<F3>::append;
        using QVector<F3>::QVector;
        using QVector<F3>::reserve;
        using QVector<F3>::resize;
        using QVector<F3>::operator[];
        using QVector<F3>::at;
        using QVector<F3>::begin;
        using QVector<F3>::count;
        using QVector<F3>::end;

        void addVertex(const F3&, int n = 1);              // add a vertex, possibly multiple copies
        void addTriangle(const F3&, const F3&, const F3&); // triangle
        void addQuad(const F3&, const F3&, const F3&,
                     const F3&); // quad as 2 triangles
        void addQuad(const Vertices&, unsigned, unsigned, unsigned, unsigned); // quad by indices
        void addFan(const Vertices&, const Indices&);                          // triangle fan
    };

    // vertex/normal mesh
    using Mesh = QVector<VertexAndNormal>;

    Geometry(GeometricID::Key);
    Geometry(GeometricID::Key, const double2d_t* top, const double2d_t* bottom, bool drawBottom);
    virtual ~Geometry();

    const Mesh& mesh() const { return m_mesh; }

private:
    GeometricID::Key m_key;

    Mesh m_mesh;
    // make a mesh from vectors of vertices and (optionally) normals
    static Mesh makeMesh(const Vertices& vs, Vertices const* ns = nullptr);
    static Mesh makeMesh(const Vertices& vs, const Vertices& ns);

    static Mesh meshPlane();
    static Mesh meshBox();
    static Mesh meshRoughBox(const double2d_t* topRough, const double2d_t* bottomRough,
                             bool drawBottom);
    static Mesh meshSphere(float cut, float baseShift = 0.0f, float removedTop = 0.0f);
    static Mesh meshColumn(float ratio_Rt_Rb, float numSides);
    static Mesh meshIcosahedron();
    static Mesh meshDodecahedron();
    static Mesh meshTruncBox(float tD);
    static Mesh meshBipyramid4(float rH, float alpha, float H);
    static Mesh meshRipple(float numSides, float ratio_asymmetry_W);

    // mesh params for round shapes
    static int const RINGS = 12, SLICES = 24;
};

} // namespace Img3D

#endif // BORNAGAIN_IMG3D_MODEL_GEOMETRY_H
