#include "Device/Detector/RectangularDetector.h"
#include "Base/Const/Units.h"
#include "Base/Math/Numeric.h"
#include "Device/Beam/Beam.h"
#include "Tests/GTestWrapper/google_test.h"
#include <iostream>
#include <memory>

double phi(R3 k)
{
    return R3Util::phi(k) / Units::deg;
}
double alpha(R3 k)
{
    return 90.0 - R3Util::theta(k) / Units::deg;
}

bool isEqual(const R3 lhs, const R3 rhs)
{
    bool is_equal = Numeric::almostEqual(lhs.x(), rhs.x(), 2)
                    && Numeric::almostEqual(lhs.y(), rhs.y(), 2)
                    && Numeric::almostEqual(lhs.z(), rhs.z(), 2);
    if (!is_equal)
        std::cout << "lhs:" << lhs << " rhs:" << rhs << " diff:" << (lhs - rhs) << std::endl;
    return is_equal;
}


TEST(RectangularDetectorTest, InitialState)
{
    RectangularDetector det(50u, 10.0, 60u, 20.0);
    EXPECT_EQ(50u, det.xSize());
    EXPECT_EQ(10.0, det.width());
    EXPECT_EQ(60u, det.ySize());
    EXPECT_EQ(20.0, det.height());

    EXPECT_EQ(0.0, det.getU0());
    EXPECT_EQ(0.0, det.getV0());
    EXPECT_EQ(0.0, det.getDistance());

    EXPECT_TRUE(R3() == det.getNormalVector());
    EXPECT_TRUE(R3(0.0, -1.0, 0.0) == det.getDirectionVector());

    EXPECT_EQ(RectangularDetector::GENERIC, det.getDetectorArrangment());
}

TEST(RectangularDetectorTest, Clone)
{
    RectangularDetector det(50u, 10.0, 60u, 20.0);
    R3 normal(10.0, 20.0, 30.0);
    R3 direction(1.0, 2.0, 3.0);
    double u0(88.0), v0(99.0);
    det.setDetectorPosition(normal, u0, v0, direction);

    std::unique_ptr<RectangularDetector> clone(det.clone());
    EXPECT_EQ(u0, clone->getU0());
    EXPECT_EQ(v0, clone->getV0());
    EXPECT_TRUE(normal == clone->getNormalVector());
    EXPECT_TRUE(direction == clone->getDirectionVector());
    EXPECT_EQ(RectangularDetector::GENERIC, clone->getDetectorArrangment());
}

TEST(RectangularDetectorTest, PerpToSample)
{
    size_t nbinsx(5u), nbinsy(4u);
    double width(50.0), height(40.0);
    double distance(100.0), u0(20.0), v0(10.0);

    RectangularDetector det(nbinsx, width, nbinsy, height);

    // detector perpendicular to sample
    det.setPerpendicularToSampleX(distance, u0, v0);
    EXPECT_EQ(distance, det.getDistance());
    EXPECT_EQ(u0, det.getU0());
    EXPECT_EQ(v0, det.getV0());
    EXPECT_TRUE(R3() == det.getNormalVector());
    EXPECT_TRUE(R3(0.0, -1.0, 0.0) == det.getDirectionVector());
    EXPECT_EQ(RectangularDetector::PERPENDICULAR_TO_SAMPLE, det.getDetectorArrangment());
}

TEST(RectangularDetectorTest, PerpToDirectBeam)
{
    size_t nbinsx(5u), nbinsy(4u);
    double width(50.0), height(40.0);
    double distance(100.0), u0(20.0), v0(10.0);

    RectangularDetector det(nbinsx, width, nbinsy, height);

    // detector perpendicular to direct beam
    det.setPerpendicularToDirectBeam(distance, u0, v0);
    EXPECT_EQ(distance, det.getDistance());
    EXPECT_EQ(u0, det.getU0());
    EXPECT_EQ(v0, det.getV0());
    EXPECT_TRUE(R3() == det.getNormalVector());
    EXPECT_TRUE(R3(0.0, -1.0, 0.0) == det.getDirectionVector());
    EXPECT_EQ(RectangularDetector::PERPENDICULAR_TO_DIRECT_BEAM, det.getDetectorArrangment());
}

TEST(RectangularDetectorTest, PerpToReflectedBeam)
{
    size_t nbinsx(5u), nbinsy(4u);
    double width(50.0), height(40.0);
    double distance(100.0), u0(20.0), v0(10.0);

    RectangularDetector det(nbinsx, width, nbinsy, height);

    // detector perpendicular to reflected beam
    det.setPerpendicularToReflectedBeam(distance, u0, v0);
    EXPECT_EQ(distance, det.getDistance());
    EXPECT_EQ(u0, det.getU0());
    EXPECT_EQ(v0, det.getV0());
    EXPECT_TRUE(R3() == det.getNormalVector());
    EXPECT_TRUE(R3(0.0, -1.0, 0.0) == det.getDirectionVector());
    EXPECT_EQ(RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM, det.getDetectorArrangment());
}
