#include "delete_junction_callback.h"

#include "topo_relations_processor.h"
#include <maps/wikimap/mapspro/services/editor/src/collection.h>
#include <maps/wikimap/mapspro/services/editor/src/objects_cache.h>
#include <maps/wikimap/mapspro/services/editor/src/relations_manager.h>
#include <maps/wikimap/mapspro/services/editor/src/relations_processor.h>
#include <maps/wikimap/mapspro/services/editor/src/configs/config.h>
#include <maps/wikimap/mapspro/services/editor/src/objects/linear_element.h>

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

namespace maps {
namespace wiki {

DeleteJunctionCallback::DeleteJunctionCallback(
        ObjectsCache& cache)
    : cache_(cache)
{}

void
DeleteJunctionCallback::processRequest(topo::MergeEdgesRequest& request) const
{
    const LinearElement* merged = as<const LinearElement>(cache_.getExisting(request.mergedId()));
    const LinearElement* deleted = as<const LinearElement>(cache_.getExisting(request.deletedId()));

    if (deleted->geom().realLength() > merged->geom().realLength()) {
        request.swapMergedAndDeleted();
        std::swap(merged, deleted);
    }

    if (!merged->canMerge(deleted)) {
        THROW_WIKI_INTERNAL_ERROR(
            "Can not be merged: " << merged->id() << ", " << deleted->id());
    }
}

void
DeleteJunctionCallback::processEvent(const topo::MergeEdgesEvent& event) const
{
    LinearElement* merged = as<LinearElement>(cache_.getExisting(event.mergedId()));
    LinearElement* deleted = as<LinearElement>(cache_.getExisting(event.deletedId()));
    Junction* commonJc = as<Junction>(cache_.getExisting(event.nodeId()));
    RelationsProcessor processor(cache_);

    cache_.relationsManager().loadRelations(
        RelationType::Master,
        TOIds {commonJc->id(), deleted->id(), merged->id()});

    processor.deleteRelations(RelationType::Master, commonJc);
    processor.deleteRelations(RelationType::Slave, commonJc);

    TOIds masterIds;
    for (const auto& rInfo : deleted->masterRelations().range()) {
        masterIds.insert(rInfo.id());
    }
    for (const auto& rInfo : merged->masterRelations().range()) {
        masterIds.insert(rInfo.id());
    }
    processor.replaceWithMerged(RelationType::Master, deleted, merged);

    if (!commonJc->masterRelations().range().empty() ||
        !commonJc->slaveRelations().range().empty())
    {
        THROW_WIKI_INTERNAL_ERROR(
            "Some links to other objects are left in junction being deleted");
    }

    TopoRelationsProcessor topoProcessor(cache_);
    GeoObjectCollection toUpdateSeqNums = cache_.get(masterIds);
    for (const auto& objPtr : toUpdateSeqNums) {
        topoProcessor.updateSeqNums(objPtr.get());
    }
}

} // namespace maps
} // namespace wiki
