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

using std::numbers::pi;

//! Check that form factors are invariant when q is transformed according to particle symmetry.

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

//***********polyhedra ***************

TEST_CASE("FormfactorSymmetry.Prism3", "")
{
    const auto* p = ff::make::Prism3(.83, .45);
    run_test(p, [](const C3& q) -> C3 { return q.rotatedZ(pi * 2 / 3); }, 1e-13, 1e-99, 2e2);
}

TEST_CASE("FormfactorSymmetry.Prism6", "")
{
    const auto* p = ff::make::Prism6(1.33, .42);
    run_test(p, [](const C3& q) -> C3 { return q.rotatedZ(pi / 3); }, 1e-13, 1e-99, 50);
    run_test(p, [](const C3& q) -> C3 { return q.rotatedZ(-pi * 2 / 3); }, 1e-13, 1e-99, 50);
}

TEST_CASE("FormfactorSymmetry.Pyramid3", "")
{
    const auto* p = ff::make::Pyramid3(8.43, .25, .53);
    run_test(p, [](const C3& q) -> C3 { return q.rotatedZ(pi * 2 / 3); }, 1e-11, 1e-99, 100);
    // Linux: 3e-12, relaxed for Mac
}

TEST_CASE("FormfactorSymmetry.Pyramid6xFlat", "")
{
    // TODO for larger q, imag(ff) is nan
    const auto* p = ff::make::Pyramid6(4.3, .09, .1);
    run_test(
        p, [](const C3& q) -> C3 { return q.rotatedZ(-pi / 3); }, 8e-10, 1e-99,
        50); // Linux: 2e-12, relaxed for Mac
}

TEST_CASE("FormfactorSymmetry.Pyramid6xSteep", "")
{
    const auto* p = ff::make::Pyramid6(.23, 3.5, .999 * pi / 2);
    run_test(p, [](const C3& q) -> C3 { return q.rotatedZ(-pi / 3); }, 3e-10, 1e-99, 50);
}

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

/*
TEST_CASE("FormfactorSymmetry.HemiEllipsoid", "")
{
    const auto *p = ff::make::HemiEllipsoid(.53, .78, 1.3);
    run_test(
        p, [](const C3& q) -> C3 { return C3(-q.x(), q.y(), q.z()); }, 1e-12, 1e-99, 2e2);
    run_test(
        p, [](const C3& q) -> C3 { return C3(q.x(), -q.y(), q.z()); }, 1e-12, 1e-99, 2e2);
}

TEST_CASE("FormfactorSymmetry.TruncatedSphere", "")
{
    const auto *p = ff::make::TruncatedSphere(.79, .34, 0);
    run_test(
        p, [](const C3& q) -> C3 { return q.rotatedZ(pi / 3.13698); }, 8e-13, 1e-99, 2e2);
}

TEST_CASE("FormfactorSymmetry.Spheroid", "")
{
    const auto *p = ff::make::Spheroid(.73, .36);
    run_test(
        p, [](const C3& q) -> C3 { return q.rotatedZ(.123); }, 1e-12, 1e-99, 2e2);
}
*/

// ****** TODO: tests that do not pass for the full q range *********
