#include <maps/wikimap/mapspro/services/mrc/libs/common/include/algorithm/sample.h>

#include <maps/wikimap/mapspro/services/mrc/libs/sideview_classifier/include/sideview.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>

#include <algorithm>

namespace maps::mrc::classifiers {

//typedef template<class FeatureIt>std::function<common::Blob(FeatureIt)> LoadFeatureImageData;

/*
    Загружает изображение для feature.
    loadFeatureImageData загружает сжатое изображение для feature
*/
template <class FeatureIt, class Function>
cv::Mat loadFeatureImage(Function loadFeatureImageData, FeatureIt featureIt)
{
    auto orient = featureIt->orientation();
    auto blob = loadFeatureImageData(*featureIt);
    auto mat = common::decodeImage(blob);
    return common::transformByImageOrientation(mat, orient);
}

/*
    Возвращает true, если у всех Feature выставлен один и тот же
    параметр cameraDeviation
*/
template <class FeatureIt>
bool haveSameCameraDeviation(FeatureIt first, FeatureIt last)
{
    return std::all_of(first, last, [&](const auto& feature) {
        return feature.hasCameraDeviation()
               && feature.cameraDeviation() == first->cameraDeviation();
    });
}

/*
    Классифицирует проезд от first до last и выдаёт общий
    поворот камеры.

    FeatureIt должны быть упорядочены по timestamp, и иметь один и
    тот же sourceId

    loadFeatureImageData загружает сжатое изображение для feature
*/
template <class FeatureIt, class Function>
db::CameraDeviation
estimateCameraDeviation(Function loadFeatureImageData, FeatureIt first, FeatureIt last)
{
    static sideview::SideViewClassifier classifier;
    float forwardConfidence = 0.;
    float sideConfidence = 0.;
    for (auto it : common::sample(first, last)) {
        REQUIRE(it != first, "Previous elements does not exist");
        auto[type, confidence]
            = classifier.inference(loadFeatureImage(loadFeatureImageData, std::prev(it)),
                                   loadFeatureImage(loadFeatureImageData, it));
        if (type == sideview::SideViewType::SideView) {
            sideConfidence += confidence;
        }
        else {
            forwardConfidence += confidence;
        }
    }
    return forwardConfidence < sideConfidence ? db::CameraDeviation::Right
                                              : db::CameraDeviation::Front;
}

} // namespace maps::mrc::classifiers