#include <library/cpp/testing/gtest/gtest.h>
#include <library/cpp/testing/common/env.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/common/include/file_utils.h>
#include <maps/wikimap/mapspro/services/mrc/libs/roadmark_detector/include/roadmarkdetector.h>

#include <opencv2/imgcodecs/imgcodecs_c.h>
#include <opencv2/opencv.hpp>

#include <fstream>
#include <iostream>
#include <sstream>
#include <unordered_set>
#include <utility>
#include <vector>

using namespace testing;

namespace maps {
namespace mrc {
namespace roadmarkdetector {

namespace tests {

namespace {

using TestDataPair = std::pair<std::string, RoadMarkDetectionVector>;
using namespace maps::mrc::traffic_signs;

const std::vector<TestDataPair> TEST_DATA =
{
    {"roadmark000.jpg", {{{352, 373, 128, 275},  TrafficSign::RoadMarkingLaneDirectionFR, 0.90f}}},
    {"roadmark001.jpg", {{{ 88, 384,  59, 110},  TrafficSign::RoadMarkingLaneDirectionF,  0.90f},
                         {{291, 375,  55, 101},  TrafficSign::RoadMarkingLaneDirectionF,  0.90f}}},
};

cv::Mat loadImage(const std::string& name, int flags) {
    static const std::string IMAGES_DIR =
        "maps/wikimap/mapspro/services/mrc/libs/roadmark_detector/tests/images/";
    auto imagePath = static_cast<std::string>(BinaryPath(IMAGES_DIR + name));
    cv::Mat image = cv::imread(imagePath, flags);
    REQUIRE(image.data != nullptr, "Can't load image " << name);
    return image;
}

#define EXPECT_RECTS_IOU(rc1, rc2, thr)                  \
{                                                        \
    const int i_area = (rc1 & rc2).area();               \
    const int u_area = rc1.area() + rc2.area() - i_area; \
    EXPECT_THAT(i_area, Ge(thr * u_area));               \
}

} // namespace

TEST(basic_tests, roadmarkdetector_on_reference_images)
{
    constexpr float IOU_THRESHOLD = 0.5f;

    RoadMarkDetector detector;
    for (size_t i = 0; i < TEST_DATA.size(); i++){
        cv::Mat image = loadImage(TEST_DATA[i].first, cv::IMREAD_COLOR);
        const RoadMarkDetectionVector& rmdv_gt = TEST_DATA[i].second;
        RoadMarkDetectionVector rmdv_test = detector.detect(image);

        EXPECT_THAT(rmdv_test.size(), Eq(rmdv_gt.size()));
        std::sort(rmdv_test.begin(), rmdv_test.end(),
                  [](const RoadMarkDetection& a, const RoadMarkDetection& b) {
                      if (a.box.x < b.box.x)
                          return true;
                      else if (a.box.x == b.box.x)
                          return (a.box.y < b.box.y);
                      else
                          return false;
                  });
        for (size_t j = 0; j < rmdv_test.size(); j++){
            EXPECT_THAT(rmdv_test[j].confidence, Ge(rmdv_gt[j].confidence));
            EXPECT_THAT(rmdv_test[j].type, Eq(rmdv_gt[j].type));
            EXPECT_RECTS_IOU(rmdv_test[j].box, rmdv_gt[j].box, IOU_THRESHOLD);
        }
    }
}

} // namespace test

} // namespace roadmarkdetector
} // namespace mrc
} // namespace maps
