#include "Base/Axis/Scale.h"
#include "Base/Const/Units.h"
#include "Device/Beam/Beam.h"
#include "Device/Coord/CoordSystem2D.h"
#include "Device/Detector/SphericalDetector.h"
#include "Tests/GTestWrapper/google_test.h"

class SphericalConverterTest : public ::testing::Test {
public:
    SphericalConverterTest();

protected:
    SphericalDetector m_detector;
    Beam m_beam;
    double m_kiz, m_kfy, m_kfz1, m_kfz2;
};

SphericalConverterTest::SphericalConverterTest()
    : m_detector(100, 0.0, 5.0 * Units::deg, 70, -2.0 * Units::deg, 1.5)
    , m_beam(Beam(1, 1.0, 1 * Units::deg))
{
    const R3 k_i = m_beam.ki();
    m_kiz = k_i.z();
    const double K = m_beam.wavenumber();
    m_kfy = K * std::sin(5.0 * Units::deg);
    m_kfz1 = K * std::sin(-2.0 * Units::deg);
    m_kfz2 = K * std::sin(1.5);
}


TEST_F(SphericalConverterTest, SphericalCoords)
{
    SphericalCoords converter(m_detector.axesClippedToRegionOfInterest(), m_beam.ki());

    EXPECT_EQ(converter.rank(), 2u);

    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Coords::UNDEFINED), 0.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Coords::NBINS), 0.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Coords::RADIANS), 0.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Coords::DEGREES), 0.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(0, Coords::QSPACE), 0.0);
    EXPECT_FAILED_ASSERT(converter.calculateMin(0, Coords::MM));

    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Coords::UNDEFINED), 5.0);
    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Coords::NBINS), 100.0);
    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Coords::RADIANS), Units::deg2rad(5.0));
    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Coords::DEGREES), 5.0);
    EXPECT_DOUBLE_EQ(converter.calculateMax(0, Coords::QSPACE), m_kfy);
    EXPECT_FAILED_ASSERT(converter.calculateMax(0, Coords::MM));

    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Coords::UNDEFINED), -2.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Coords::NBINS), 0.0);
    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Coords::RADIANS), Units::deg2rad(-2.0));
    EXPECT_DOUBLE_EQ(converter.calculateMin(1, Coords::DEGREES), -2.0);
    EXPECT_FAILED_ASSERT(converter.calculateMin(1, Coords::MM));

    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Coords::UNDEFINED), Units::rad2deg(1.5));
    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Coords::NBINS), 70.0);
    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Coords::RADIANS), 1.5);
    EXPECT_DOUBLE_EQ(converter.calculateMax(1, Coords::DEGREES), Units::rad2deg(1.5));
    EXPECT_FAILED_ASSERT(converter.calculateMax(1, Coords::MM));

    EXPECT_FAILED_ASSERT(converter.calculateMin(2, Coords::UNDEFINED));
    EXPECT_FAILED_ASSERT(converter.calculateMax(2, Coords::UNDEFINED));

    std::unique_ptr<Scale> axis(converter.convertedAxis(0, Coords::UNDEFINED));
    EXPECT_EQ(axis->min(), converter.calculateMin(0, Coords::UNDEFINED));
    EXPECT_EQ(axis->max(), converter.calculateMax(0, Coords::UNDEFINED));

    std::unique_ptr<Scale> axis2(converter.convertedAxis(1, Coords::QSPACE));
    EXPECT_EQ(axis2->min(), converter.calculateMin(1, Coords::QSPACE));
    EXPECT_EQ(axis2->max(), converter.calculateMax(1, Coords::QSPACE));

    EXPECT_FAILED_ASSERT(converter.convertedAxis(2, Coords::UNDEFINED));
}

TEST_F(SphericalConverterTest, SphericalCoordsClone)
{
    SphericalCoords converter(m_detector.axesClippedToRegionOfInterest(), m_beam.ki());
    std::unique_ptr<SphericalCoords> P_clone(converter.clone());

    EXPECT_EQ(P_clone->rank(), 2u);

    EXPECT_DOUBLE_EQ(P_clone->calculateMin(0, Coords::UNDEFINED), 0.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(0, Coords::NBINS), 0.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(0, Coords::RADIANS), 0.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(0, Coords::DEGREES), 0.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(0, Coords::QSPACE), 0.0);
    EXPECT_FAILED_ASSERT(P_clone->calculateMin(0, Coords::MM));

    EXPECT_DOUBLE_EQ(P_clone->calculateMax(0, Coords::UNDEFINED), 5.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(0, Coords::NBINS), 100.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(0, Coords::RADIANS), Units::deg2rad(5.0));
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(0, Coords::DEGREES), 5.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(0, Coords::QSPACE), m_kfy);
    EXPECT_FAILED_ASSERT(P_clone->calculateMax(0, Coords::MM));

    EXPECT_DOUBLE_EQ(P_clone->calculateMin(1, Coords::UNDEFINED), -2.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(1, Coords::NBINS), 0.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(1, Coords::RADIANS), Units::deg2rad(-2.0));
    EXPECT_DOUBLE_EQ(P_clone->calculateMin(1, Coords::DEGREES), -2.0);
    EXPECT_FAILED_ASSERT(P_clone->calculateMin(1, Coords::MM));

    EXPECT_DOUBLE_EQ(P_clone->calculateMax(1, Coords::UNDEFINED), Units::rad2deg(1.5));
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(1, Coords::NBINS), 70.0);
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(1, Coords::RADIANS), 1.5);
    EXPECT_DOUBLE_EQ(P_clone->calculateMax(1, Coords::DEGREES), Units::rad2deg(1.5));

    EXPECT_FAILED_ASSERT(P_clone->calculateMax(1, Coords::MM));

    EXPECT_FAILED_ASSERT(P_clone->calculateMin(2, Coords::UNDEFINED));
    EXPECT_FAILED_ASSERT(P_clone->calculateMax(2, Coords::UNDEFINED));
}
