#include "ff/IBody.h"
#include "ff/Make.h"
#include "test/3rdparty/catch.hpp"
#include "test/util/RunMultiQ.h"
#include <numbers>

using std::numbers::pi;


//! Compare form factor for particle shapes A and B, where A is given special
//! parameter values so that it coincides with the more symmetric B.

void run_test(const ff::IBody* p0, const ff::IBody* p1, double eps, double qmag1, double qmag2)
{
    int failures = formfactorTest::run_test_for_many_q(
        [&](C3 q) -> complex_t { return p0->formfactor(q); },
        [&](C3 q) -> complex_t { return p1->formfactor(q); }, qmag1, qmag2, eps);
    CHECK(failures == 0);
}

const double eps_polyh = 7e-12; // Linux 3e-12, relaxed for Win

TEST_CASE("FormfactorLimits.TruncatedCubeAsBox", "")
{
    const double L = .5;
    const auto* p0 = ff::make::TruncatedCube(L, 0);
    const auto* p1 = ff::make::Box(L, L, L);
    run_test(p0, p1, eps_polyh, 1e-99, 5e2);
}

TEST_CASE("FormfactorLimits.Pyramid2AsPyramid4", "")
{
    const double L = 1.5, H = .24, alpha = .6;
    const auto* p0 = ff::make::Pyramid2(L, L, H, alpha);
    const auto* p1 = ff::make::Pyramid4(L, H, alpha);
    run_test(p0, p1, eps_polyh, 1e-99, 50);
}

TEST_CASE("FormfactorLimits.Pyramid3AsTetrahedron", "")
{
    static const double alpha = atan(sqrt(8));
    const double L = 1.8;
    const auto* p0 = ff::make::Pyramid3(L, sqrt(2. / 3) * L, alpha);
    const auto* p1 = ff::make::Tetrahedron(L);
    run_test(p0, p1, eps_polyh, 1e-99, 50);
}

TEST_CASE("FormfactorLimits.OctahedronAsTruncated", "")
{
    const double L = 1.8;
    const auto* p0 = ff::make::Octahedron(L);
    const auto* p1 = ff::make::Bipyramid4(L, L / sqrt(2), 1., atan(sqrt(2)));
    run_test(p0, p1, eps_polyh, 1e-99, 50);
}

TEST_CASE("FormfactorLimits.Pyramid3AsPrism", "")
{
    const double L = 1.8, H = .3;
    const auto* p0 = ff::make::Pyramid3(L, H, pi / 2);
    const auto* p1 = ff::make::Prism3(L, H);
    run_test(p0, p1, eps_polyh, 1e-99, 50);
}

TEST_CASE("FormfactorLimits.Pyramid4AsBox", "")
{
    const double L = 1.8, H = .3;
    const auto* p0 = ff::make::Pyramid4(L, H, pi / 2);
    const auto* p1 = ff::make::Box(L, L, H);
    run_test(p0, p1, eps_polyh, 1e-99, 100);
}

TEST_CASE("FormfactorLimits.Pyramid6AsPrism", "")
{
    const double L = .8, H = 1.13;
    const auto* p0 = ff::make::Pyramid6(L, H, pi / 2);
    const auto* p1 = ff::make::Prism6(L, H);
    run_test(p0, p1, eps_polyh, 1e-99, 100);
}

/*
// *********** cylinders ***************

TEST_CASE("FormfactorLimits.HorizontalCylinderSlicedVsUnsliced", "")
{
    const double R = .3, L = 3;
    const auto* p0 = ff::make::HorizontalCylinder(R, L);
    const auto* p1 = ff::make::HorizontalCylinder(R, L, -R, +R * (1 - 3e-16));
    run_test(p0, p1, 3e-8, 1e-99, 100);
}

// *********** spheroids ***************

TEST_CASE("FormfactorLimits.HemiEllipsoidAsTruncatedSphere", "")
{
    const double R = 1.07;
    const auto* p0 = ff::make::HemiEllipsoid(R, R, R);
    const auto* p1 = ff::make::TruncatedSphere(R, R, 0);
    run_test(p0, p1, 6e-12, 1e-99, 5e2);
}

TEST_CASE("FormfactorLimits.EllipsoidalCylinderAsCylinder", "")
{
    const double R = .8, H = 1.2;
    const auto* p0 = ff::make::EllipsoidalCylinder(R, R, H);
    const auto* p1 = ff::make::Cylinder(R, H);
    run_test(p0, p1, 5e-12, 1e-99, 50);
}

TEST_CASE("FormfactorLimits.TruncatedSphereAsSphere", "")
{
    const double R = 1.;
    const auto* p0 = ff::make::TruncatedSphere(R, 2 * R, 0);
    const auto* p1 = ff::make::Sphere(R);
    run_test(p0, p1, 1e-12, .02, 5e1);
}

TEST_CASE("FormfactorLimits.SpheroidAsSphere", "")
{
    const double R = 1.;
    const auto* p0 = ff::make::Spheroid(R, 2 * R);
    const auto* p1 = ff::make::Sphere(R);
    run_test(p0, p1, 1e-12, 1e-99, 50);
}
*/
