#include "Device/Detector/SimulationAreaIterator.h"
#include "Device/Detector/SphericalDetector.h"
#include "Device/Mask/Rectangle.h"
#include "Tests/GTestWrapper/google_test.h"
#include <iostream>
#include <memory>

//! Iteration over non-masked detector

TEST(SimulationAreaTest, detectorIteration)
{
    SphericalDetector detector(4, -1.0, 3.0, 2, 0.0, 2.0);

    std::vector<size_t> expectedIndexes = {0, 1, 2, 3, 4, 5, 6, 7};

    std::vector<size_t> indexes;
    std::vector<size_t> detectorIndexes;
    detector.iterateOverNonMaskedPoints([&](const auto it) {
        indexes.push_back(it.roiIndex());
        detectorIndexes.push_back(it.detectorIndex());
    });
    EXPECT_EQ(indexes, expectedIndexes);
    EXPECT_EQ(detectorIndexes, expectedIndexes);
}

//! Iteration over masked detector

TEST(SimulationAreaTest, maskedIteration)
{
    SphericalDetector detector(5, -1.0, 4.0, 4, 0.0, 4.0);
    detector.addMask(Rectangle(0.1, 1.1, 2.9, 2.9), true);
    detector.addMask(Rectangle(3.1, 3.1, 3.9, 3.9), true);

    std::vector<size_t> expectedIndexes = {0, 1, 2, 3, 4, 7, 8, 11, 12, 15, 16, 17, 18};
    std::vector<size_t> indexes;
    detector.iterateOverNonMaskedPoints([&](const auto it) { indexes.push_back(it.roiIndex()); });
    EXPECT_EQ(indexes, expectedIndexes);
}

//! Iteration over the detector with first and last bin masked

TEST(SimulationAreaTest, maskedCornerIteration)
{
    SphericalDetector detector(5, -1.0, 4.0, 4, 0.0, 4.0);
    detector.addMask(Rectangle(-0.9, 0.1, -0.1, 0.9), true);
    detector.addMask(Rectangle(3.1, 3.1, 3.9, 3.9), true);

    std::vector<size_t> expectedIndexes = {1,  2,  3,  4,  5,  6,  7,  8,  9,
                                           10, 11, 12, 13, 14, 15, 16, 17, 18};
    std::vector<size_t> indexes;
    std::vector<size_t> elementIndexes;
    detector.iterateOverNonMaskedPoints([&](const auto it) { indexes.push_back(it.roiIndex()); });
    EXPECT_EQ(indexes, expectedIndexes);
}

//! Iteration when whole detector is masked

TEST(SimulationAreaTest, allMaskedIteration)
{
    SphericalDetector detector(5, -1.0, 4.0, 4, 0.0, 4.0);
    detector.addMask(Rectangle(-0.9, 0.1, 3.9, 3.9), true);

    std::vector<size_t> indexes;

    detector.iterateOverNonMaskedPoints([&](const auto it) { indexes.push_back(it.roiIndex()); });

    EXPECT_EQ(indexes.size(), size_t(0));
}

//! Iteration when RegionOfInterest and masks are present

TEST(SimulationAreaTest, maskAndRoiIteration)
{
    SphericalDetector detector(5, -1.0, 4.0, 4, 0.0, 4.0);
    detector.setRegionOfInterest(0.1, 1.1, 2.9, 3.9);
    detector.addMask(Rectangle(-0.9, 0.1, 0.9, 1.9), true);

    std::vector<size_t> expectedRoiIndexes = {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<size_t> expectedDetectorIndexes = {6, 7, 9, 10, 11, 13, 14, 15};
    std::vector<size_t> indexes;
    std::vector<size_t> elementIndexes;
    std::vector<size_t> detectorIndexes;
    std::vector<size_t> roiIndexes;
    detector.iterateOverNonMaskedPoints([&](const auto it) {
        indexes.push_back(it.roiIndex());
        detectorIndexes.push_back(it.detectorIndex());
        roiIndexes.push_back(it.roiIndex());
    });
    EXPECT_EQ(indexes, expectedRoiIndexes);
    EXPECT_EQ(detectorIndexes, expectedDetectorIndexes);
    EXPECT_EQ(roiIndexes, expectedRoiIndexes);

    // Now same as above, but using IDetector::iterate
    indexes.clear();
    detectorIndexes.clear();
    roiIndexes.clear();
    detector.iterateOverNonMaskedPoints([&](const auto it) {
        indexes.push_back(it.roiIndex());
        detectorIndexes.push_back(it.detectorIndex());
        roiIndexes.push_back(it.roiIndex());
    });
    EXPECT_EQ(indexes, expectedRoiIndexes);
    EXPECT_EQ(detectorIndexes, expectedDetectorIndexes);
    EXPECT_EQ(roiIndexes, expectedRoiIndexes);
}

//! Checking index of ROI

TEST(SimulationAreaTest, indexInRoi)
{
    SphericalDetector detector(5, -1.0, 4.0, 4, 0.0, 4.0);
    detector.setRegionOfInterest(0.1, 1.1, 2.9, 3.9);
    detector.addMask(Rectangle(-0.9, 0.1, 0.9, 1.9), true);

    std::vector<size_t> expectedIndexes = {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<size_t> expectedDetectorIndexes = {6, 7, 9, 10, 11, 13, 14, 15};
    std::vector<size_t> expectedRoi = {1, 2, 3, 4, 5, 6, 7, 8};
    std::vector<size_t> indexes;
    std::vector<size_t> roiIndexes;
    std::vector<size_t> detectorIndexes;
    detector.iterateOverNonMaskedPoints([&](const auto it) {
        indexes.push_back(it.roiIndex());
        roiIndexes.push_back(it.roiIndex());
        detectorIndexes.push_back(it.detectorIndex());
    });
    EXPECT_EQ(indexes, expectedIndexes);
    EXPECT_EQ(roiIndexes, expectedRoi);
    EXPECT_EQ(detectorIndexes, expectedDetectorIndexes);
}
