import { Coordinate, distance, rotate } from "ol/coordinate";
import { Extent, getCenter } from "ol/extent";
import { addCoordinateTransforms, addProjection, get, getTransform, Projection, ProjectionLike, transform } from "ol/proj";
import proj4 from "proj4";

function rotateProjection(projection: ProjectionLike, angle: number, extent: Extent) {
    function rotateCoordinate(coordinate: Coordinate, angle: number, anchor: Coordinate) {
        var coord = rotate(
            [coordinate[0] - anchor[0], coordinate[1] - anchor[1]],
            angle
        );
        return [coord[0] + anchor[0], coord[1] + anchor[1]];
    }

    function rotateTransform(coordinate: Coordinate) {
        return rotateCoordinate(coordinate, angle, getCenter(extent));
    }

    function normalTransform(coordinate: Coordinate) {
        return rotateCoordinate(coordinate, -angle, getCenter(extent));
    }

    var normalProjection = get(projection);

    if (normalProjection) {
        var rotatedProjection = new Projection({
            code: normalProjection.getCode() + ":" + angle.toString() + ":" + extent.toString(),
            units: normalProjection.getUnits(),
            extent: extent
        });
        addProjection(rotatedProjection);

        addCoordinateTransforms(
            "EPSG:4326",
            rotatedProjection,
            function (coordinate) {
                return rotateTransform(transform(coordinate, "EPSG:4326", projection));
            },
            function (coordinate) {
                return transform(normalTransform(coordinate), projection, "EPSG:4326");
            }
        );

        addCoordinateTransforms(
            "EPSG:3857",
            rotatedProjection,
            function (coordinate) {
                return rotateTransform(transform(coordinate, "EPSG:3857", projection));
            },
            function (coordinate) {
                return transform(normalTransform(coordinate), projection, "EPSG:3857");
            }
        );

        // also set up transforms with any projections defined using proj4
        if (typeof proj4 !== "undefined") {
            var projCodes = Object.keys(proj4.defs);
            projCodes.forEach(function (code) {
                var proj4Projection = get(code) as Projection;
                if (proj4Projection) {
                    if (!getTransform(proj4Projection, rotatedProjection)) {
                        addCoordinateTransforms(
                            proj4Projection,
                            rotatedProjection,
                            function (coordinate) {
                                return rotateTransform(
                                    transform(coordinate, proj4Projection, projection)
                                );
                            },
                            function (coordinate) {
                                return transform(
                                    normalTransform(coordinate),
                                    projection,
                                    proj4Projection
                                );
                            }
                        );
                    }
                }
            });
        }

        return rotatedProjection;
    }
}

const calculateCentroid = (bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) => {
    const x = (bottomLeft[0] + topLeft[0] + topRight[0] + bottomRight[0]) / 4;
    const y = (bottomLeft[1] + topLeft[1] + topRight[1] + bottomRight[1]) / 4;
    return [x, y];
}

function calculateRotationAngle(bottomLeft: Coordinate, bottomRight: Coordinate) {
    // Calculate the difference in x and y coordinates between bottom right and bottom left
    const deltaX = bottomRight[0] - bottomLeft[0];
    const deltaY = bottomRight[1] - bottomLeft[1];

    // Calculate the angle using atan2
    const angle = -Math.atan2(deltaY, deltaX);

    return angle;
}

function calculateExtent(bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) {
    const width = distance(bottomLeft, bottomRight);
    const height = distance(bottomLeft, topLeft);

    // Calculate the centroid of the polygon
    const [centerX, centerY] = calculateCentroid(bottomLeft, topLeft, topRight, bottomRight);

    // Define the extent based on the center and dimensions
    const extent = [
        centerX - width / 2, // minX
        centerY - height / 2, // minY
        centerX + width / 2, // maxX
        centerY + height / 2  // maxY
    ];

    return extent;
}

export {
    rotateProjection,
    calculateRotationAngle,
    calculateExtent,
    calculateCentroid
}