#include "Device/Detector/SphericalDetector.h"

#include "Base/Axis/Frame.h"
#include "Base/Axis/MakeScale.h"
#include "Base/Axis/Scale.h"
#include "Base/Const/Units.h"
#include "Device/Beam/Beam.h"
#include "Device/Data/Datafield.h"
#include "Device/Mask/MaskStack.h"
#include "Device/Mask/Polygon.h"
#include "Device/Mask/Rectangle.h"
#include "Device/Resolution/ConvolutionDetectorResolution.h"
#include "Device/Resolution/ResolutionFunction2DGaussian.h"
#include "Tests/GTestWrapper/google_test.h"
#include <memory>

using Units::deg;

// Construction of the detector via classical constructor.
TEST(SphericalDetector, constructionWithParameters)
{
    SphericalDetector detector(10, -1 * deg, 1 * deg, 20, 0, 2 * deg);
    EXPECT_EQ(10u, detector.axis(0).size());
    EXPECT_EQ(-1 * deg, detector.axis(0).min());
    EXPECT_EQ(1 * deg, detector.axis(0).max());
    EXPECT_EQ(20u, detector.axis(1).size());
    EXPECT_EQ(0 * deg, detector.axis(1).min());
    EXPECT_EQ(2 * deg, detector.axis(1).max());
}

// Creation of the detector map with axes in given units
TEST(SphericalDetector, createDetectorMap)
{
    SphericalDetector detector(10, -1 * deg, 1 * deg, 20, 0, 2 * deg);

    // creating map in default units, which are radians and checking axes
    auto data = detector.createDetectorMap();
    EXPECT_EQ(data.axis(0).size(), 10u);
    EXPECT_EQ(data.axis(0).min(), -1.0 * deg);
    EXPECT_EQ(data.axis(0).max(), 1.0 * deg);
    EXPECT_EQ(data.axis(1).size(), 20u);
    EXPECT_EQ(data.axis(1).min(), 0.0 * deg);
    EXPECT_EQ(data.axis(1).max(), 2.0 * deg);
}

// Testing region of interest.
TEST(SphericalDetector, regionOfInterest)
{
    SphericalDetector detector(6, -2 * deg, 6 * deg, 4, 0 * deg, 4 * deg);

    // creating region of interest
    double xlow(-2 * deg), ylow(1 * deg), xup(4 * deg), yup(3 * deg);
    detector.setRegionOfInterest(xlow, ylow, xup, yup);
    EXPECT_TRUE(detector.hasExplicitRegionOfInterest());
    EXPECT_EQ(detector.regionOfInterestBounds(0).first, xlow);
    EXPECT_EQ(detector.regionOfInterestBounds(0).second, xup);
    EXPECT_EQ(detector.regionOfInterestBounds(1).first, ylow);
    EXPECT_EQ(detector.regionOfInterestBounds(1).second, yup);

    // replacing region of interest with a new one
    double xlow2(-2.1 * deg), ylow2(1.1 * deg), xup2(4.1 * deg), yup2(3.1 * deg);
    detector.setRegionOfInterest(xlow2, ylow2, xup2, yup2);
    EXPECT_EQ(detector.regionOfInterestBounds(0).first, xlow2);
    EXPECT_EQ(detector.regionOfInterestBounds(0).second, xup2);
    EXPECT_EQ(detector.regionOfInterestBounds(1).first, ylow2);
    EXPECT_EQ(detector.regionOfInterestBounds(1).second, yup2);
}

// Create detector map in the presence of region of interest.
TEST(SphericalDetector, regionOfInterestAndDetectorMap)
{
    SphericalDetector detector(6, -2 * deg, 6 * deg, 4, 0 * deg, 4 * deg);

    detector.setRegionOfInterest(0.1 * deg, 1.1 * deg, 2.999 * deg, 2.9 * deg);
    // Creating map in default units, which are radians and checking that axes are clipped
    // to region of interest.
    auto data = detector.createDetectorMap();
    EXPECT_EQ(data.axis(0).size(), 3u);
    EXPECT_EQ(data.axis(0).min(), 0.1 * deg);
    EXPECT_EQ(data.axis(1).size(), 2u);
    EXPECT_EQ(data.axis(1).min(), 1.1 * deg);
    EXPECT_EQ(data.axis(1).max(), 2.9 * deg);
}

// Checking clone in the presence of ROI and masks.
TEST(SphericalDetector, Clone)
{
    SphericalDetector detector(6, -2 * deg, 6 * deg, 4, 0 * deg, 4 * deg);
    detector.setRegionOfInterest(0.1 * deg, 1.1 * deg, 3.9 * deg, 2.9 * deg);
    detector.addMask(Rectangle(-0.9 * deg, 0.1 * deg, 0.9 * deg, 1.9 * deg), true);
    detector.addMask(Rectangle(3.1 * deg, 2.1 * deg, 4.9 * deg, 3.9 * deg), true);
    detector.setDetectorResolution(
        ConvolutionDetectorResolution(ResolutionFunction2DGaussian(1, 1)));

    std::vector<size_t> indexes1 = detector.activeIndices();

    std::unique_ptr<SphericalDetector> clone(detector.clone());

    auto data = clone->createDetectorMap();
    EXPECT_EQ(data.axis(0).size(), 4u);
    EXPECT_EQ(data.axis(0).min(), 0.1 * deg);
    EXPECT_EQ(data.axis(0).max(), 3.9 * deg);
    EXPECT_EQ(data.axis(1).size(), 2u);
    EXPECT_EQ(data.axis(1).min(), 1.1 * deg);
    EXPECT_EQ(data.axis(1).max(), 2.9 * deg);

    // checking iteration over the map of cloned detector
    std::vector<size_t> indexes2 = detector.activeIndices();
    EXPECT_EQ(indexes2, indexes1);
}
