#include "transformer.h"
#include "configuration.h"
#include "record_common.h"
#include <maps/wikimap/mapspro/tools/ymapsdf-conversion/json2ymapsdf/lib/common/config.h>
#include <maps/wikimap/mapspro/tools/ymapsdf-conversion/json2ymapsdf/lib/ymapsdf/schema.h>

namespace maps::wiki::json2ymapsdf::transformers {

namespace {

ymapsdf::Records
transformSingleObject(const tds::Object& object)
{
    ymapsdf::Records records;
    for (const auto* category : object.categories()) {
        const auto range = objectTransformers().equal_range(category);
        REQUIRE(range.first != range.second,
            "No transformer for category '" << category->name() << "'");
        for (auto it = range.first; it != range.second; ++it) {
            records.splice(records.end(), it->second->transform(object));
        }
    }
    return records;
}

ymapsdf::Records
transformSingleLink(const tds::Link& link)
{
    ymapsdf::Records records;
    const auto& tRel = link.relation();
    const auto range = linkTransformers().equal_range(&tRel);
    REQUIRE(range.first != range.second, "No transformer for category " << tRel.name());
    for (auto it = range.first; it != range.second; ++it) {
        records.splice(records.end(), it->second->transform(link));
    }
    return records;
}

ymapsdf::Records
transformLinks(const tds::Object& object)
{
    ymapsdf::Records records;
    for (const auto* category : object.categories()) {
        for(const auto& relation: category->relations()) {
            const auto linksForRelation = object.links().find(*relation);
            if (linksForRelation != object.links().end()) {
                for (const auto& link: linksForRelation->second) {
                    records.splice(records.end(), transformSingleLink(link));
                }
            } else if (relation->ymapsdfRelation()
                && &relation->ymapsdfRelation()->table() == category->tablePtr())
            {
                    tds::Link linkStub(*relation, object[tds::ATTRIBUTE_OBJECT_ID]);
                    records.splice(records.end(), transformSingleLink(linkStub));
            }
        }
    }
    return records;
}

ymapsdf::Records
transformUncertainLinks(const tds::Object& object)
{
    ymapsdf::Records records;
    for (const auto& it: object.links()) {
        for (const auto& link: it.second) {
            records.splice(records.end(), transformSingleLink(link));
        }
    }
    return records;
}

} // namespace

ymapsdf::Records
transform(const tds::Object& object)
{
    ymapsdf::Records records = transformSingleObject(object);

    if (tds::schema::relationsMode().unknown()) {
        records.splice(records.end(), transformUncertainLinks(object));
    } else {
        records.splice(records.end(), transformLinks(object));
    }

    return records;
}

} // namespace maps::wiki::json2ymapsdf::transformers
