#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/recognition_value.h>

#include <maps/libs/common/include/exception.h>

#include <string>

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

namespace {

const std::string BOX = "box";
const std::string REST_BOX = "rest_box";
const std::string TYPE_SHORT = "tp";
const std::string TYPE = "type";
const std::string TEMPORARY_SHORT = "tmp";
const std::string TEMPORARY = "temporary";

const std::string CONFIDENCE_SHORT = "cnf";
const std::string CONFIDENCE = "confidence";
const std::string TYPE_CONFIDENCE_SHORT = "tp_cnf";
const std::string TYPE_CONFIDENCE = "type_confidence";
const std::string TEMPORARY_CONFIDENCE_SHORT = "tmp_cnf";
const std::string TEMPORARY_CONFIDENCE = "temporary_confidence";

const std::string NUMBER = "number";

inline void checkConfidence(double confidence){ ASSERT(0 <= confidence and confidence <= 1); }

json::Value getAny(const json::Value& value, std::string_view firstKey, std::string_view secondKey) {
    return value.hasField(firstKey) ? value[firstKey] : value[secondKey];
}

} // namespace

DetectedPanel DetectedPanel::fromJson(const json::Value& value) {
    return DetectedPanel{
        common::ImageBox::fromJson(getAny(value, REST_BOX, BOX))
    };
}

json::Value toJson(const DetectedPanel& attrs) {
    return json::Value{
        {BOX, common::toJson(attrs.restBox)}
    };
}


BaseDetectedObject BaseDetectedObject::fromJson(const json::Value& value) {
    return BaseDetectedObject{
        common::ImageBox::fromJson(value[BOX])
    };
}


DetectedSign DetectedSign::fromJson(const json::Value& value) {
    return DetectedSign{
        common::ImageBox::fromJson(value[BOX]),
        traffic_signs::stringToTrafficSign(getAny(value, TYPE, TYPE_SHORT).as<std::string>()),
        getAny(value, TYPE_CONFIDENCE, TYPE_CONFIDENCE_SHORT).as<double>(),
        getAny(value, TEMPORARY, TEMPORARY_SHORT).as<bool>(),
        getAny(value, TEMPORARY_CONFIDENCE, TEMPORARY_CONFIDENCE_SHORT).as<double>()
    };
}

json::Value toJson(const DetectedSign& attrs) {
    checkConfidence(attrs.typeConfidence);
    checkConfidence(attrs.temporaryConfidence);

    return json::Value{
        {BOX, common::toJson(attrs.box)},
        {TYPE_SHORT, json::Value{traffic_signs::toString(attrs.type)}},
        {TYPE_CONFIDENCE_SHORT, json::Value{attrs.typeConfidence}},
        {TEMPORARY_SHORT, json::Value{attrs.temporary}},
        {TEMPORARY_CONFIDENCE_SHORT, json::Value{attrs.temporaryConfidence}},
    };
}

DetectedTrafficLight DetectedTrafficLight::fromJson(const json::Value& value) {
    return DetectedTrafficLight{
        common::ImageBox::fromJson(value[BOX]),
        getAny(value, CONFIDENCE, CONFIDENCE_SHORT).as<double>()
    };
}

json::Value toJson(const DetectedTrafficLight& attrs) {
    checkConfidence(attrs.confidence);

    return json::Value{
        {BOX, common::toJson(attrs.box)},
        {CONFIDENCE_SHORT, json::Value{attrs.confidence}},
    };
}


DetectedHouseNumber DetectedHouseNumber::fromJson(const json::Value& value) {
    return DetectedHouseNumber{
        common::ImageBox::fromJson(value[BOX]),
        getAny(value, CONFIDENCE, CONFIDENCE_SHORT).as<double>(),
        value[NUMBER].as<std::string>()
    };
}

json::Value toJson(const DetectedHouseNumber& attrs) {
    checkConfidence(attrs.confidence);

    return json::Value{
        {BOX, common::toJson(attrs.box)},
        {CONFIDENCE_SHORT, json::Value{attrs.confidence}},
        {NUMBER, json::Value(attrs.number)}
    };
}


DetectedRoadMarking DetectedRoadMarking::fromJson(const json::Value& value) {
    return DetectedRoadMarking{
        common::ImageBox::fromJson(value[BOX]),
        traffic_signs::stringToTrafficSign(getAny(value, TYPE, TYPE_SHORT).as<std::string>()),
        getAny(value, CONFIDENCE, CONFIDENCE_SHORT).as<double>(),
    };
}

json::Value toJson(const DetectedRoadMarking& attrs) {
    checkConfidence(attrs.confidence);

    return json::Value {
        {BOX, common::toJson(attrs.box)},
        {TYPE_SHORT, json::Value{traffic_signs::toString(attrs.type)}},
        {CONFIDENCE_SHORT, json::Value{attrs.confidence}},
    };
}

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