#include "module.h"

#include <yandex/maps/wiki/validator/check.h>
#include <yandex/maps/wiki/validator/categories.h>

#include "../utils/misc.h"
#include "../utils/names.h"

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

#include <boost/optional.hpp>

namespace maps {
namespace wiki {
namespace validator {
namespace checks {

using categories::RD;
using categories::RD_NM;
using categories::AD;
using categories::AD_NM;
using categories::AD;
using categories::VEGETATION;
using categories::VEGETATION_NM;

namespace {

void ValidateParentAd(
    const Road* road,
    CheckContext* context)
{
    const auto adId = road->associatedObject().id;
    if (!context->objects<AD>().loaded(adId)) {
        return;
    }

    const AdmUnit* admUnit = context->objects<AD>().byId(road->associatedObject().id);
    if (admUnit->levelKind() == AdmUnit::LevelKind::Other) {
        context->fatal(
            "bound-to-ad-level-kind-other",
            boost::none,
            { road->id(), admUnit->id() });
    }

    if (!utils::isNamed<AD_NM>(admUnit, context)) {
        context->fatal(
            "bound-to-unnamed-ad",
            boost::none,
            { road->id(), admUnit->id() });
    }

    if (!common::isIn(road->type(),
            { Road::Type::CityRoad, Road::Type::CityHighway })) {
        return;
    }

    if (static_cast<int>(admUnit->levelKind())
            < static_cast<int>(AdmUnit::LevelKind::Locality)) {
        context->error("rd-ad-type-mismatch",
            boost::none,
            { road->id(), admUnit->id() });
    }
}

void ValidateParentFt(
    const Road* road,
    CheckContext* context)
{
    const auto ftId = road->associatedObject().id;
    if (!context->objects<VEGETATION>().loaded(ftId)) {
        context->fatal(
            "vegetation-not-loaded",
            boost::none,
            { road->id(), ftId });
    }

    const ContourFeature* vegetation = context->objects<VEGETATION>().byId(ftId);

    if (!common::isIn(vegetation->featureType(), {401, 402, 403, 405, 221})) {
        context->fatal(
            "bound-to-wrong-type-vegetation",
            boost::none,
            { road->id(), vegetation->id() });
    }

    if (!utils::isNamed<VEGETATION_NM>(vegetation, context)) {
        context->fatal(
            "bound-to-unnamed-vegetation",
            boost::none,
            { road->id(), vegetation->id() });
    }
}

} // namespace

VALIDATOR_SIMPLE_CHECK( rd_ad_relations, RD, RD_NM, AD, AD_NM, VEGETATION, VEGETATION_NM )
{
    context->objects<RD>().visit([&](const Road* road)
    {
        switch (road->associatedObject().type) {
            case Road::AssociatedObject::Type::NotSet: {
                if (utils::isNamed<RD_NM>(road, context)) {
                    context->error("unbound-named-road",
                        boost::none,
                        { road->id() });
                }
            }
            return;

            case Road::AssociatedObject::Type::AdmUnit: {
                ValidateParentAd(road, context);
            }
            return;

            case Road::AssociatedObject::Type::Feature: {
                ValidateParentFt(road, context);
            }
            return;
        }
    });
}

} // namespace checks
} // namespace validator
} // namespace wiki
} // namespace maps
