#pragma once

#include "opencv2/core/core.hpp"
#include <vector>


namespace maps {
namespace wiki {
namespace autocart {

// параметры выделения вершин из изображения с вероятностями
struct ExtractVerticesParams
{
    ExtractVerticesParams()
        : m_minThreshold(0.2)
        , m_nmsCellSz(6)
    {}
    // минимальное значение веса в изображении с вероятностями вершин. Всё что меньше - занулиться,
    // в остатках будем искать локальные максимумы (величина порога от 0. до 1. хотя изобпражение
    // у нас 8UC1)
    double m_minThreshold;
    // размер ячейки для non maximum suppression вершин
    int m_nmsCellSz;
};

// параметры выделения рёбер из изображения с вероятностями
struct ExtractEdgesParams
{
    ExtractEdgesParams()
        : m_vertRad(1)
        , m_minWeightThreshold(0.35)
        , m_maxLength(100.)
        , m_minLength(5.)
    {}

    int m_vertRad;
    // ребра с весом меньше m_minWeightThreshold не рассматриваем
    double m_minWeightThreshold;
    // рассматриваем только рёбра с длинами от m_minLength до m_maxLength
    double m_minLength;
    double m_maxLength;
};

// параметры взвешивания циклов (см. функцию штрафа
//    double CoordGraph::weightCycle(const EdgesPath &cycle, const cv::Mat &segm, const WeightCycleParams &param) const)
struct WeightCycleParams
{
    WeightCycleParams()
        : m_smallAngleMax(30.)
        , m_smallAnglePenaltyEps(1e-2)
        , m_nonQuadPenalty(0.0)
        , m_segmAreaCoeff(0.3)
        , m_angleDeltaCoefPenalty(10.0)
        , m_minArea(500)
    {}

    // если цикл содержит вершину с углом ребер меньше m_smallAngleMax
    // то вес такого цикла штрафуется используя величину m_smallAnglePenalty
    double m_smallAngleMax;
    double m_smallAnglePenaltyEps;
    // снижаем вес цикла на каждое ребро после первых 4-х
    double m_nonQuadPenalty;

    // величина используется как коэффициент штрафа отличия углов от 90 градусов
    double m_angleDeltaCoefPenalty;

    // вес складывается из веса ребер (с учетом штрафа за углы) +
    // относительная площадь внутренности цикла, которая размечена как здание при семантической сегментации
    //      m_segmAreaCoeff - коэффициент с которым добавляем относительную площадь
    double m_segmAreaCoeff;

    // минимальная площадь внутренности цикла (в пикселях), если меньше зануляем вес
    int m_minArea;
};

//параметры выбора "лучших" циклов из набора возможных
struct BestCycleParams
{
    BestCycleParams()
        : m_minCycleWeight(0.3)
        , m_minIntersectionArea(300)
        , m_minRelativeIntersectionArea(0.2)
        , m_newEdgesPercent(0.5)
    {}
    // все циклы с весами меньшими m_minCycleWeight на выход подаваться не будут
    double m_minCycleWeight;

    // если цикл пересекается с уже выбраными больше чем на m_minIntersectionArea (в пикселях),
    // то в Best цикл не отправляется
    int m_minIntersectionArea;

    // если площадь пересечения цикла с уже выбраными делённая на площадь цикла, больше чем
    // m_minRelativeIntersectionArea, то в Best цикл не отправляется
    double m_minRelativeIntersectionArea;

    //если процент "новых" ребер в цикле меньше, то цикл не добавляем
    double m_newEdgesPercent;
};

struct ThinPolygonsParams
{
    ThinPolygonsParams()
        : m_maxVertEdgeDist(6.)
        , m_leftRange(0.1)
        , m_rightRange(0.9)
    {}

    // если больше 0.0 то удаляем узкие многоугольники (т.е. если точка лежит от ребра на расстоянии меньше
    // m_maxVertEdgeDist, то ребро удаляется)
    double m_maxVertEdgeDist;

    // мы проверяем куда проектируется точка на ребро, мы хотим удалять ребра на которые близкая
    // вершина проектируется примерно по центру ребра, а не на его концы
    double m_leftRange;
    double m_rightRange;
};

struct ExtractPolygonsParams
{
    ExtractPolygonsParams()
        : m_extractLevelsCount(4)
        , m_keenVerticesMaxAngle(15.)
        , m_removeIntersectEdges(true)
    {}
    ExtractVerticesParams m_vertsParams;
    ExtractEdgesParams m_edgesParams;

    int m_extractLevelsCount;

    // если больше 0.0 удаляем острые вершины (с двумя ребрами угол между которыми меньше m_keenVerticesMaxAngle)
    // если меньше или равен 0.0 то удаляем только изолированные вершины и листья (т.е. вершины с 0 или 1 ребром)
    double m_keenVerticesMaxAngle;
    // удалять или нет пересекающиеся ребра
    bool m_removeIntersectEdges;

    ThinPolygonsParams m_thinPolygonsParams;
    WeightCycleParams m_weightCycleParams;
    BestCycleParams m_bestCycleParams;
};

bool extractPolygons(const cv::Mat &vertData, const cv::Mat &edgeData, const cv::Mat &segm_data, const ExtractPolygonsParams &params, std::vector< std::vector<cv::Point> > &polygons);

} //namespace autocart
} //namespace wiki
} //namespace maps
