#include "connector_geometry.h"
#include "thread_geometry_helpers.h"

#include <maps/wikimap/mapspro/libs/masstransit/masstransit.h>

#include <yandex/maps/wiki/common/misc.h>
#include <maps/libs/geolib/include/serialization.h>

namespace maps::wiki::masstransit {

YmapsdfBusConnectorGeometryHandler::YmapsdfBusConnectorGeometryHandler(Masstransit& masstransit)
    : YmapsdfObjectHandler(masstransit, masstransit.connectorGeometries)
{ }

std::vector<FtType>
YmapsdfBusConnectorGeometryHandler::ftTypes() const
{
    return {FtType::TransportBusThreadConnector};
}

std::string
YmapsdfBusConnectorGeometryHandler::loadRowsSqlTemplate() const
{
    return BUS_ROWS_SQL_TEMPLATE;
}

void
YmapsdfBusConnectorGeometryHandler::addObject(const pqxx::row& tuple)
{
    const auto connectorId = getAttr<DBID>(tuple, ymapsdf::ID);
    if (!masstransit_.connectorGeometries.count(connectorId)) {
        auto connectorGeom = std::make_shared<ThreadGeometry>(connectorId);
        insert(connectorId, connectorGeom);
    }
    auto& connectorGeom = masstransit_.connectorGeometries[connectorId];

    addRoutedElement(connectorGeom, tuple);
}

void
YmapsdfBusConnectorGeometryHandler::updateObject(Object& object)
{
    auto& connectorGeom = dynamic_cast<ThreadGeometry&>(object);
    if (!common::isIn(masstransit_.connectors[connectorGeom.threadId].ftType, ftTypes())) {
        return;
    }

    routeThreadGeometry(connectorGeom, masstransit_, UseConditions::No);
}

YmapsdfTramConnectorGeometryHandler::YmapsdfTramConnectorGeometryHandler(Masstransit& masstransit)
    : YmapsdfObjectHandler(masstransit, masstransit.connectorGeometries)
{ }

std::vector<FtType>
YmapsdfTramConnectorGeometryHandler::ftTypes() const
{
    return {FtType::TransportTramThreadConnector};
}

std::string
YmapsdfTramConnectorGeometryHandler::loadRowsSqlTemplate() const
{
    return TRAM_ROWS_SQL_TEMPLATE;
}

void
YmapsdfTramConnectorGeometryHandler::addObject(const pqxx::row& tuple)
{
    const auto connectorId = getAttr<DBID>(tuple, ymapsdf::ID);
    if (!masstransit_.connectorGeometries.count(connectorId)) {
        auto connectorGeom = std::make_shared<ThreadGeometry>(connectorId);
        insert(connectorId, connectorGeom);
    }
    auto& connectorGeom = masstransit_.connectorGeometries[connectorId];

    addRoutedElement(connectorGeom, tuple);
}

void
YmapsdfTramConnectorGeometryHandler::updateObject(Object& object)
{
    auto& connectorGeom = dynamic_cast<ThreadGeometry&>(object);
    if (!common::isIn(masstransit_.connectors[connectorGeom.threadId].ftType, ftTypes())) {
        return;
    }

    routeThreadGeometry(connectorGeom, masstransit_, UseConditions::No);
}

YmapsdfMetroConnectorGeometryHandler::YmapsdfMetroConnectorGeometryHandler(Masstransit& masstransit)
    : YmapsdfObjectHandler(masstransit, masstransit.connectorGeometries)
{ }

std::vector<FtType>
YmapsdfMetroConnectorGeometryHandler::ftTypes() const
{
    return {
        FtType::TransportMetroThreadConnector,
        FtType::TransportWaterwayThreadConnector
    };
}

std::string
YmapsdfMetroConnectorGeometryHandler::loadRowsSqlTemplate() const
{
    return METRO_ROWS_SQL_TEMPLATE;
}

void
YmapsdfMetroConnectorGeometryHandler::addObject(const pqxx::row& tuple)
{
    const auto connectorId = getAttr<DBID>(tuple, ymapsdf::ID);
    const auto shape = getAttr<std::string>(tuple, ymapsdf::SHAPE);
    const auto edgePolyline = geolib3::WKT::read<Polyline>(shape);

    auto connectorGeom = std::make_shared<ThreadGeometry>(connectorId);
    connectorGeom->polyline = edgePolyline;

    insert(connectorId, connectorGeom);
}

void
YmapsdfMetroConnectorGeometryHandler::updateObject(Object& object)
{
    auto& connectorGeom = dynamic_cast<ThreadGeometry&>(object);
    const auto connectorId = connectorGeom.threadId;

    try {
        const auto& connector = masstransit_.connectors[connectorId];
        if (!common::isIn(connector.ftType, ftTypes())) {
            return;
        }
        adjustLinearThread(masstransit_, connectorGeom);
    } catch (std::exception& e) {
        DATA_ERROR() << "Connector id=" << connectorId << " geometry error: " << e.what();
        throw;
    }
}

MtrConnectorGeometryHandler::MtrConnectorGeometryHandler(Masstransit& masstransit)
    : MtrObjectHandler(masstransit, masstransit.connectorGeometries)
{ }

void
MtrConnectorGeometryHandler::writeObject(StreamMap& ostreams, const Object& object)
{
    const auto& connectorGeom = dynamic_cast<const ThreadGeometry&>(object);
    auto mtrConnectorId = idMap().getMtrId(connectorGeom.threadId);

    auto& ostream = ostreams[fileNames().front()];
    for (size_t seqId = 0; seqId != connectorGeom.polyline.pointsNumber(); ++seqId) {
        const auto point = connectorGeom.polyline.pointAt(seqId);
        ostream
            << mtrConnectorId << FIELD_SEPARATOR
            << seqId << FIELD_SEPARATOR
            << point.y() << FIELD_SEPARATOR
            << point.x() << std::endl;
    }
}

} // namespace maps::wiki::masstransit
