#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/traffic_signs/include/yandex/maps/mrc/traffic_signs/signs.h>

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

#include <maps/libs/json/include/value.h>

namespace maps::mrc::db::eye {

enum class RecognitionType {
    DetectSign,
    DetectTrafficLight,
    DetectRoadMarking,
    DetectHouseNumber,
    DetectPanel,
};

using RecognitionTypes = std::vector<RecognitionType>;

DECLARE_ENUM_IO(RecognitionType);


enum class DetectionType {
    Sign,
    TrafficLight,
    RoadMarking,
    HouseNumber,
};

using DetectionTypes = std::vector<DetectionType>;

DECLARE_ENUM_IO(DetectionType);


struct DetectedPanel {
    static const RecognitionType RECOGNITION_TYPE = RecognitionType::DetectPanel;

    static DetectedPanel fromJson(const json::Value& value);

    common::ImageBox restBox;
};

json::Value toJson(const DetectedPanel& attrs);


template <class DetectedObject>
class DetectedObjects: public std::vector<DetectedObject> {
public:
    static const RecognitionType RECOGNITION_TYPE = DetectedObject::RECOGNITION_TYPE;

    using std::vector<DetectedObject>::vector;

    static DetectedObjects fromJson(const json::Value& values) {
        DetectedObjects attrs;
        for (const json::Value& value : values) {
            attrs.push_back(DetectedObject::fromJson(value));
        }

        return attrs;
    }
};

template <class DetectedObject>
json::Value toJson(const DetectedObjects<DetectedObject>& attrs) {
    std::vector<json::Value> values;
    for (const DetectedObject& itemAttrs : attrs) {
        values.push_back(toJson(itemAttrs));
    }

    return json::Value{values};
}

struct BaseDetectedObject {
    static BaseDetectedObject fromJson(const json::Value& value);

    common::ImageBox box;
};

struct DetectedSign {
    static const RecognitionType RECOGNITION_TYPE = RecognitionType::DetectSign;
    static const DetectionType DETECTION_TYPE = DetectionType::Sign;

    static DetectedSign fromJson(const json::Value& value);

    common::ImageBox box;
    traffic_signs::TrafficSign type;
    double typeConfidence;
    bool temporary;
    double temporaryConfidence;
};

json::Value toJson(const DetectedSign& attrs);

using DetectedSigns = DetectedObjects<DetectedSign>;


struct DetectedTrafficLight {
    static const RecognitionType RECOGNITION_TYPE = RecognitionType::DetectTrafficLight;
    static const DetectionType DETECTION_TYPE = DetectionType::TrafficLight;

    static DetectedTrafficLight fromJson(const json::Value& value);

    common::ImageBox box;
    double confidence;
};

json::Value toJson(const DetectedTrafficLight& attrs);

using DetectedTrafficLights = DetectedObjects<DetectedTrafficLight>;


struct DetectedHouseNumber {
    static const RecognitionType RECOGNITION_TYPE = RecognitionType::DetectHouseNumber;
    static const DetectionType DETECTION_TYPE = DetectionType::HouseNumber;

    static DetectedHouseNumber fromJson(const json::Value& value);

    common::ImageBox box;
    double confidence;
    std::string number;
};

json::Value toJson(const DetectedHouseNumber& attrs);

using DetectedHouseNumbers = DetectedObjects<DetectedHouseNumber>;


struct DetectedRoadMarking {
    static const RecognitionType RECOGNITION_TYPE = RecognitionType::DetectRoadMarking;
    static const DetectionType DETECTION_TYPE = DetectionType::RoadMarking;

    static DetectedRoadMarking fromJson(const json::Value& json);

    common::ImageBox box;
    traffic_signs::TrafficSign type;
    double confidence;
};

json::Value toJson(const DetectedRoadMarking& attrs);

using DetectedRoadMarkings = DetectedObjects<DetectedRoadMarking>;

} // namespace maps::mrc::db::eye
