#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/exif.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/keypoints.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/image_box.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/types.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detection/include/catboost_visibility_predictor.h>

#include <maps/libs/json/include/value.h>
#include <maps/libs/geolib/include/line.h>
#include <maps/libs/geolib/include/point.h>

#include <vector>
#include <string>


namespace maps::mrc::eye::pov {

const std::string CLUSTER_ID_COLUMN = "cluster_id";
const std::string FEATURE_ID_COLUMN = "feature_id";

struct Detection {
    db::TId id;
    common::ImageBox bbox;
    std::string type;

    static Detection fromJson(const json::Value& value);
};

struct ObjectId {
    db::TId featureId;
    db::TId detectionId;

    auto introspect() const {
        return std::tie(featureId, detectionId);
    }
};

struct ClusterWithFeatureId {
    db::TId clusterId;
    db::TId featureId;

    auto introspect() const {
        return std::tie(clusterId, featureId);
    }
};

class ImageData {
public:
    // Keypoints detection algorithm is trained to process images
    // with max dimension no greater than DESIRED_MAX_SIZE
    const int DESIRED_MAX_SIZE = 1024;

    ImageData(std::string encodedImage, common::ImageOrientation orientation);

    double scale() const { return scale_; }

    cv::Mat decodedImage() const;

    cv::Mat scaledDecodedImage() const;

    const cv::Mat& scaledCarMask() const { return scaledCarMask_; }

    void setScaledCarMask(cv::Mat mask)
    {
        scaledCarMask_ = mask;
    }

    void resetScaledCarMask() { scaledCarMask_.release(); }

    const common::Keypoints& scaledKpts() const { return scaledKpts_; }

    void setScaledKpts(common::Keypoints kpts)
    {
        scaledKpts_ = std::move(kpts);
    }

    void resetEncodedImage() { encodedImage_.clear(); }

    const common::Size& imageSize() const { return imageSize_; }

private:
    std::string encodedImage_;
    common::Size imageSize_;
    common::ImageOrientation orientation_;
    // scale for converting image to desired size
    double scale_;
    cv::Mat scaledCarMask_;
    common::Keypoints scaledKpts_;

    double getImageScale(const cv::Mat& image);
};


struct ObjectMatchInfo {
    db::TId clusterId;
    ObjectId objectId;
    db::TId predictedFeatureId;
    FrameMatchPlace matchPlace;
    FramesMatchData framesMatch;
    MatchEpilines epilines;
};

/// Data model for the format of a YT-table that is accepted
/// by catboost as an input
struct CatboostDatasetRow {
    std::string key;
    std::string value;

    template<class T>
    static auto introspect(T& t)
    {
        return std::tie(
            t.key,
            t.value
        );
    }

    static auto columns() {
        return std::make_tuple(
            "key",
            "value"
        );
    }
};


} // namespace maps::mrc::eye::pov
