#include "transformation_polyorder0.h"

#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/vector.h>
#include <maps/libs/tile/include/utils.h>

using namespace maps::geolib3;
using namespace maps::tile;

namespace maps::wiki::renderer_overlay {

TransformationPOLYORDER0::TransformationPOLYORDER0(
    size_t zoom,
    const std::vector<PixelMapping>& pointMappings)
{
    REQUIRE(pointMappings.size() >= 2,
        "Two points required to compute easy affine transform");

    const PixelMapping& point1Mapping = pointMappings[0];
    const PixelMapping& point2Mapping = pointMappings[1];
    Point2 pointOnMap1 = geoPoint2Mercator(point1Mapping.mapCoord);
    Point2 pointOnMap2 = geoPoint2Mercator(point2Mapping.mapCoord);
    auto pointOnMap1Display = mercatorToDisplaySigned(pointOnMap1, zoom);
    auto pointOnMap2Display = mercatorToDisplaySigned(pointOnMap2, zoom);
    Point2 pointOnImage1(point1Mapping.imageCoord);
    Point2 pointOnImage2(point2Mapping.imageCoord);
    double dxImage = pointOnImage2.x() - pointOnImage1.x();
    double dyImage = pointOnImage2.y() - pointOnImage1.y();
    double dxDisplay = pointOnMap2Display.x() - pointOnMap1Display.x();
    double dyDisplay = pointOnMap2Display.y() - pointOnMap1Display.y();
    double scale =
        sqrt(
            (dxImage * dxImage + dyImage * dyImage)
            /
            (dxDisplay * dxDisplay + dyDisplay * dyDisplay));

    double rotation = -signedAngle(
            Vector2{
                static_cast<double>(pointOnImage2.x() - pointOnImage1.x()),
                static_cast<double>(pointOnImage2.y() - pointOnImage1.y())},
            Vector2{
                static_cast<double>(pointOnMap2Display.x() - pointOnMap1Display.x()),
                static_cast<double>(pointOnMap2Display.y() - pointOnMap1Display.y())});
    const auto ca = cos(rotation);
    const auto sa = sin(rotation);
    const auto x0 = pointOnMap1Display.x();
    const auto y0 = pointOnMap1Display.y();
    double s = scale;
    AffineTransform2::Matrix displayToImageMatrix
            {{
                {{ca * s, -sa * s, - x0 * ca * s + y0 * sa * s + pointOnImage1.x()}},
                {{sa * s, ca * s, -x0 * sa * s - y0 * ca * s + pointOnImage1.y()}}
            }};
    transform.reset(new AffineTransform2(displayToImageMatrix));
}

} // namespace maps::wiki::renderer_overlay
