#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/object/include/loader.h>
#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/point.h>
#include <maps/libs/geolib/include/static_geometry_searcher.h>

#include <util/generic/string.h>

namespace maps::mrc::addr_pt_searcher {


/*
    1. Заменяем кириллические символы (прописные и строчные) на "похожие" по написанию латинские,
    если такие есть..
    2. Заменяем строчные буквы на прописные
    3. Если validSymbols не пустой, то удаляем символы, которые не содержатся в validSymbols.
*/
TUtf16String normalizeSymbolsInString(const TUtf16String& str, const std::set<char16_t>& validSymbols);

/*
    Функция вычисляет расстояние между двумя строками с номером дома.
    Игнорируются все совпадающие символы из начала строки.
    Если в одной из строк появляется один из символов '/', '-', 'K', то он
    игнорируется, но при этом расстояние увеличивается на 0.6

    1. Если первая строка окончилась, а вторая еще содержит символы, то
    расстояние считается как:
        a) если первый оставшийся символ второй строки '/', то
            (кол-во символов оставшихся во второй строке) * 0.05
        б) иначе
            (кол-во символов оставшихся во второй строке) * 0.1
    2. Если вторая строка окончилась, а первая еще содержит символы, то
    расстояние считается как кол-во символов оставшихся в первой строке * 0.5
    3. Если нашли разные символы в строках, то возвращаем 2.
*/
double stringDistance(const TUtf16String& featureNumber, const TUtf16String& candidateNumber);

struct AddressPointCandidate{
    AddressPointCandidate(const geolib3::Point2& _mercatorPos, const TString& _number, double _confidence)
        : mercatorPos(_mercatorPos), number(_number), confidence(_confidence) {}
    geolib3::Point2 mercatorPos;
    TString number;
    double confidence;
    bool operator<(const AddressPointCandidate& other) const
    {
        return std::tie(mercatorPos, number) < std::tie(other.mercatorPos, other.number);
    }
};

class AddressPointSearcher {
    struct NamedPoint {
        NamedPoint(const geolib3::Point2& _mercatorPos, const std::string& _name)
            : mercatorPos(_mercatorPos), name(_name) {}
        const geolib3::Point2 mercatorPos;
        std::string name;
    };
    class BuildingSearcher {
    public:
        typedef maps::geolib3::StaticGeometrySearcher<maps::geolib3::Polygon2, int> InternalSearcher;
    public:
        explicit BuildingSearcher(const maps::mrc::object::Buildings& buildings);

        const InternalSearcher::SearchResult find(const geolib3::Point2& mercatorPos) const;
        const InternalSearcher::SearchResult find(const maps::geolib3::BoundingBox& searchBox) const;
    private:
        void insert(const maps::geolib3::Polygon2& pgn);
        InternalSearcher searcher_;
        std::list<maps::geolib3::Polygon2> polygons_;
    };

    class GeometrySearcher {
        typedef geolib3::StaticGeometrySearcher<geolib3::Polygon2, NamedPoint> InternalSearcher;
    public:
        GeometrySearcher(
            const object::AddressPointWithNames& addrPointsWithName,
            const maps::mrc::object::Buildings& buildings,
            const geolib3::BoundingBox& bbox);

        const InternalSearcher::SearchResult find(const geolib3::Point2& mercatorPos) const;
    private:
        void insert(const geolib3::Polygon2& pgn, const geolib3::Point2& addrPointMercatorPos, const std::string& name);
        InternalSearcher searcher_;
        std::list<geolib3::Polygon2> polygons_;
        geolib3::BoundingBox searchBbox_;
    };
public:
    AddressPointSearcher(object::Loader& loader, std::set<char16_t> recognizerSupportedSymbols);

    /*
        Меняет позицию в окрестностях которой будем искать адресные точки
        Читает здания и адресные точки с карты. Создает геометрический поиск.
        Операция может быть достаточно не быстрой.
    */
    void resetPosition(const geolib3::Point2& mercatorPos);
    void resetPosition(const geolib3::BoundingBox& mercatorBbox);

    /*
        Кандидаты упорядочены по уменьшению confidence
    */
    std::vector<AddressPointCandidate> find(const geolib3::Point2& mercatorPos, const TString& number, double minConfidence) const;
    /*
        В случае если нам не нужны кандидаты, а только проверить есть ли они в принципе.
    */
    bool hasCandidate(const geolib3::Point2& mercatorPos, const TString& number, double minConfidence) const;
private:
    object::Loader& loader_;
    std::set<char16_t> recognizerSupportedSymbols_;

    void resetGeometrySearcher(const geolib3::BoundingBox& searchBbox);
    std::unique_ptr<GeometrySearcher> geometrySearcher_;
};

} //maps::mrc::addr_pt_searcher