#include "hotspot.h"
#include "maps/wikimap/mapspro/services/editor/src/utils.h"

#include <maps/libs/log8/include/log8.h>
#include <maps/libs/tile/include/utils.h>
#include <geos/geom/CoordinateFilter.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Point.h>

namespace maps
{
namespace wiki
{

class TransformToTileFilter
{
public:
    /**
    * Construct filter which
    * perform affine transformation for every coordinate
    * making it relative ot the specified point in pixels
    */
    TransformToTileFilter(const TGeoPoint& corner, TZoom zoom)
    {
        TMercatorPoint mercatorBase = common::geodeticTomercator(corner.x(), corner.y());
        a_ = 1.0/tile::zoomToResolution(zoom);
        e_ = - a_;
        b_ = 0;
        d_ = 0;
        xoff_ = - mercatorBase.x() * a_;
        yoff_ = mercatorBase.y() * a_;
    }

    geolib3::Point2 operator()(const TGeoPoint& point)
    {
        return geolib3::Point2(
                floor(a_ * point.x() + b_ * point.y() + xoff_),
                floor(d_ * point.x() + e_ * point.y() + yoff_));
    }

private:
    double a_;
    double e_;
    double b_;
    double d_;
    double xoff_;
    double yoff_;

};

namespace
{
std::list<Geom>
unwrapMultiGeom(std::list<Geom>&& subGeoms)
{
    for (auto it = subGeoms.begin(); it != subGeoms.end();) {
        auto& subGeom = *it;
        if (subGeom->getNumGeometries() < 2) {
            ++it;
            continue;
        }
        for (size_t i = 0; i < subGeom->getNumGeometries(); ++i) {
            subGeoms.emplace_back(subGeom->getGeometryN(i)->clone());
        }
        it = subGeoms.erase(it);
    }
    return std::move(subGeoms);
}

std::list<Geom>
createHotspotGeometry(const Geom& geom, double width,
    PolygonBufferPolicy polygonBufferPolicy,
    unsigned int simplificationTolerance)
{
    std::list<Geom> buffers;
    if(geom->getGeometryTypeId() == geos::geom::GEOS_POLYGON){
        buffers = unwrapMultiGeom(createPolygonBuffers(geom, width, polygonBufferPolicy));
    } else {
        buffers = unwrapMultiGeom({geom.createBuffer(width)});
    }
    for (auto& buffer : buffers) {
        buffer.simplify(simplificationTolerance);
    }
    return buffers;
}
}

Hotspot::Hotspot(const TGeoPoint& corner
                 , TOid id
                 , const Geom& geom
                 , TZoom zoom
                 , unsigned int simplificationTolerance
                 , double width
                 , PolygonBufferPolicy polygonBufferPolicy)
    :corner_(corner),
    objectId_(id)
{
    TransformToTileFilter filter(corner_, zoom);
    Geom g = geom.transformed(filter);
    geoms_ = createHotspotGeometry(g, width,
        polygonBufferPolicy, simplificationTolerance);
}
}//wiki
}//maps
