#include "indoor_plan.h"

#include <yandex/maps/proto/indoor/indoor.pb.h>
#include <maps/renderer/libs/base/include/geom/wkb_reader.h>
#include <maps/renderer/libs/base/include/geom/box.h>
#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/point.h>

using namespace maps;
using namespace maps::renderer;

namespace maps::wiki::renderer {

namespace {

namespace columns {

constexpr auto GEOM = "geom";
constexpr auto INDOOR_LEVEL_ID = "indoor_level_id";
constexpr auto INDOOR_LEVEL_NAME = "indoor_level_name";
constexpr auto INDOOR_LEVEL_ORDER = "indoor_level_order";
constexpr auto IS_DEFAULT = "is_default";
constexpr auto IS_UNDERGROUND = "is_underground";

} // namespace columns

struct Level
{
    std::string id;
    std::string name;
    bool isDefault;
    bool isUnderground;
    base::BoxD box;
};
using Levels = std::vector<Level>;

} // namespace

std::string indoorPlan(
    pgpool3::TransactionHandle& txn,
    TIndoorPlanId planId)
{
    std::stringstream query;
    query << "SELECT "
          << "      ST_AsBinary(v.the_geom) as " << columns::GEOM << " ,"
          << "      cast(r.slave_id as text) as " << columns::INDOOR_LEVEL_ID << " ,"
          << "      v.domain_attrs->'indoor_level:universal' as " << columns::INDOOR_LEVEL_NAME << " ,"
          << "      v.domain_attrs?'indoor_level:default' as " << columns::IS_DEFAULT << " ,"
          << "      v.domain_attrs?'indoor_level:underground' as " << columns::IS_UNDERGROUND << " ,"
          << "      v.domain_attrs->'indoor_level:order' as " << columns::INDOOR_LEVEL_ORDER << " "
          << " FROM objects_a_view v"
          << " LEFT JOIN objects_r r ON r.slave_id = v.id"
          << " WHERE v.domain_attrs?'cat:indoor_level'"
          << "    AND r.master_id = " << planId
          << " ORDER BY v.domain_attrs->'indoor_level:order'";

    auto result = txn->exec(query.str());
    if (result.size() == 0) {
        return "";
    }

    auto idIdx = result.column_number(columns::INDOOR_LEVEL_ID);
    auto nameIdx = result.column_number(columns::INDOOR_LEVEL_NAME);
    auto isDefaultIdx = result.column_number(columns::IS_DEFAULT);
    auto isUndergroundIdx = result.column_number(columns::IS_UNDERGROUND);
    auto geomIdx = result.column_number(columns::GEOM);

    Levels levels;

    std::string defaultLevelId = result.at(0).at(idIdx).as<std::string>();
    for (const auto& tuple : result) {
        Level level;
        level.id = tuple.at(idIdx).as<std::string>();
        level.name = tuple.at(nameIdx).as<std::string>();
        level.isDefault = tuple.at(isDefaultIdx).as<bool>();
        level.isUnderground = tuple.at(isUndergroundIdx).as<bool>();

        auto geomField = tuple.at(geomIdx);
        auto geom = pqxx::binarystring(geomField);
        base::Vertices vertices;
        base::readWkb({geom.get(), geom.size()}, &vertices);
        level.box = base::BoxD(vertices);

        if (level.isDefault) {
            defaultLevelId = level.id;
        }
        levels.push_back(level);
    }

    base::BoxD box(levels.front().box);

    yandex::maps::proto::indoor::Plan protoPlan;
    for (const auto& level : levels) {
        auto protoLevel = protoPlan.add_levels();
        protoLevel->set_id(level.id.c_str());
        protoLevel->set_name(level.name.c_str());
        protoLevel->set_isunderground(level.isUnderground);

        box.enlarge(level.box);
    }
    protoPlan.set_defaultlevelid(defaultLevelId.c_str());

    auto lowerPoint = geolib3::mercator2GeoPoint({box.x1, box.y1});
    auto upperPoint = geolib3::mercator2GeoPoint({box.x2, box.y2});

    protoPlan.mutable_boundary()->mutable_lower_corner()->set_lon(lowerPoint.x());
    protoPlan.mutable_boundary()->mutable_lower_corner()->set_lat(lowerPoint.y());
    protoPlan.mutable_boundary()->mutable_upper_corner()->set_lon(upperPoint.x());
    protoPlan.mutable_boundary()->mutable_upper_corner()->set_lat(upperPoint.y());

    TString binaryData;
    Y_PROTOBUF_SUPPRESS_NODISCARD protoPlan.SerializeToString(&binaryData);
    return std::string(binaryData);
}

} // namespace maps::wiki::renderer
