#include <maps/wikimap/mapspro/services/mrc/eye/lib/feedback/include/object.h>

#include <maps/wikimap/mapspro/services/mrc/eye/lib/location/include/rotation.h>

namespace maps::mrc::eye {

namespace {

void setObjectAttrs(
    const db::eye::SignAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["type"] = toString(attrs.type);
}

void setObjectAttrs(
    const db::eye::TrafficLightAttrs& /*attrs*/,
    json::ObjectBuilder /*obj*/)
{
    // has no additional attributes
}

void setObjectAttrs(
    const db::eye::HouseNumberAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["number"] = attrs.number;
}

void setObjectAttrs(
    const db::eye::RoadMarkingAttrs& attrs,
    json::ObjectBuilder obj)
{
    obj["type"] = toString(attrs.type);
}

void setObjectAttrs(
    const db::eye::Object& object,
    json::ObjectBuilder obj)
{
    switch (object.type()) {
        case db::eye::ObjectType::Sign:
            setObjectAttrs(object.attrs<db::eye::SignAttrs>(), obj);
            return;
        case db::eye::ObjectType::TrafficLight:
            setObjectAttrs(object.attrs<db::eye::TrafficLightAttrs>(), obj);
            return;
        case db::eye::ObjectType::HouseNumber:
            setObjectAttrs(object.attrs<db::eye::HouseNumberAttrs>(), obj);
            return;
        case db::eye::ObjectType::RoadMarking:
            setObjectAttrs(object.attrs<db::eye::RoadMarkingAttrs>(), obj);
            return;
    }
}

} // namespace

void setObject(
    const db::eye::Object& object,
    const db::eye::ObjectLocation& location,
    json::ObjectBuilder obj)
{
    obj["id"] = std::to_string(object.id());

    setObjectAttrs(object, obj);

    const auto& [heading, orientation, pitch] = decomposeRotation(location.rotation());
    obj["heading"] = heading.value();
    obj["geometry"] = geolib3::geojson(location.geodeticPos());
}

void setObjects(
    const HypothesisContext& context,
    json::ArrayBuilder objects)
{
    REQUIRE(!context.empty(), "Source context may not be empty");

    db::IdTo<db::eye::Object> objectById;
    db::IdTo<db::eye::ObjectLocation> objectLocationByObjectId;
    for (const auto& item : context.items()) {
        objectById.emplace(item.object.id(), item.object);
        objectLocationByObjectId.emplace(item.object.id(), item.objectLocation);
    }

    for (auto it = objectById.begin(); it != objectById.end(); it++) {
        const db::eye::Object& object = it->second;
        const db::eye::ObjectLocation& objectLocation = objectLocationByObjectId.at(object.id());
        objects << [&](json::ObjectBuilder obj) {
            setObject(object, objectLocation, obj);
        };
    }
}

} // namespace maps::mrc::eye
