#pragma once

#include "incidence_direction.h"

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

#include <functional>
#include <map>
#include <vector>

namespace maps::wiki::validator::utils {

constexpr common::AccessId notVehicle = common::AccessId::Pedestrian | common::AccessId::Bicycle;

typedef std::vector<const RoadElement*> RdEls;
typedef std::map<int, RdEls> RdElsByZLevel;

// Long conditions are ignored
typedef std::pair<TId, TId> RdElContinuation;
typedef std::vector<RdElContinuation> RdElContinuations;
typedef std::map<TId, RdElContinuations> RdElContinuationsByJcId;

/* ===== RD_EL filters ===== */

typedef std::function<bool(const RoadElement*)> RdElFilter;
typedef std::function<bool(const RoadElement*, TId)> RdElDirectionFilter;

class RoadAccessIdFilter
{
public:
    explicit RoadAccessIdFilter(common::AccessId accessId)
        : accessId_(accessId)
    {}

    bool operator()(const RoadElement* rdEl) const
    { return common::isSet(accessId_, rdEl->accessId()); }

private:
    common::AccessId accessId_;
};

class BackLaneTaxiFilter
{
public:
    bool operator()(const RoadElement* rdEl) const
    { return rdEl->backTaxi(); }
};

/* ===== RD_EL and RD_JC functions ===== */

int
getRdElZLevelByJc(const RoadElement* rdEl, const Junction* rdJc);

bool
isRdElCanBeFrom(const RoadElement* rdEl, const Junction* rdJc);

bool
isRdElCanBeTo(const RoadElement* rdEl, const Junction* rdJc);

RdElsByZLevel
groupRdElByZLevForJc(
    CheckContext* context,
    const Junction* rdJc,
    common::AccessId accessId);

/* ===== Condition-related functions ===== */

RdElContinuationsByJcId
findProhibitedContinuations(
    CheckContext* context,
    common::AccessId accessId);

bool
canDrive(
    const RoadElement* from,
    const Junction* via,
    const RoadElement* to,
    const RdElContinuationsByJcId& prohibitedContinuations,
    RdElFilter backLaneFilter = [](const RoadElement*) { return false; });

RdEls
findRdElContinuations(
    CheckContext* context,
    TId primaryRdElId,
    RdElFilter graphFilter,
    const RdElContinuationsByJcId& prohibitedContinuations,
    IncidenceDirection incidence = IncidenceDirection::Both,
    RdElFilter backLaneFilter = [](const RoadElement*) { return false; },
    RdElDirectionFilter directionFilter = [](const RoadElement*, TId) { return true; });

} // namespace maps::wiki::validator::utils
